Very good summary and well argued, thanks David!

The factory pattern you're proposing is very similar to what Shelf does in
Dart - the only real difference is they're doing it with typedefs, but yes,
we can accomplish effectively the same thing with interfaces.

In my opinion, this approach will provide considerably better modularity
than the current proposal, because it will make it very easy to composer a
few middlewares insides a middleware-factory, which in turn will make it
very easy to host several of these multi-middlewares ("modules" if you
like) as part of an application, whether manually bootstrapped or
auto-magically by priority using some kind of framework.

I think this will provide more architectural freedom, and will make it much
easier to write handlers or middleware that composes multiple handlers -
e.g. being able to compose two or more low-level handlers or middleware
into a more high-level handler or middleware, without needing to depend on
a dispatcher or "pipe" implementation. This helps alleviate dependency
issues too - if I ship a middleware internally composed of one or more
internal handlers, I don't have to ship that component with a dependency on
a specific dispatcher/pipe.

The only thing I disagree with is the term "middleware" - the
handler-interface need not be designated as middleware-specific, it has
many much more versatile uses. For example, a dispatcher or framework can
provide both a handler for direct use, as well as factory that makes it
easy to compose an instance of a sub-stack as part of your
application-stack.

So we really do need to standardize on handlers first.

https://github.com/http-message-strategies-interop/fig-standards/blob/http-message-strategies/proposed/http-message-strategies/http-message-strategies.md


On Sat, May 13, 2017 at 12:38 PM, David Négrier <david.negr...@gmail.com>
wrote:

