> the middleware themselves cannot be autowired and *must* be created
manually by the factory.

Are you highlighting this as a problem?

Of course the factory would need to create the middleware - that's what a
factory is for. (?)

> At the end of the day it comes down to whether the delegate/next of a
middleware should be a runtime dependency or an instance dependency. I
think that is the core of what Rasmus is exploring here. It seems like he
has decided it should not be a runtime dependency

I don't feel it's a decision so much as a conclusion.

I'd like it not to be the case, because it would surely be simpler, but the
fact is that there isn't always precisely one delegate - which leads me to
conclude it can't be a runtime dependency, or at least not if you want the
model to reflect reality, which is something I always strive for.

The fact that dispatchers need to generate a "mock" delegate that throws a
run-time exception, for example, is a symptom of this being incorrectly
modeled.

The fact that you can build "valid" middleware stacks with components that
can never be run, to me is a symptom of modeling things in an opinionated
manner.

The fact that you need a dispatcher in the first place, solely to work
around "mechanical" issues created by the abstraction itself, to me points
at something being "wrong".

Our dispatchers aren't solving any real problem - they exist solely to work
around problems created by the abstraction itself.

I think that the problem is worth exploring, and I think we can do better.

> I'm not happy about the idea that every middleware is going to
effectively require two classes be written

Let's explore that problem.

I think, first of all, you need to not think of this as "middleware
consisting of two classes" - it's really two different classes, one is a
handler, the other is middleware: they have different purposes in different
contexts.

In some cases, this separation is going to be useful in practice - in other
cases, the only interesting use of the handler is as middleware, and in
those cases, it's perhaps not very interesting to have this separation.

Can we make handlers work better in a DI container context, where we want
them created dynamically?

If the only problem is deferring the loading and creation of handlers, and
the issue is "not knowing" which constructor-argument is the delegate,
maybe we could solve this by marking that argument somehow...

I went ahead and tried it:

https://bitbucket.org/mindplaydk/middleware-experiment/src

Look at the test, and as you can see, the handlers are bootstrapped in the
DI container, and are loaded only as needed.

I wanted to use a tag/marker interface, but that's not really possible in
PHP, since we have neither type unions, nor tags - and a class alias is
likely the closest thing we have to a typedef.

It uses a proprietary factory-method from unbox, and this could of course
be abstracted further, so as to be decoupled from the DI container - this
is just a quick prototype, but it works, right?

Not saying this is *the solution*, but I think this at least demonstrates
that you can create a middleware-framework around a simple
handler-interface, with similar performance characteristics, and similar
ease of use.

The class alias feels a bit unorthodox, perhaps mostly because it's a
rarely-used feature... there may be another similar approach that's even
better...? I'm out of ideas right now :-)


On Sun, May 14, 2017 at 12:25 AM, Beau Simensen <[email protected]> wrote:

