{"record":{"id":"967761ff9c2857ff","repo":"sebastianbergmann/phpunit","slug":"event-s-already-assigned","errorCode":null,"errorMessage":"Event \"%s\" already assigned","messagePattern":"Event \"(.+?)\" already assigned","errorType":"exception","errorClass":"EventAlreadyAssignedException","httpStatus":null,"severity":"error","filePath":"src/Event/TypeMap.php","lineNumber":182,"sourceCode":"        if (array_key_exists($subscriberInterface, $this->mapping)) {\n            throw new SubscriberTypeAlreadyRegisteredException(\n                sprintf(\n                    'Subscriber type \"%s\" already registered',\n                    $subscriberInterface,\n                ),\n            );\n        }\n    }\n\n    /**\n     * @param class-string $eventClass\n     *\n     * @throws EventAlreadyAssignedException\n     */\n    private function ensureEventWasNotAlreadyAssigned(string $eventClass): void\n    {\n        if (in_array($eventClass, $this->mapping, true)) {\n            throw new EventAlreadyAssignedException(\n                sprintf(\n                    'Event \"%s\" already assigned',\n                    $eventClass,\n                ),\n            );\n        }\n    }\n}\n","sourceCodeStart":164,"sourceCodeEnd":191,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Event/TypeMap.php#L164-L191","documentation":"TypeMap enforces a strict one-to-one relation: each event class may be assigned to exactly one subscriber interface. ensureEventWasNotAlreadyAssigned() scans the mapping values with in_array($eventClass, $this->mapping, true) and throws EventAlreadyAssignedException when the event is already claimed by another (or the same) interface.","triggerScenarios":"addMapping(SecondSubscriberInterface::class, SameEvent::class) after (FirstSubscriberInterface, SameEvent) is registered; re-adding a built-in event class, since Facade::registerDefaultTypes() already claims every stock event class.","commonSituations":"Wanting two different subscriber types for the same event (the design answer is multiple subscriber instances of the one interface, not two interfaces); merging a custom map over the default map; refactoring one subscriber interface into several while keeping the event.","solutions":["Register one subscriber interface per event and register multiple subscriber objects of that interface; the dispatcher stores a list of subscribers per event class.","If the events genuinely differ, introduce a distinct event class (subclass or sibling implementing Event) for the second mapping.","Drop or rename the conflicting mapping so each event class appears once."],"exampleFix":"// before\n$typeMap->addMapping(ExecutionFinishedSubscriber::class, ExecutionFinished::class);\n$typeMap->addMapping(MetricsFlushSubscriber::class, ExecutionFinished::class); // already assigned\n\n// after\n$typeMap->addMapping(ExecutionFinishedSubscriber::class, ExecutionFinished::class);\n$typeMap->addMapping(MetricsFlushSubscriber::class, MetricsFlushed::class); // distinct event\n// or register two ExecutionFinishedSubscriber instances instead","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    $typeMap->addMapping($subscriberInterface, $eventClass);\n} catch (PHPUnit\\Event\\EventAlreadyAssignedException $e) {\n    // One event maps to one interface: register subscriber *instances* instead of a second interface\n}","preventionTips":["Remember the mapping is one-to-one; add multiple subscriber objects of the single mapped interface for the same event.","Audit custom mappings for event classes that duplicate built-in events.","Introduce a distinct event subclass when a second event type is genuinely needed."],"tags":["phpunit","type-map","duplicate-registration","event-system","php"],"backgroundTag":"duplicate-registration","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}