I think it's great that you're coming around to the idea, Woody :-)

But I am no longer trying to view this as an alternative to PSR-15,
because, assuming we standardize on that handler-interface first, there is
nothing about PSR-15 that prevents us from exploring other options.

Middleware developers can ship packages with middleware and handler
implementations if and when that makes sense. So this doesn't need to be an
ultimatum.

I can see the handler-interface having 3 uses in the context of PSR-15:

1. As the common interface of middleware dispatchers.

2. As the type-hint on delegates.

3. As type-hint for the "final" handler supported by many dispatchers.

That interface has an endless number of uses, and I'm sure we'll provide
interop on a lot of points we haven't even thought of yet.

Now, one could debate whether PSR-15 has anything to do with interop in the
first place, or is it mostly a "framework in disguise", given that PSR-15
middleware doesn't work without a dispatcher.

I don't stop much care anymore. There are already a lot of people using
PSR-15 and depending on it, and there is no good reason to disrupt that
more than necessary.

I do think it's in everybody's best interest to disrupt it a little, to
make sure it does what it does _and_ is itself interoperable with a wider
ecosystem. I think that standardizing on the handler interface will do that.

Let's push for the standardization of the handler interface ASAP so we can
finalize PSR-15 and bring it to a close? :-)


On May 17, 2017 03:29, "Woody Gilk" <woody.g...@gmail.com> wrote:

