Having slept on it, I see that you have valid concerns to which I don't have good solutions, at least not at this time.
However, it seems that all of those concerns stem from two fixed ideas: 1. Creating individual middleware components must happen on-the-fly, possibly coupled to a DI container. 2. The composition of a middleware stack is somehow easier to understand or work with when represented as a flat stack or list. Neither of those ideas are fixed in my head, perhaps that's where our perspectives differ. I think we have very different ideas about what middleware can or should be used for. To me, it's a modular approach for classification and filtering of HTTP requests - it's not a new kind of application framework. The projects I've been building with middleware the past two years largely consist of something like: 1. Error handler 2. Application dispatch (router, controller dispatch) 3. 404 page In some cases, there's a middleware for caching etc. in there, or a logger - so with that, I think the most I've ever used is 5 components. I long since gave up on trying to put things like routing and session-management in there as separate components from application dispatch - the component I call application dispatch handles both routing, sessions and a cookie abstraction. If that sounds like a lot to put in a single component, it's not - we're literally talking about 25 lines of (trivial) code: invoke the router, create the session and cookie abstraction and inject them into a request context (a DI container), create the controller, and run it. Trying to spread those concerns across several components feels like "doing the right thing", because separation of concerns, right? But the more I thought about it, the more I found that this point of view is actually contrived. Classifying the request actually ends with the router - once the path/method have been mapped to a controller, the request has been classified, and we're ready to dispatch - we're out of the HTTP domain into the application domain. At that point, things like the cookie and session abstractions are dependencies of my controllers - they're how I choose to model and integrate with the HTTP domain on a high level. There's no reason that needs to be middleware. It's contrived, and only works because you put those middlewares in the correct order, so they can communicate - which is to say, they're really acting as a single component, they can't operate correctly in isolation (or out of order) and the apparent separation is only skin deep. Components like this one, for example, don't make sense to me: https://github.com/middlewares/fast-route/blob/master/src/FastRoute.php It's a vestigial router integration that doesn't actually route to anything - it doesn't dispatch anything. In fact, it doesn't really do anything - not directly - it only produces side-effects, by injecting the result into attributes, which is a means of communicating it's result to the component that is going to actually dispatch the controller. Clearly one is a dependency of the other. It's a clever way to hide your dependencies, but they're still there - only hidden, and much harder to debug. What does that accomplish? I mean, I get it, you want everything to be modular - you want things such as different router implementations to "plug and play" so you can just throw them on the stack and have them work. But that's not really possible - it doesn't really work that way, and implementing it as middleware doesn't change that fact, it just hides that fact. You haven't managed to abstract from anything - the dependency of some other component, on the result produced by this one, is still there, you've only managed to hide that fact. It's throwing complexity after more complexity - fighting fire with fire. You don't get modularity - what you get is fragmentation. I'm stomping on this particular case because it's a perfect example of what I would call middleware abuse. If you'd stop trying to do that, your typical application would have much fewer middleware components. It wouldn't matter (much) if you were creating the individual middleware components on-the-fly or not. Your application would bootstrap a single HTTP kernel (in a DI container) composed of some number of proxies/decorators. I think where we differ is, you want to actually build the application itself out of middleware components? I think that's contrived. The DI container and abstractions is a better way to structure the application itself. To me, middleware is just a better, more structured way to classify and filter HTTP requests - it's a well-structured approach to building front-controllers. It's not a "framework" or "architecture" for applications - that's a different layer. To me, a simple transaction like "takes a request, returns a response" perfectly describes a front-controller, which is all I need to do at this layer. I don't know, maybe I just have a very different idea of middleware from most people :-) On Fri, Apr 21, 2017 at 11:30 PM, Matthieu Napoli <[email protected]> wrote: > > This is Symfony's HttpKernelInterface and StackPHP and it has already >> been discussed at length >> >> Same principle, yes, but what I recall being discussed at length was the >> lambda-style vs double-pass aspect, which seems unrelated to this >> discussion. >> >> I found one or two older threads on this subject, here's one: >> >> https://groups.google.com/d/msg/php-fig/Ew36Ng5EwXE/52dAzZAbAQAJ >> >> The discussion quickly turns to the other, at the time dominant subject >> though. >> > > OK I've started to re-read the mega-thread (https://groups.google.com/ > forum/#!topic/php-fig/vTtGxdIuBX8%5B101-125%5D - 7 pages long) but most > of the debate was indeed about lambda-style/double-pass, and I'm clearly > not masochistic enough to read all of it again ^^ > > So yes maybe we should discuss that (to me it feels like "again" but maybe > that's just me). > > >> > I'm not sure why we are starting it all again? >> >> Are you're saying all of the concerns I described here have been >> discussed and are all invalid? >> >> According to the PSR-15 meta: >> >> - "There are currently two common approaches to server middleware that >> use HTTP Messages", single-pass and double-pass. >> >> - There's no mention of the fact that HttpKernelInterface doesn't have >> the delegate in the interface. >> >> - The section on "Delegate Design" talks about the design of that >> interface, but doesn't say why it exists in the first place. >> >> I recall there being lengthy discussions about how to name and describe >> the delegate interface, but skimming back through the discussion threads >> that are listed in the meta, it seems that the discussions all start from >> the assumption that a middleware interface has the delegate argument. >> > > So yes even if it was discussed or not, the fact that it lacks from the > metadocument is a sign there is something to improve here. > > My 2 cents about the Stack approach: > > - AFAIK it didn't work. And it was not tried in some weekend project: it > was built on Symfony's interfaces, so pretty solid stuff and it got a good > chance for success. On the other hand PSR-7 middlewares were incompatible > with most of the existing architectures at the time (incompatible with > Symfony for example) and without a standard interface (rather a "callable" > convention) and it worked out! So to me that's a clear sign. > - it's harder to compose middlewares because they are all nested: the > "pipe" pattern was a huge help IMO in spreading the concept > - it's harder to write reusable middlewares because you need to > standardize the constructor (see the Stack "convention" about the > constructor which is less than ideal) > - it's harder to wire in DI containers > - lazy middlewares are doable but not as easy > > That's all I have on my mind right now. And we may or may not have > discussed it before, but the PHP community has already tried it. That kind > of middleware has existed for a long time, and right now what works and > what's spreading is not that kind of middlewares. I believe that's because > of good reasons and we should not ignore that and start all over again. > > Matthieu > > -- > 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/b8317921-172a-476c-9df7-5b47e1f089da%40googlegroups.com > <https://groups.google.com/d/msgid/php-fig/b8317921-172a-476c-9df7-5b47e1f089da%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_jGzbWQaAPY3kyUD-G-%2BJ9MDdMVJOj%3Dinx1Nmb%2BLE902A%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.
