{"record":{"id":"c6145eb5fd46208c","repo":"sebastianbergmann/phpunit","slug":"subscriber-s-does-not-extend-subscriber-interfa","errorCode":null,"errorMessage":"Subscriber \"%s\" does not extend Subscriber interface","messagePattern":"Subscriber \"(.+?)\" does not extend Subscriber interface","errorType":"exception","errorClass":"InvalidSubscriberException","httpStatus":null,"severity":"error","filePath":"src/Event/TypeMap.php","lineNumber":131,"sourceCode":"        if (!class_exists($eventClass)) {\n            throw new UnknownEventException(\n                sprintf(\n                    'Event class \"%s\" does not exist',\n                    $eventClass,\n                ),\n            );\n        }\n    }\n\n    /**\n     * @param class-string $subscriberInterface\n     *\n     * @throws InvalidSubscriberException\n     */\n    private function ensureSubscriberInterfaceExtendsInterface(string $subscriberInterface): void\n    {\n        if (!in_array(Subscriber::class, class_implements($subscriberInterface), true)) {\n            throw new InvalidSubscriberException(\n                sprintf(\n                    'Subscriber \"%s\" does not extend Subscriber interface',\n                    $subscriberInterface,\n                ),\n            );\n        }\n    }\n\n    /**\n     * @param class-string $eventClass\n     *\n     * @throws InvalidEventException\n     */\n    private function ensureEventClassImplementsEventInterface(string $eventClass): void\n    {\n        if (!in_array(Event::class, class_implements($eventClass), true)) {\n            throw new InvalidEventException(\n                sprintf(","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Event/TypeMap.php#L113-L149","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ninterface OrderPlacedSubscriber { public function notify(OrderPlaced $event): void; }\n$typeMap->addMapping(OrderPlacedSubscriber::class, OrderPlaced::class); // throws\n\n// after\nuse PHPUnit\\Event\\Subscriber;\n\ninterface OrderPlacedSubscriber extends Subscriber { public function notify(OrderPlaced $event): void; }\n$typeMap->addMapping(OrderPlacedSubscriber::class, OrderPlaced::class);","handlingStrategy":"validation","validationCode":"if (!in_array(PHPUnit\\Event\\Subscriber::class, class_implements($subscriberInterface), true)) {\n    throw new InvalidArgumentException($subscriberInterface . ' must extend PHPUnit\\\\Event\\\\Subscriber');\n}\n\n$typeMap->addMapping($subscriberInterface, $eventClass);","typeGuard":"function extendsSubscriberContract(string $interface): bool\n{\n    return in_array(PHPUnit\\Event\\Subscriber::class, class_implements($interface), true);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["phpunit","type-map","subscriber","interface-contract","php"],"backgroundTag":"interface-contract-violation","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}