> On Saturday, May 13, 2017 at 12:31:20 PM UTC-5, Woody Gilk wrote:
>>
>> On Sat, May 13, 2017 at 12:12 PM, Rasmus Schultz <[email protected]>
>> wrote:
>>
>>> What do you mean by "middleware that does not return a response"?
>>>
>>
>> Ultimately it will. In PSR-15 this is done via delegate. My question is
>> more about how the interfaces proposed by David would do this delegation.
>> Right now I don't see how it would be possible without passing a factory to
>> a middleware, which (I thought) he was attempting to avoid.
>>
>
> I don't think that was what he was attempting to avoid. I think his was an
> attempt to solve the DI problem (the thing I care about) while still not
> having to pass the delegate to every middleware handle/__invoke call (I
> believe the idea that Rasmus is exploring). From what I see, this is done
> by injecting a factory concept into the workflow.
>
> The factory would receive the next/delegate at runtime and could thus be
> instantiated by an autowiring DI container upfront without any problems.
>
> It still requires the individual middleware & factory combo to have a way
> to ensure that the middleware instance stores the next/delegate prior to
> being invoked. Which means, most likely, that the middleware themselves
> cannot be autowired and *must* be created manually by the factory.
>
> In my mind, it moves the magic around in a way that is slightly more
> automated (meaning, I'd only be able to whine about DI concerns at a
> fraction of the volume of before because the *middleware factory* can
> still be autowired...) at the expense of adding an explicit factory to the
> workflow of every middleware.
>
> While I think this is a great way to move the discussion forward, I'm not
> happy about the idea that every middleware is going to effectively require
> two classes be written. I would guess that most factories are going to end
> up being proxies and will have all of the dependencies of the original
> middleware just to pass them on via constructor injection.
>
> Here is what this would look like using an actual middleware I've used in
> the past. I've kept the delegate naming for now just to show what we're
> actually talking about here in practical terms.
>
> BEFORE
>
> class UserIdMetadataEnrichment implements MiddlewareInterface
> {
>     private $metadataEnricherChain;
>     private $userIdentityMapper;
>
>     public function __construct(
>         MetadataEnricherChain $metadataEnricherChain,
>         UserIdentityMapper $userIdentityMapper
>     )
>     {
>         $this->metadataEnricherChain = $metadataEnricherChain;
>         $this->userIdentityMapper = $userIdentityMapper;
>     }
>
>     public function __invoke(RequestInterface $request, DelegateInterface 
> $delegate)
>     {
>         // ... do stuff
>
>     }
> }
>
>
>
> AFTER
>
> class UserIdMetadataEnrichmentFactory implements MiddlewareFactoryInterface
> {
>     private $metadataEnricherChain;
>     private $userIdentityMapper;
>
>     public function __construct(
>         MetadataEnricherChain $metadataEnricherChain,
>         UserIdentityMapper $userIdentityMapper
>     )
>     {
>         $this->metadataEnricherChain = $metadataEnricherChain;
>         $this->userIdentityMapper = $userIdentityMapper;
>     }
>
>     public function createMiddleware(DelegateInterface $delegate)
>     {
>         return new UserIdMetadataEnrichment(
>             $delegate,
>             $this->metadataEnricherChain,
>             $this->userIdentityMapper
>         )
>     }
> }
>
>
> class UserIdMetadataEnrichment implements MiddlewareInterface
> {
>     private $metadataEnricherChain;
>     private $userIdentityMapper;
>     private $delegate;
>
>     public function __construct(
>         DelegateInterface $delegate,
>         MetadataEnricherChain $metadataEnricherChain,
>         UserIdentityMapper $userIdentityMapper
>     )
>     {
>         $this->metadataEnricherChain = $metadataEnricherChain;
>         $this->userIdentityMapper = $userIdentityMapper;
>         $this->delegate = $delegate;
>     }
>
>     public function __invoke(RequestInterface $request)
>     {
>         // ... do stuff
>
>     }
> }
>
>
>
>
>
>
> At the end of the day it comes down to whether the delegate/next of a
> middleware should be a runtime dependency or an instance dependency. I
> think that is the core of what Rasmus is exploring here. It seems like he
> has decided it should not be a runtime dependency. I disagree.
>
> If the same middleware class is used in several points throughout an
> application, why might each individual instance know about the delegate it
> is responsible for? In which situations would this impact how the
> middleware will actually run?
>
> I've neither seen nor written middleware where the same instance mounted
> several places throughout an application did not work as intended and
> expected.
>
>
> TL;DR?
>
> The original proposal treats the delegate as a runtime dependency along
> with the request in question at that point in time. Rasmus is proposing the
> delegate becomes an instance dependency tying each instance of the
> middleware explicitly to a specific point in the application when it is
> constructed, requiring every instance of a middleware to be instantiated
> individually instead of reusing a shared instance and finding some way to
> get access to the delegate by some other means (constructor or setter
> injection?). David's proposal tries to resolve these two by providing a
> factory that allows a single factory instance to receive the delegate at
> runtime from any point in the application with the intention to create a
> middleware instance on the spot that ties the request and delegate to each
> other at that point in time.
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "PHP Framework Interoperability Group" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/
> topic/php-fig/B3jtdJA7-6w/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> [email protected].
> To post to this group, send email to [email protected].
> To view this discussion on the web visit https://groups.google.com/d/
> msgid/php-fig/e9af0c97-575d-4a76-8e22-01275f7f37a2%40googlegroups.com
> <https://groups.google.com/d/msgid/php-fig/e9af0c97-575d-4a76-8e22-01275f7f37a2%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/CADqTB_jWTBmPvo0ao2ybbZRxuPRPV64eVkoRJXiH03e28cUh1w%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to