Skip to content

Instantly share code, notes, and snippets.

@weierophinney
Last active September 22, 2016 17:55
Show Gist options
  • Save weierophinney/db0a5b1d2d8ec1b8871e9ebb72a4091c to your computer and use it in GitHub Desktop.
Save weierophinney/db0a5b1d2d8ec1b8871e9ebb72a4091c to your computer and use it in GitHub Desktop.
Idea for removing the final handler concept from Stratigility

[zend-stratigility] RFC: Remove FinalHandler, ErrorMiddlewareInterface

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 both MiddlewareInterface and MiddlewarePipe::__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
  • 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))
  • 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 to Next
    • Dispatch receives an ErrorMiddlewareInterface 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 is null.

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.

Examples

  • My own site has a prototype version of this in place already, albeit built on top of the Expressive classes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment