The FinalHandler concept in zend-stratigility comes essentially whole cloth from Sencha Connect (with the primary difference being the arguments passed to it). It's unique in the PHP ecosystem, and provided an early, catchall way to handle both 404 conditions and server errors.
However, it also introduces some cognitive overhead, does not play well with middleware-based error handling, and can lead to unexpected results due to how pipeline ordering works once an error handler is invoked.
The idea I present is to do the following:
- Remove the
FinalHandler
class entirely. - Rename the
$out
argument to$next
in bothMiddlewareInterface
andMiddlewarePipe::__invoke()
; additionally, make it required. - Remove the
ErrorMiddlewareInterface
entirely. - Remove the
Dispatch
class entirely; essentially, it's no longer relevant, as it existed to determine if the handler should be invoked as regular middleware or error middleware. With no concept of error middleware, we can just invoke the middleware with the request, response, and next arguments. Additionally, its try/catch block can be implemented in middleware; see below for more information.- Remove the
$err
argument and arity features
- Remove the
- Update
Next
to:- remove the
$done
constructor argument and property. - remove the
$dispatch
property and instantiation in the constructor. - modify
__invoke()
to either raise an exception or return the response when the queue is empty. (My inclination is that this should raise an exception, which can be caught per the error middleware outlined below.) - pull the handler from the route and invoke it directly (current line 113, reading
$result = $dispatch($layer, $err, $request, $response, $this)
becomes$middleware = $layer->handler; $result = $middleware($request, $response, $this)
)
- remove the
- Provide generic middleware for each of:
- handling 404 conditions. This middleware would be either the innermost middleware, or directly following middleware that provides routing and/or dispatch of routed middleware.
- handling PHP exceptions/errors via try/catch and/or registered exception and/or error handlers. This middleware would be the outermost layer (or one of the outermost layers; logging, for instance, might be even further out).
These changes would:
- make interoperability easier. Right now, we have two different signatures, one for normal middleware, one for error middleware, and the latter is not used with any middleware dispatchers other than Stratigility.
- simplify internals and maintentance.
- provide more flexibility around error handling strategies in general.
- provide a more robust mechanism around error handling.
The target is for Stratigility 2.0.
We would need to do some work in the 1.X series to support this:
- Mark
FinalHandler
as deprecated. - Mark
ErrorMiddlewareInterface
as deprecated. - Mark
Dispatch
as deprecated. - Potentially raise an
E_USER_DEPRECATED
when one of the following conditions appears:$err
is passed toNext
Dispatch
receives anErrorMiddlewareInterface
or arity 4 middleware to dispatch
- Provide the 404 and error middleware implementations suggested for version 2; these will work in the stack already anyways.
- Provide an alternate final handler version that acts as a no-op, returning only the response provided on invocation, and ignoring all other arguments.
- Provide the ability to specify a "final handler" class or callable via setter in
MiddlewarePipe
; if present,__invoke()
would use it if$out
isnull
.
These latter three points would provide a forwards-compatible migration path for users; they could pass in the no-op final handler to the application MiddlewarePipe
before passing it to the server, and layer in the 404 and error middleware as needed.
- My own site has a prototype version of this in place already, albeit built on top of the Expressive classes.