sebastianbergmann/phpunit · error · InvalidEventException
Event "%s" does not implement Event interface
Error message
Event "%s" does not implement Event interface
What it means
An event class passed to TypeMap::addMapping() must implement the marker interface PHPUnit\Event\Event; InvalidEventException is thrown when class_implements($eventClass) does not contain it. This keeps the dispatcher's dispatch(Event $event) type guarantee meaningful for custom events registered in the map.
Source
Thrown at src/Event/TypeMap.php:148
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(
'Event "%s" does not implement Event interface',
$eventClass,
),
);
}
}
/**
* @param class-string $subscriberInterface
*
* @throws SubscriberTypeAlreadyRegisteredException
*/
private function ensureSubscriberWasNotAlreadyRegistered(string $subscriberInterface): void
{
if (array_key_exists($subscriberInterface, $this->mapping)) {
throw new SubscriberTypeAlreadyRegisteredException(
sprintf(View on GitHub (pinned to f123cdb2a2)
Solutions
- Add 'implements PHPUnit\Event\Event' to the event class.
- If the class wraps another library's event, make the wrapper implement Event and expose the wrapped object via a getter.
- Verify with in_array(Event::class, class_implements($eventClass), true) when in doubt.
Example fix
// before
final class OrderPlaced { public function __construct(public string $id) {} }
$typeMap->addMapping(OrderPlacedSubscriber::class, OrderPlaced::class); // throws
// after
use PHPUnit\Event\Event;
final class OrderPlaced implements Event { public function __construct(public string $id) {} }
$typeMap->addMapping(OrderPlacedSubscriber::class, OrderPlaced::class); Defensive patterns
Strategy: validation
Validate before calling
if (!in_array(PHPUnit\Event\Event::class, class_implements($eventClass), true)) {
throw new InvalidArgumentException($eventClass . ' must implement PHPUnit\\Event\\Event');
}
$typeMap->addMapping($subscriberInterface, $eventClass); Type guard
function implementsEventContract(string $class): bool
{
return in_array(PHPUnit\Event\Event::class, class_implements($class), true);
} Prevention
- Add 'implements Event' to every custom event class when it is created.
- When wrapping foreign event objects, make the wrapper implement Event rather than mapping the foreign class.
- Re-run mapping bootstrap tests after event-hierarchy refactors.
When it happens
Trigger: Mapping a plain DTO or value class that has no 'implements Event'; mapping an enum or an interface instead of a concrete class; an event class whose parent implements a custom base event that itself does not extend PHPUnit\Event\Event.
Common situations: Bringing existing domain events into PHPUnit's emitter without adopting the Event contract; refactoring an event hierarchy and dropping the implements clause; wrapping third-party event objects instead of composing them.
Related errors
- Subscriber "%s" does not extend Subscriber interface
- Subscriber "%s" does not implement a known interface
- Subscriber "%s" does not exist or is not an interface
- Event class "%s" does not exist
- Subscriber type "%s" already registered
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/1d11bdccf1fb4187.
Report an issue: GitHub.