> Hey guys,
>
> Catching up on the thread. Just read Matthew's and Beau's post as well as
> Rasmus ones.
>
> I'm still surprised because I feel like I proposed a solution to the
> problem 2 weeks ago and nobody noticed (or cared to point me that I'm
> wrong).
>
> So I'll try to explain it again.
>
> Rasmus is annoyed by the current proposal because a middleware might have
> 0 or many delegates (but the current proposal only allows for one "main"
> delegate.
> Yet, if we remove the delegate and go the "Stack" way, working with DI
> containers becomes a real mess. In particular, it becomes almost impossible
> to write "bundles" that ship middlewares that should be applied by a
> priority index. Or we need to rely on a "convention" (like next middleware
> being passed as first argument of the constructor), and we know conventions
> suck.
>
> But we can really solve this:
>
> interface ServerMiddlewareInterface // aka the HandlerInterface
> {
>     public function __invoke(ServerRequestInterface $request) :
> ResponseInterface
> }
>
>
> interface MiddlewareFactoryInterface
> {
>     public function createMiddleware(ServerMiddlewareInterface $next) :
> ServerMiddlewareInterface
> }
>
> The first interface is the "HandlerInterface" Rasmus is talking about. It
> is the "purest" thinkg we can imagine. A class that takes a request and
> returns a response.
> But as we know, it does not play well with Pipes/Stacks. So rather that
> stacking middlewares (or handlers), I'm proposing we are stacking
> "Middleware factories". Factories are objects that create a middleware
> given the "next" middleware.
>
> So this:
>
> ```php
> $app->route('/api/blog', [
>     Authentication::class,
>     Authorization::class,
>     Blog::class,
> ], ['GET', 'POST']);
> ```
>
> becomes this:
>
> ```php
> $app->route('/api/blog', [
>     AuthenticationFactory::class,
>     AuthorizationFactory::class,
>     BlogFactory::class,
> ], ['GET', 'POST']);
> ```
>
> From the list of factories, we can create back the middlewares.
>
> Look at the signature of the factory:
>     public function createMiddleware(ServerMiddlewareInterface $next) :
> ServerMiddlewareInterface
>
> The factory will be passed the "next" middleware/handler (or a
> delegate/proxy, this is an implementation detail) and return the created
> middleware.
>
> Doing things this way has a ton of advantages:
>
> - it answers Rasmus concerns:
>     - we are standardizing the "HandlerInterface" (or MiddlewareInterface,
> whatever the name)
>     - Middlewares can have 0, 1 or many delegates, it does not matter
> regarding this interface
>     - you can use only "Handlers/Middlewares" directly in your application
> and disregard middleware factories completely if you don't use them
> - yet, it answers Matthew's concerns too:
>     - it plays well with DI, as you can stack/pipe middleware factories
>     - it does not rely on a convention
>     - depending on the way you implement your framework, you can have lazy
> instantiation of the middlewares (for instance using proxies)
>
> Even better, regarding performance, it can be quite quick too. Just
> imagine a scenario where you use ReactPHP (and therefore, middlewares are
> reused). Using middleware factories, the middleware "tree" is constructed
> once at the beginning of the application and then, the request is piped
> from one middleware to the next one without going through the "delegate" we
> currently have (so it should be faster in the case of ReactPHP).
>
> This proposal looks a lot like Stack, but it removes the convention part
> and replaces it with a factory interface.
>
> I'd be interested in having Beau and Matthew feedback on this idea. Do you
> see the benefit? Is something not clear? Or is there a flaw I did not see?
>
> ++
> David.
>
>
>
> Le vendredi 12 mai 2017 12:49:36 UTC+2, Rasmus Schultz a écrit :
>>
>> > I don't think it is at all natural to say "all middleware must be
>> context free and middleware order never matters". That's simply unrealistic.
>>
>> That's not what I'm saying.
>>
>> Of course middleware components are going to communicate facts about the
>> state of the Request and Response - that's the whole point.
>>
>> Of course middleware order matters, as far as getting a meaningful
>> result, I'm not trying to say otherwise.
>>
>> What I'm saying is, if you use the Request object as a transport
>> mechanism for your _domain_ objects, stuffed into reserved attributes,
>> you're first of all using the Request object as a service locator, which is
>> bad practice.
>>
>> You're no longer communicating facts about the state of the Request or
>> Response, you're just overloading the Request model as a means to avoid
>> dealing with a dependency issue. Worse, you're hiding the fact that
>> dependency exists, as is the general issue with service location.
>>
>> There are always going to be better, cleaner, simpler, more transparent
>> patterns that don't hide your dependencies or solve the problem by using
>> the Request model as a kind of "container" for your domain objects.
>>
>> IN MY OPINION, attributes in the Request object are widely abused as a
>> general solution to all sorts of dependency issues, when, really, they
>> probably ought not to be used for much else other than an additional source
>> of parameters.
>>
>> Per the PSR-7 definition, attributes hold "parameters derived from the
>> request" - I find that stuffing your domain objects in there under reserved
>> keys is a bit of a stretch, even if your domain objects were somehow
>> "derived from the request", with examples given such as "the results of
>> path match operations; the results of decrypting cookies; the results of
>> deserializing non-form-encoded message bodies; etc.", I think it's quite a
>> long reach.
>>
>> Bottom line, if it doesn't really function like middleware, why did you
>> implement it as middleware? Wrong abstraction for the job.
>>
>>
>> On Wed, May 10, 2017 at 11:36 PM, Woody Gilk <woody...@gmail.com> wrote:
>>
>>> On Wed, May 10, 2017 at 1:54 PM, Rasmus Schultz <ras...@mindplay.dk>
>>> wrote:
>>>
>>>> In my opinion, if a middleware component doesn't work when taken out of
>>>> context from other middleware, or is dependent on related middleware and
>>>> execution order etc., it's no longer middleware, strictly speaking - at
>>>> that point, you're overloading the middleware mechanism with
>>>> responsibilities that don't belong to that domain and don't naturally fit
>>>> there.
>>>
>>>
>>> That's purely an opinion and I definitely don't agree about the
>>> execution order. Would this make sense:
>>>
>>> middleware = [
>>>   ActionHandler,
>>>   ClientIp,
>>>   Authorization,
>>> ]
>>>
>>> Of course not! Some middleware will always need to be early or late in
>>> processing. Authentication and authorization will need to be early, and
>>> action handling will need to be late. I don't think it is at all natural to
>>> say "all middleware must be context free and middleware order never
>>> matters". That's simply unrealistic.
>>>
>>> --
>>> Woody Gilk
>>> http://about.me/shadowhand
>>>
>>> --
>>> 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/to
>>> pic/php-fig/B3jtdJA7-6w/unsubscribe.
>>> To unsubscribe from this group and all its topics, send an email to
>>> php-fig+u...@googlegroups.com.
>>> To post to this group, send email to php...@googlegroups.com.
>>> To view this discussion on the web visit https://groups.google.com/d/ms
>>> gid/php-fig/CAGOJM6%2BKW4nFeyMb07%3DGvY0C9nj9ChtrCe8cHLAiJJs
>>> %3DPwjd-w%40mail.gmail.com
>>> <https://groups.google.com/d/msgid/php-fig/CAGOJM6%2BKW4nFeyMb07%3DGvY0C9nj9ChtrCe8cHLAiJJs%3DPwjd-w%40mail.gmail.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 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
> php-fig+unsubscr...@googlegroups.com.
> To post to this group, send email to php-fig@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/
> msgid/php-fig/3d95b640-8571-4937-91e7-30277b7a97cd%40googlegroups.com
> <https://groups.google.com/d/msgid/php-fig/3d95b640-8571-4937-91e7-30277b7a97cd%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 php-fig+unsubscr...@googlegroups.com.
To post to this group, send email to php-fig@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/CADqTB_gewQz6bfoVKUy1tNQVnHf0u%3DePezbnW-ubmEaewZQCBQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to