{"record":{"id":"881ff4c3876c6d61","repo":"sebastianbergmann/phpunit","slug":"subscriber-s-does-not-implement-any-known-inter","errorCode":null,"errorMessage":"Subscriber \"%s\" does not implement any known interface - did you forget to register it?","messagePattern":"Subscriber \"(.+?)\" does not implement any known interface - did you forget to register it\\?","errorType":"exception","errorClass":"UnknownSubscriberTypeException","httpStatus":null,"severity":"error","filePath":"src/Event/Dispatcher/DirectDispatcher.php","lineNumber":55,"sourceCode":"\n    public function __construct(TypeMap $map)\n    {\n        $this->typeMap = $map;\n    }\n\n    public function registerTracer(Tracer\\Tracer $tracer): void\n    {\n        $this->tracers[] = $tracer;\n    }\n\n    /**\n     * @throws MapError\n     * @throws UnknownSubscriberTypeException\n     */\n    public function registerSubscriber(Subscriber $subscriber): void\n    {\n        if (!$this->typeMap->isKnownSubscriberType($subscriber)) {\n            throw new UnknownSubscriberTypeException(\n                sprintf(\n                    'Subscriber \"%s\" does not implement any known interface - did you forget to register it?',\n                    $subscriber::class,\n                ),\n            );\n        }\n\n        $eventClassName = $this->typeMap->map($subscriber);\n\n        if (!array_key_exists($eventClassName, $this->subscribers)) {\n            $this->subscribers[$eventClassName] = [];\n        }\n\n        $this->subscribers[$eventClassName][] = $subscriber;\n    }\n\n    /**\n     * @throws Throwable","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Event/Dispatcher/DirectDispatcher.php#L37-L73","documentation":"PHPUnit's event dispatcher routes events to subscribers via a TypeMap that maps subscriber interfaces (such as PHPUnit\\Event\\Test\\PreparedSubscriber) to event classes. registerSubscriber() rejects any subscriber object whose class_implements() list contains no interface present in that map, because the dispatcher could not decide which events to deliver to it. The built-in interfaces are pre-registered by Facade::registerDefaultTypes(), so hitting this error means your subscriber implements only the marker PHPUnit\\Event\\Subscriber interface (or nothing) instead of a concrete subscriber interface.","triggerScenarios":"Calling Facade::registerSubscriber()/registerSubscribers() from an extension's bootstrap() method, or DirectDispatcher::registerSubscriber() directly, with an object that implements only PHPUnit\\Event\\Subscriber; a custom subscriber interface that was never registered with TypeMap::addMapping(); a subscriber whose interface was renamed or removed during a PHPUnit major-version upgrade.","commonSituations":"Writing a first custom PHPUnit extension (<extensions><bootstrap> in phpunit.xml) and implementing the generic Subscriber interface instead of a specific one like Test\\PreparedSubscriber or TestRunner\\ExecutionFinishedSubscriber; copy-pasting extension code from an older PHPUnit tutorial where interfaces lived elsewhere; trying to register a tracer-like class as a subscriber.","solutions":["Make the subscriber class implement the built-in interface for the event you care about, e.g. final class SlowTestAlert implements PHPUnit\\Event\\Test\\PreparedSubscriber, and implement its notify(Test\\Prepared $event): void method.","If you need custom events, build a TypeMap, call addMapping(MySubscriberInterface::class, MyEvent::class), and construct the DirectDispatcher with that map before registering subscribers.","Run var_export(class_implements($subscriber), true) and compare the list against PHPUnit\\Event\\* interfaces to spot stale use statements or wrong namespaces after upgrading PHPUnit.","If you only want to observe every event, register a PHPUnit\\Event\\Tracer\\Tracer via registerTracer() instead of a Subscriber."],"exampleFix":"// before\nuse PHPUnit\\Event\\Event;\nuse PHPUnit\\Event\\Subscriber;\n\nfinal class SlowTestAlert implements Subscriber\n{\n    public function notify(Event $event): void { /* ... */ }\n}\n$facade->registerSubscriber(new SlowTestAlert);\n\n// after\nuse PHPUnit\\Event\\Test\\Prepared;\nuse PHPUnit\\Event\\Test\\PreparedSubscriber;\n\nfinal class SlowTestAlert implements PreparedSubscriber\n{\n    public function notify(Prepared $event): void { /* ... */ }\n}\n$facade->registerSubscriber(new SlowTestAlert);","handlingStrategy":"validation","validationCode":"$interfaces = class_implements($subscriber);\nunset($interfaces[PHPUnit\\Event\\Subscriber::class]);\n\n$implementsConcreteSubscriber = (bool) array_filter(\n    $interfaces,\n    fn (string $i) => str_starts_with($i, 'PHPUnit\\Event\\') && str_ends_with($i, 'Subscriber'),\n);\n\nif (!$implementsConcreteSubscriber) {\n    // registerSubscriber() would throw UnknownSubscriberTypeException: fix the class first\n}","typeGuard":null,"tryCatchPattern":"try {\n    $facade->registerSubscriber($subscriber);\n} catch (PHPUnit\\Event\\UnknownSubscriberTypeException $e) {\n    // Log and skip, or switch to registerTracer() if all events should be observed.\n    // Do not swallow silently: the subscriber will receive no events.\n}","preventionTips":["Always implement a specific built-in subscriber interface (e.g. Test\\PreparedSubscriber) rather than the bare Subscriber marker.","After upgrading PHPUnit, re-check the use statements in extension subscriber classes against the new namespaces.","Register custom mappings in the same TypeMap the dispatcher uses before registering any custom subscriber."],"tags":["phpunit","event-dispatcher","subscriber","extension-registration","php"],"backgroundTag":"unknown-subscriber-type","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}