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
- Declare 'interface MyEventSubscriber extends PHPUnit\Event\Subscriber' and implement notify(Event $event): void in implementing classes.
- Check what the interface actually extends with class_implements(MyInterface::class) and fix the hierarchy.
- 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
- Declare custom subscriber interfaces as 'extends Subscriber' from the start.
- Keep the PHPUnit Event contracts installed in any package declaring subscriber interfaces.
- Review interface hierarchies when splitting subscriber interfaces across packages.
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
- Subscriber "%s" does not implement a known interface
- Event "%s" does not implement Event interface
- Subscriber "%s" does not implement any known interface - did
- Subscriber "%s" does not exist or is not an interface
- Event class "%s" does not exist
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/c6145eb5fd46208c.
Report an issue: GitHub.