sebastianbergmann/phpunit · error · InvalidSubscriberException

Subscriber "%s" does not extend Subscriber interface

Error message

Subscriber "%s" does not extend Subscriber interface

What it means

Beyond existing, a subscriber interface passed to TypeMap::addMapping() must extend the marker interface PHPUnit\Event\Subscriber (verified with class_implements()); InvalidSubscriberException is thrown otherwise. This guarantees every mapped subscriber type carries the notify() contract the dispatcher invokes on delivery.

Source

Thrown at src/Event/TypeMap.php:131

        if (!class_exists($eventClass)) {
            throw new UnknownEventException(
                sprintf(
                    'Event class "%s" does not exist',
                    $eventClass,
                ),
            );
        }
    }

    /**
     * @param class-string $subscriberInterface
     *
     * @throws InvalidSubscriberException
     */
    private function ensureSubscriberInterfaceExtendsInterface(string $subscriberInterface): void
    {
        if (!in_array(Subscriber::class, class_implements($subscriberInterface), true)) {
            throw new InvalidSubscriberException(
                sprintf(
                    'Subscriber "%s" does not extend Subscriber interface',
                    $subscriberInterface,
                ),
            );
        }
    }

    /**
     * @param class-string $eventClass
     *
     * @throws InvalidEventException
     */
    private function ensureEventClassImplementsEventInterface(string $eventClass): void
    {
        if (!in_array(Event::class, class_implements($eventClass), true)) {
            throw new InvalidEventException(
                sprintf(

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Declare 'interface MyEventSubscriber extends PHPUnit\Event\Subscriber' and implement notify(Event $event): void in implementing classes.
  2. Check what the interface actually extends with class_implements(MyInterface::class) and fix the hierarchy.
  3. Ensure the PHPUnit Event contracts are importable (dependency installed) in the package declaring the interface.

Example fix

// before
interface OrderPlacedSubscriber { public function notify(OrderPlaced $event): void; }
$typeMap->addMapping(OrderPlacedSubscriber::class, OrderPlaced::class); // throws

// after
use PHPUnit\Event\Subscriber;

interface OrderPlacedSubscriber extends Subscriber { public function notify(OrderPlaced $event): void; }
$typeMap->addMapping(OrderPlacedSubscriber::class, OrderPlaced::class);
Defensive patterns

Strategy: validation

Validate before calling

if (!in_array(PHPUnit\Event\Subscriber::class, class_implements($subscriberInterface), true)) {
    throw new InvalidArgumentException($subscriberInterface . ' must extend PHPUnit\\Event\\Subscriber');
}

$typeMap->addMapping($subscriberInterface, $eventClass);

Type guard

function extendsSubscriberContract(string $interface): bool
{
    return in_array(PHPUnit\Event\Subscriber::class, class_implements($interface), true);
}

Prevention

When it happens

Trigger: addMapping() with a domain interface from your own code or another package that does not extend PHPUnit\Event\Subscriber; accidentally passing an unrelated built-in interface (a PSR interface, PHPUnit\Event\Tracer\Tracer) as the first argument.

Common situations: First-time extension authors mapping their own interfaces into the event system and assuming duck typing is enough; copy-pasting interface names; splitting subscriber interfaces into a shared package without the PHPUnit dependency.

Related errors


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/c6145eb5fd46208c. Report an issue: GitHub.