Follow-up to #2303673: Implement stackphp; cleanup handlePageCache() and preHandle()
Problem/Motivation
Services tagged with http_middleware
are not constructed the same way as others. For example the calls
parameter is not respected.
In addition, service construction of middlewares fails entirely if something accesses a middleware via container:
$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$kernel->boot();
$kernel->getContainer()->get('http_middleware.reverse_proxy');
This fails with:
Recoverable fatal error: Argument 1 passed to Drupal\Core\StackMiddleware\ReverseProxyMiddleware::__construct() must implement interface Symfony\Component\HttpKernel\HttpKernelInterface, instance of Drupal\Core\Site\Settings given
Proposed resolution
Either document that clearly or fix the implementation.
Remaining tasks
User interface changes
API changes
Comment | File | Size | Author |
---|---|---|---|
#45 | interdiff.txt | 800 bytes | Fabianx |
#45 | stack_middleware-2343677-45.patch | 7.94 KB | Fabianx |
Comments
Comment #1
znerol CreditAttribution: znerol commentedComment #2
znerol CreditAttribution: znerol commentedComment #3
znerol CreditAttribution: znerol commentedComment #5
damiankloip CreditAttribution: damiankloip commentedHmm, yes. I guess we have a couple of options, this goes for injected parameters and setters (as your demo patch shows):
- Remove the actual definitions from the service container after our compiler pass (patch attached for that). These should not be used outside of the stack anyway?
- Allow the httpkernel service to be passed in so we can create an instance but the stack would replace this anyway
- Implement our own builder (would be in a similar problem space though, and compiler pass could do similar)
- Don't use the StackBuilder at all; rely on passing the correct decorated kernel into the next. This could easily break and lead to strange things. A developer could easily add the wrong parameter to one of them and it would break the whole stack. Seems too fragile maybe?
- Just document this somewhere and leave it as that (meh).
Thoughts?
Comment #6
znerol CreditAttribution: znerol commentedI just tested @pounard version from #2303673-103: Implement stackphp; cleanup handlePageCache() and preHandle(), it does not suffer of those problems.
Comment #7
damiankloip CreditAttribution: damiankloip commentedYes, I guess this is because it adds the parameter from the last kernel id to the actual definition.
Comment #8
damiankloip CreditAttribution: damiankloip commentedHere is pounards patch from mentioned issue in #6.
Comment #9
pounardIndeed, this fixes the kernel definition problem. If you want to keep the StackBuilder dependency, it's still possible to fix the definitions during the compiler pass the exact same way, it only takes one or two lines of code to add.
As mentioned in the other issue, the only problem by getting rid of the StackBuilder is that we cannot chain terminate() calls, but the fact that the stackphp initiative does only document the KernelInterface and not the TerminableInterface mitigates that.
Comment #10
damiankloip CreditAttribution: damiankloip commentedYes, indeed. We could keep things how they are, let the stack do what it is doing, but fix the definitions in the compiler pass so it solves the problems this issue outlines? Something like this.
Comment #11
znerol CreditAttribution: znerol commented#10 resolves the
$container->get('http_middleware.reverse_proxy');
issue, but it does not have any effect on how middlewares are constructed normally. That means that we still cannot usecalls:
in the YAML service definition etc.Comment #12
znerol CreditAttribution: znerol commentedComment #13
damiankloip CreditAttribution: damiankloip commentedok, well I don't think this is possible with the Stack/Builder class.
Comment #14
dawehnerSo if Stack/Builder doesn't provider what we need, let's just try to use the solution from pounard.
On the longrun moving that generic container feature could be moved upstream somehow.
Comment #15
Crell CreditAttribution: Crell commentedI'm fine with dropping the stack library itself, since even with it we need to do Drupal-specific bridge code anyway. The important part is that stackable kernels work, not that they're added using a specific library to do it.
As for avoiding loading middlewares, they're a good example of a service that should be marked private. That would make $container->get() not work for them, which you should never be doing anyway.
Comment #16
dawehnerYeah probably, this would be certainly good.
Comment #17
damiankloip CreditAttribution: damiankloip commentedI think we need to decide here first if we want to support getting these services outside the context of the stack.
Comment #18
pounardAt first sight, I'd say we don't, except for Drupal specific services that may need configuration in specific use cases.
Comment #19
znerol CreditAttribution: znerol commented@damiankloip: It does not make any difference. Even if we set the middleware services to private, the
Stack\Builder
still fails to construct middlewares properly if they have e.g.calls
in their service definitions.Comment #20
Crell CreditAttribution: Crell commentedI cannot think of any reason to modify or reuse a middleware outside of, you know, being a middleware. The same applies for a lot of things; event subscribers, for instance, should all be private because there is no good reason to call $container->get() on them, ever. MOST of what we have in the container should be private, frankly.
Comment #21
damiankloip CreditAttribution: damiankloip commentedMeh, yes. It has no bearing on that problem. Stack is not container aware in any way, so shall we favour pounards approach? Maybe with the addition of private services?
Here is a reroll of that for now.
Comment #22
pounardI think that Crell is right, making those services private makes sense, they are not supposed to be used anywhere outside of the stack itself.
Comment #23
damiankloip CreditAttribution: damiankloip commentedOk, making them private services. So what we lose is the terminate calls for stacked items that the StackedHttpKernel provides. I guess people could use DestructableInterface if there were some uses? Not sure.
Comment #24
pounardWouldn't those services be set private directly in the yaml file instead ? I sounds odd to leave this responsability to the compiler pass ; While I agree that those services should be set private by the provider, I don't see the point of forcing them to be. For example, if a module set such service public for some various reason (I don't see any good reason right now, but some may rise anytime in the future in specific edge use cases) - why forcing at compilation time while it might be set directly in the definition file instead ?
Comment #25
damiankloip CreditAttribution: damiankloip commentedok, I was going with the enforcement route. We can add this just to the definitions though, I don't care either way.
Comment #26
Crell CreditAttribution: Crell commentedI agree, let's just mark them private in the yaml file. No sense being fancy if we don't have a clear need to be.
Damian, I thought we said earlier that we were going to keep the Stack library for the Stacked kernel.
Comment #27
dawehnerThe problem is that the container does not allow an array of references, sadly.
Comment #28
damiankloip CreditAttribution: damiankloip commentedMarking the services in the YAML definition instead.
Comment #29
znerol CreditAttribution: znerol commented@dawehner: It seems to me that at least container parameters may actually contain arrays with service references. See ParameterBag::resolveValue().
This would mean that it might be possible to keep the
StackedHttpKernel
(which also handlesterminate()
) if we'd just stick the sorted array of middleware services into a container parameter.Comment #30
Fabianx CreditAttribution: Fabianx commentedThere is definitely a use case to get data out of the middleware service.
e.g.
A Page Cache Middleware that needs to store some data, but don't want to store it on the request object.
So you can then check e.g
The other option is to put the object or all the data on the request itself, but I don't think that is as clean.
If I understand the last comment correctly, according to znerol it should be possible to change the structure, so we can have the stack and still get back the individual services.
Comment #31
znerol CreditAttribution: znerol commentedSomething like this (without kernel parameters though, I was wrong in that regard). No interdiff because this is a different approach.
Comment #32
damiankloip CreditAttribution: damiankloip commentedYes, something like that is what I wanted to see originally if we keep the stack builder library (which is a good idea). If this resolves the dependencies properly this looks good.
Comment #33
znerol CreditAttribution: znerol commentedOk, but this is PoC quality. It needs to be made more robust, e.g., it should test whether the
http_kernel
service is really a stacked kernel before altering its definition.Comment #34
damiankloip CreditAttribution: damiankloip commentedYes, this approach looks good is what I meant, I will make some changes now.
Comment #35
damiankloip CreditAttribution: damiankloip commentedSo here is some test coverage too.
Also added the check for the stack you mentioned znerol.
Comment #36
dawehnerReviewed already kinda during the process of damian writing it. +1
Not sure whether we want to have a dedicated test for the calls in order to ensure that the "regression" works in the future.
Comment #37
damiankloip CreditAttribution: damiankloip commentedI am not sure about testing calls, as now we are always dealing with service definitions. So this would just be like us writing a test that the Symfony tests work, or not? :p
Comment #38
dawehnerYes, this ensures that the symfony implementation still works, though we have to have to ensure on a more high level way that things are still working.
I don't want to fight things here.
Comment #41
Fabianx CreditAttribution: Fabianx commentedI am okay with making the services, public: false - if it cannot be done any other way. There are other ways Stack state can be stored somewhere, so RTBC for me too, just needs to pass tests again.
Comment #42
damiankloip CreditAttribution: damiankloip commentedOk, so the tests are good again. This was an issue with the test bot yesterday.
Comment #43
Fabianx CreditAttribution: Fabianx commentedI have one concern:
- This means that whenever we add a middleware service we force the user to mark the service public: false, because else it breaks e.g. "console" which does an iteration over all public services.
Is it technically possible / feasible to either:
- a) do the marking private as part of the compiler pass and document somewhere that decorated services are hidden automatically?
- b) Output an error message if we try to decorate a public service?
Thanks!
Comment #44
Fabianx CreditAttribution: Fabianx commentedAs discussed in IRC:
- The public: false is a larger API change (in a way) and needs to be discussed separately.
Lets remove it.
Even with public: false removed, this successfully unbreaks "container:debug" in console! :)
Great work!
This will be RTBC once those three public: false lines are removed.
The rest makes perfect sense to me and I like this method of service definition much more that we use now.
Comment #45
Fabianx CreditAttribution: Fabianx commentedUploading a patch to get some commit credit :D (for reviewing).
Only implemented my reviewer feedback, so RTBC now.
As this could cause confusiion:
I did not RTBC my own patch here, I rather did not have a novice or anyone else remove those three lines we agreed in IRC to remove, but did it quickly myself as I had that diff applied to test it locally anyway.
Comment #46
znerol CreditAttribution: znerol commentedFiled #2350545: Protect some services from being referenced from outside container.
Comment #47
dawehnerReally, we should fix our process ... and our idea that credits count actually, because our model is best upon trust
which you should get when you give good reviews and all other kind of contributions.
Wait, don't we need a @group in order to appear it in the UI?
Comment #48
damiankloip CreditAttribution: damiankloip commented@znerol, I also created #2350491: Discuss/implement our usage of private services but did this on a train journey, and forgot to reference here.
Comment #49
alexpottCommitted c033857 and pushed to 8.0.x. Thanks!
Fixed on commit.