> I reviewed what Rasmus and Michael (et al) are saying about different
> kinds of handlers and I _think_ I'm starting to understand what they are
> talking about. It goes way beyond what is proposed in PSR-15. I would even
> say that's an entirely different way of thinking about middleware. (Please
> correct me if any of the following is wrong. ;)
>
> For the most part, we have thought about middleware as being basically a
> list of things that the request drops through and something inside the list
> will produce a response:
>
> req -> [set-client-ip, require-auth, parse-body, handle-req] -> res
>
> And if something goes awry during that sequence, the rest of the
> middleware will be skipped by returning early:
>
> req -> [set-client-ip, require-auth (failed), parse-body, handle-req] ->
> res
>
> This works perfectly well for simple systems and not so well for more
> complex systems. For instance, if we have a complex application where there
> is a "user" part and an "admin" part, the admin part needs a different
> sequence:
>
> req -> [set-client-ip, require-auth, *require-admin-role*, parse-body,
> handle-req] -> res
>
> In the case of something like Slim Framework, this is accomplished at the
> routing level, where any route can have middleware attached to it:
> https://www.slimframework.com/docs/concepts/middleware.
> html#route-middleware
>
> What Michael seems to be thinking about is these sorts of complex
> scenarios where having the same sequence of middleware executed for every
> request is insufficient. Instead, the middleware becomes more of a tree
> structure, where each middleware might do 3 possible things:
>
> 1. modify the request
> 2. generate a response (possibly by delegating to another middleware
> chain!)
> 3. modify the response
>
> Now, to be clear, there's nothing in PSR-15 that says this is not
> possible. In fact, several dispatchers already support nested middleware.
> From what I can tell, Michael's objection (and maybe Rasmus' too) is more
> about that fact that the three possible actions are not distinct. With
> distinct interfaces for the three actions, this becomes a thing:
>
> (Side note, I am using my own made up interfaces here, not the ones that
> Michael has proposed.)
>
> $foo = new FooMiddleware(); // implements ModifyRequestInterface
> $request = $foo->modify($request);
>
> With just one interface, this is impossible because the middleware has to
> return a response or delegate to something else that does. So rather than
> having a dispatching system that runs X middleware, one of which is assumed
> to return a response by handling the request, it becomes possible to use a
> simple routing system that just points at a handler class:
>
> class AdminCreatePage implements ServerRequestHandlerInterface
> {
>     public function __construct(
>         SetClientIp $ip,
>         RequireAuth $auth,
>         RequireAdminRole $admin,
>         ParseBody $body
>     ) { ... }
>
>     public function handle(ServerRequestInterface $req)
>     {
>         try {
>             $req = $this->ip->process($req);
>             $req = $this->auth->process($req);
>             $req = $this->admin->process($req);
>             $req = $this->body->process($req);
>         } catch (Exception $e) {
>             return $this->errorResponse($e);
>         }
>
>         // do whatever domain stuff
>         return $this->successResponse();
>     }
> }
>
> This a pretty contrived example and the various modifying middleware would
> probably be wrapped into a single class, but I think it illustrates the
> difference between PSR-15 and what Michael is proposing.
>
> Honestly, I see a lot of value in this approach. It works better for
> complex applications and is much more flexible. It also makes it easy to
> share various kinds of middleware as stand alone components, or even
> collections of middleware grouped into a single class that implements one
> of the three interfaces. With variadic declarations and some functional
> tricks, it could be a really powerful approach. Is it middleware? Not by
> the definition that we have been using. Is it interesting? Definitely. I am
> definitely coming around to the idea.
>
> --
> Woody Gilk
> http://about.me/shadowhand
>
> On Tue, May 16, 2017 at 12:27 AM, Geert Eltink <geert.elt...@gmail.com>
> wrote:
>
>> On Monday, May 15, 2017 at 4:00:53 PM UTC+2, Michael Mayer wrote:
>>
>>
>>> Can we achieve the same by using only MiddlewareInterface and simply
>>> ignoring $next/$delegate? Matthew wrote:
>>>
>>> ```php
>>>> $app->route('/api/blog', [
>>>>     Authentication::class,
>>>>     Authorization::class,
>>>>     Blog::class,
>>>> ], ['GET', 'POST']);
>>>> ```
>>>>
>>>> …
>>>>
>>>> Now, does innermost middleware need to receive the delegate? No, but by
>>>> having a
>>>> single interface to describe any link in the chain, versus two
>>>> interfaces (one
>>>> for middleware that delegates, one for the final dispatched "things" --
>>>> controller, action, what have you), we simplify how application
>>>> stacks/kernels
>>>> are built, as everything can be injected exactly the same.
>>>>
>>>
>>> Honestly, I cannot image how implementing Blog::class as middleware
>>> would _simplify_ anything. Neither for applications nor for users.
>>>
>>> For applications it would be bad, because:
>>> 1. if the stack runs empty, the application must provide a
>>> mechanism/convention to deal with that
>>>
>>
>> In Expressive 1 there was a mechanism (FinalErrorHandler) to deal with
>> empty stacks. It was confusing for users. In Expressive 2 this got changed
>> and the last middleware on the stack is always a NotFoundMiddleware.
>> Basically if the stack runs empty and it gets to the NotFoundMiddleware it
>> means no Response is returned and you can return a 404.
>>
>>
>>> 2. the application must create a delegate object to call Blog::class,
>>> even if it is not used
>>>
>>> For users it would be bad, because:
>>> 1. it is easy to misconfigure your stack: either by _forgetting_ to add
>>> Blog::class or by adding middlewares after Blog::class
>>>
>> 2. it would be confusing if Blog::class is called with a $delegate, when
>>> it is not allowed to use it
>>>
>>
>> Isn't this where PHPUnit comes in? If you create the right tests for this
>> you know your stack is configured correctly and that the url
>> `/blog/some-post` actually can return a blog post.
>>
>> I think it's more confusing if you have a lot of different middleware
>> interfaces. Why is the Blog::class not allowed to use the delegate? I've
>> actually seen people doing it. I can't remember the use case or if it was a
>> good solution, but they had 3 ActionMiddlewares stacked. If some conditions
>> were met the next one would be executed and otherwise it would return a
>> Response.
>>
>>
>>> If Blog::class implements HandlerInterface (or DelegateInterface)
>>> instead, then things will become simpler IMO:
>>> 1. the stack can be verified earlier; thus the app can fail before
>>> creating middlewares or at least before Blog::class is called
>>>
>> 2. the last Middleware will be called with the Blog::class as $next – no
>>> EmptyStackHandler/Delegate or similar is needed
>>>
>>
>> So let's say you have a RouterMiddleware. It takes care of parsing the
>> url and redirect to the configured Action::class. However that Action class
>> needs some specific middleware as well which are added to the stack when
>> the route is determined. So you can't check for a valid stack until all
>> middleware before the RouterMiddleware is executed and the RouterMiddleware
>> itself.
>>
>> I'm getting the impression that people want to regulate to much with
>> interfaces and that would restrict how an application (could) work. And at
>> the end of the day you end up with 10 middleware frameworks which do all
>> the same thing in the same way.
>>
>> --
>> 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/ms
>> gid/php-fig/42102e4a-ffeb-46ce-aa51-b117dbf6975c%40googlegroups.com
>> <https://groups.google.com/d/msgid/php-fig/42102e4a-ffeb-46ce-aa51-b117dbf6975c%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 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/CAGOJM6KFn4Gc3X7mLnvJp9c_fobmtMmcXj70ht7rWe05oHcJJw%
> 40mail.gmail.com
> <https://groups.google.com/d/msgid/php-fig/CAGOJM6KFn4Gc3X7mLnvJp9c_fobmtMmcXj70ht7rWe05oHcJJw%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 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_g7NsQbjFtdwL411K1Z1RGHQuror_gHqythPXOzagQ5VQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to