Skip to content
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

[DI] add syntax to stack decorators #36373

Merged
merged 1 commit into from Apr 24, 2020
Merged

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Apr 6, 2020

Q A
Branch? master
Bug fix? no
New feature? yes
Deprecations? no
Tickets Fix #30599
License MIT
Doc PR -

Declare this:

services:
    my_stack_of_decorators:
        stack:
            - class: App\ExternalDecorator
            - class: App\InternalDecorator
            - class: App\DecoratoredClass

And get this:
image

The PR is now ready with support for Yaml, XML and the PHP-DSL. It needs #36388, #36392 and #36389 to pass, and relates to #36390 to be DX-friendly.

The new syntax now supports composable stacks - i.e stack you can reuse in the middle of another stack.

RIP middleware, simple decorators FTW :)

From the test cases:

services:
    reusable_stack:
        stack:
            - class: stdClass
              properties:
                  label: A
                  inner: '@.inner'
            - class: stdClass
              properties:
                  label: B
                  inner: '@.inner'

    concrete_stack:
        stack:
            - parent: reusable_stack
            - class: stdClass
              properties:
                  label: C

This will create a service similar to:

(object) [
    'label' => 'A',
    'inner' => (object) [
        'label' => 'B',
        'inner' => (object) [
             'label' => 'C',
        ]
    ],
];

When used together with autowiring, this is enough to declare a stack of decorators:

services:
    my_processing_stack:
        stack:
            - App\ExternalDecorator: ~
            - App\InternalDecorator: ~
            - App\TheDecoratedClass: ~

See fixtures for the other configuration formats.

See also https://twitter.com/nicolasgrekas/status/1248198573998604288

Todo:

@nicolas-grekas nicolas-grekas added this to the next milestone Apr 6, 2020
@nicolas-grekas nicolas-grekas force-pushed the di-stack branch 3 times, most recently from 8737bd7 to 6dc1059 Compare April 7, 2020 19:09
@nicolas-grekas nicolas-grekas changed the title [DI] add syntax to stack decorators easily [DI] add syntax to stack decorators Apr 7, 2020
@nicolas-grekas nicolas-grekas force-pushed the di-stack branch 2 times, most recently from b3c8821 to d4378d7 Compare April 9, 2020 10:23
@nicolas-grekas nicolas-grekas marked this pull request as ready for review April 9, 2020 10:35
@dragoonis
Copy link

can you share details on why the community would switch from middleware to decorator stack pattern?

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Apr 9, 2020

@dragoonis everyone in the community will decide what they need, there is no one-size-fits-all solution. But this PR fills the gap between a middleware system and a stack of decorators. This brings "ease of stacking" to both styles. If that was the main benefit of a middleware system, this PR makes it less relevant - for ppl using the Symfony DI component of course.

See this thread for more details: https://twitter.com/nicolasgrekas/status/1248198573998604288

@jeremyFreeAgent
Copy link
Contributor

Today I do that stack part by myself. Can’t wait to use it that way.

@nicolas-grekas nicolas-grekas force-pushed the di-stack branch 6 times, most recently from 9e12b40 to 640173f Compare April 14, 2020 16:20
@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Apr 14, 2020

This PR is ready and I'm quite happy about it!

Here are some implementation details and a future opening:

How does this work?

When a stack foo is declared, loaders create a ChildDefinition with the chain of decorators set as its arguments. The child definition is also tagged with a new container.stack tag. Like aliases, stacks support two attributes: public and deprecated.

This means it's possible to define a stack without using a loader, by following the above convention (all other attributes of the ChildDefinition would be ignored). The benefit of using a new syntax is that it makes defining the chain compatible with "defaults" set in the config file.

About the chain itself, it must be a list of either services, aliases or references. Alias and references are used undifferentiated to insert another stack definition in the middle of a new one. Both styles are needed to fit each kind of loader (yaml&xml can do only aliases and php-dsl does ref()).

This is already quite powerful. But there's more!

There's a second way to insert a stack inside a stack: using a "parent" in the chain. Such an insertion point will override the top-most definition in the embedded stack. This means one can also configure a stack while including it! All attributes of child definitions work here, it all depends on what the stack is about and only you will know about this.

Then, the new ResolveDecoratorStackPass will turn all this into plain old decorating services.

What could come next?

I've been talking with @dunglas and @pamil on Twitter and Slack and they asked me about inserting or removing frames inside an existing stack.

For now, this would be reasonably easy using a compiler pass.

Using configuration, we could also imagine new tags. E.g. one to allow filtering a frame while embedding a stack, another to allow replacing a frame by a new one, another for inserting a frame before/after another one - doing so either while embedding and/or by altering an existing stack definition. I didn't mention it, but frames can be named, this could help.

All this could replace an event dispatcher in some situations I believe. But that's hypotheses for the future.

Please give this a try for now :)

@fabpot
Copy link
Member

fabpot commented Apr 24, 2020

Thank you @nicolas-grekas.

@fabpot fabpot merged commit d6b9011 into symfony:master Apr 24, 2020
@nicolas-grekas nicolas-grekas deleted the di-stack branch April 24, 2020 09:13
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.

[DI] Decorate the service directly instead of override
6 participants