Problem/Motivation

RegisterEventSubscribersPass will throw an exception on container rebuild if an event subscriber doesn't implement the right interface:

      $interface = EventSubscriberInterface::class;
      if (!is_subclass_of($class, $interface)) {
        throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
      }

But the check for is_subclass_of() will also fail if the class wasn't found at all.

This means that for example, if you mess up the class name or the namespace, but your code implements the interface, you have a confusing error message.

Steps to reproduce

Proposed resolution

Add class_exists() check and exception first.

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Comments

joachim created an issue. See original summary.

akhilsoni’s picture

I was able to reproduce the confusing error described in the issue. When an `event_subscriber` service points to a class that does not exist, `is_subclass_of()` returns FALSE, so the exception says the service must implement `EventSubscriberInterface` even though the real problem is a missing or misspelled class.

I added a preflight check in `RegisterEventSubscribersPass` before delegating to Symfony’s `RegisterListenersPass`. If an `event_subscriber` service has a missing class, Drupal now throws a clearer exception naming both the service ID and the missing class.

Added unit test coverage for the missing-class case in:
`core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/RegisterEventSubscribersPassTest.php`

Tested locally:
`php -l core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php`
`php -l core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/RegisterEventSubscribersPassTest.php`
`../vendor/bin/phpunit --configuration phpunit.xml.dist tests/Drupal/Tests/Core/DependencyInjection/Compiler/RegisterEventSubscribersPassTest.php`
`../vendor/bin/phpunit --configuration phpunit.xml.dist tests/Drupal/Tests/Core/DependencyInjection/Compiler`

Result:
`OK (27 tests, 109 assertions)`

Also ran PHPCS on the changed files:
`vendor/bin/phpcs --standard=core/phpcs.xml.dist core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/RegisterEventSubscribersPassTest.php`

The patch is ready for review.

akhilsoni’s picture

Status: Active » Needs review
joachim’s picture

Status: Needs review » Needs work

Hi. Thanks for the patch.

It's really overkill though -- we just need a class_exists() condition.

What's this check for:

> if ($container->hasDefinition('event_dispatcher') || $container->hasAlias('event_dispatcher')) {

akhilsoni’s picture

Status: Needs work » Needs review
StatusFileSize
new2.33 KB

Thanks, good point.

I removed the extra `event_dispatcher` check and simplified the patch. It now only checks services tagged with `event_subscriber` and verifies that the configured class exists before passing the container to Symfony's `RegisterListenersPass`.

I reran the tests after the update:

`../vendor/bin/phpunit --configuration phpunit.xml.dist tests/Drupal/Tests/Core/DependencyInjection/Compiler/RegisterEventSubscribersPassTest.php`
`../vendor/bin/phpunit --configuration phpunit.xml.dist tests/Drupal/Tests/Core/DependencyInjection/Compiler`

Result:
`OK (27 tests, 109 assertions)`

charlliequadros’s picture

Hi @joachim, @akhilsoni

Could you please check whether I performed any of the steps incorrectly or missed any relevant scenario?

In the first test, I configured the service with an existing class that does not implement the required interface to be registered as an event subscriber. As shown in the image below, Symfony identifies the problem and displays an appropriate error message:

id service wrong

In the second test, I configured the service with a class that does not exist. In this case, Symfony also identifies the problem and displays the expected error message:

class missing

Therefore, based on my tests, both scenarios already appear to be handled by Symfony.

If I understood the purpose of this issue correctly, I believe it can be closed, since the error messages appear to be correct and sufficiently clear.

Please let me know what you think.

Cheers!
Charllie

charlliequadros’s picture

Hi @akhilsoni

If what I mentioned above is correct, the validation may be being implemented in the wrong place. In that case, the check to confirm that the class exists should be added here.

To include this scenario in the testing steps, we would need to create a service that references a non-existent class and add the `logger_aware` tag to that service.

The test would then verify the behaviour of a service tagged with `logger_aware` whose configured class does not exist.

In any case, let’s wait @joachim's confirmation before proceeding.

akhilsoni’s picture

Thanks for checking this.

Your examples make sense. I was looking at Drupal's wrapper before the tag is passed to Symfony, but if current 11.x already reports the missing class clearly through Symfony's RegisterListenersPass, then the patch probably does not need to be in RegisterEventSubscribersPass.

The LoggerAwarePass case does look closer to the original problem pattern, since it calls is_subclass_of() directly on the configured service class. A missing class there could still produce the misleading “must implement interface” message.

I agree, let's wait for @joachim to confirm the intended scope before rerolling this. If the issue is really about logger_aware, I can update the patch in that direction with a test for a logger_aware service using a non-existent class.

jacobupal’s picture

Issue tags: -Novice

Seems like we're outside of the novice aspect of this work, current state is "Needs review" which would be better for a more-than-novice person. Removing the Novice tag now to help move things forwards (here in Issue Triage at DrupalCon Rotterdam 2026).