Skip to content

[DependencyInjection] Allow disabling instanceof config inheritance #61511

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 7.4
Choose a base branch
from

Conversation

HypeMC
Copy link
Member

@HypeMC HypeMC commented Aug 25, 2025

Q A
Branch? 7.4
Bug fix? no
New feature? yes
Deprecations? no
Issues -
License MIT

When configuring services like normalizers for a named serializer or Monolog processors for specific channels/handlers, autoconfiguration can get in the way.

For example, the NormalizerInterface is registered for autoconfiguration.
If I try to register a custom normalizer for a specific named serializer, it also gets registered with the default serializer because of autoconfiguration:

#[AutoconfigureTag(
    name: 'serializer.normalizer',
    attributes: [
        'serializer' => 'my_named_serializer',
    ],
)]
class MyNormalizer implements NormalizerInterface {}

Currently, the only way around this is to disable autoconfiguration entirely:

services:
    App\MyNormalizer:
        autoconfigure: false
        tags:
            - serializer.normalizer: { serializer: 'my_named_serializer' }

This PR introduces a new inherit_configuration option for service definitions and a #[WithoutInheritedConfiguration] attribute.
Both allow skipping the inheritance of instanceof conditionals from parent classes or interfaces:

#[AutoconfigureTag(
    name: 'serializer.normalizer',
    attributes: [
        'serializer' => 'my_named_serializer',
    ],
)]
#[WithoutInheritedConfiguration]
class MyNormalizer implements NormalizerInterface {}

It can also be used to exclude specific services from inheriting _instanceof configurations:

services:
    _defaults:
        autowire: true
        autoconfigure: true

    _instanceof:
        App\SomeInterface:
            tags: ['some.tag']

    App\SomeInterfaceImplementation:
        inherit_configuration: false # This implementation won't inherit instanceof config

Why a new attribute?

I initially tried adding a new argument to the existing #[Autoconfigure] attribute.
However, this attribute itself is resolved via instanceof conditionals.
I could try something like $conditionals[$class], but since the attribute is repeatable, it’s unclear how to handle cases like:

#[Autoconfigure(inheritConfiguration: false)]
#[Autoconfigure(inheritConfiguration: true)]
class MyClass {}

@HypeMC HypeMC force-pushed the without-inheritance branch from ddf1359 to 3fb5849 Compare August 25, 2025 01:27
@nicolas-grekas
Copy link
Member

Thanks for the detailed description.
I'd prefer not adding this, because it means more exceptions to the rules of autoconfiguration.
But I do understand why it might be needed :)

Before, let me challenge the proposal:

  • What about not ignoring a serializer.normalizer tag with no name when another one exists with a name? that'd be something to deal with in the compiler pass
  • Please expand on Monolog channels/handlers so that we can wonder about similar more specific rules?

@HypeMC
Copy link
Member Author

HypeMC commented Aug 25, 2025

@nicolas-grekas Hi. Yeah, I see your point about exceptions. I'm not sure how many cases like this there actually are.

What about not ignoring a serializer.normalizer tag with no name when another one exists with a name? that'd be something to deal with in the compiler pass

I'm assuming you meant ignore the one without a name. I agree, that would solve the serializer case. However, there might be other cases besides this and the Monolog one.

Please expand on Monolog channels/handlers so that we can wonder about similar more specific rules?

Here's a simple example:

#[AsMonologProcessor('my_channel')]
final class MyProcessor implements ProcessorInterface
{
    public function __invoke(LogRecord $record) {}
}

You'd expect this processor to be registered only for my_channel, but because ProcessorInterface is registered for autoconfiguration, the processor is added to all channels.

The workaround is the same, disable autoconfiguration and add an explicit tag.

services:
    App\MyProcessor:
        autoconfigure: false
        tags:
            - monolog.processor: { channel: 'my_channel' }

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Aug 25, 2025

I'd like to see how my proposal fits before looking for a more generic solution, to lower the overall complexity of things...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants