Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Forward port error handling to 2.0.0 #79

Merged
23 changes: 23 additions & 0 deletions doc/book/error-handlers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ You can typically handle these conditions via middleware itself.

## Handling 404 conditions

- Since 1.3.0

If no middleware is able to handle the incoming request, this is typically
representative of an HTTP 404 status. Stratigility provides a barebones
middleware that you may register in an innermost layer that will return a 404
Expand Down Expand Up @@ -64,6 +66,27 @@ class NotFoundMiddleware implements ServerMiddlewareInterface

## Handling PHP errors and exceptions

- Since 1.3.0

> ### Opting in to error middleware
>
> If you have upgraded from Expressive 1.0.0, you will have been using the
> `FinalHandler` implementation, and relying on the fact that, internally,
> dispatching wraps all middleware in `try/catch` blocks.
>
> Starting in 1.3.0, we provide a new way to handle errors via middleware.
>
> **To opt-in to the new system, you must call `raiseThrowables()` on your
> middleware pipeline:**
>
> ```php
> $pipeline = new MiddlewarePipe();
> $pipeline->raiseThrowables();
> ```
>
> (Starting in 2.0.0, this will no longer be necessary, but until then, this is
> how you opt-in to the system described below.)

`Zend\Stratigility\Middleware\ErrorHandler` is a middleware implementation to
register as the *outermost layer* of your application (or close to the outermost
layer). It does the following:
Expand Down
15 changes: 13 additions & 2 deletions doc/book/migration/to-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,17 @@ These approaches, however, have several shortcomings:
Starting in 1.3, we are promoting using standard middleware layers as error
handlers, instead of using the existing error middleware/final handler system.

To achieve this, we have provided some new functionality, as well as augmented
existing functionality:
The first step is to opt-in to having throwables and exceptions raised by
middleware, instead of having the dispatcher catch them and then invoke
middleware. Do this via the `MiddlewarePipe::raiseThrowables()` method:

```php
$pipeline = new MiddlewarePipe();
$pipeline->raiseThrowables();
```

Once you have done that you may start using some of the new functionality, as
well as augmented existing functionality:

- [NotFoundHandler middleware](../error-handlers.md#handling-404-conditions)
- [ErrorHandler middleware](../error-handlers.md#handling-php-errors-and-exceptions)
Expand Down Expand Up @@ -133,6 +142,8 @@ request and a response, and be guaranteed to return a response instance.)

To summarize:

- Call the `raiseThrowables()` method of your `MiddlewarePipe` instance to
opt-in to the new error handling strategy.
- Use the new `Zend\Stratigility\Middleware\NotFoundHandler` as the innermost
layer of your application pipeline in order to provide 404 responses.
- Use the new `Zend\Stratigility\Middleware\ErrorHandler` middleware as the
Expand Down
55 changes: 55 additions & 0 deletions src/Exception/MiddlewareException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* @link http://github.com/zendframework/zend-stratigility for the canonical source repository
* @copyright Copyright (c) 2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\Stratigility\Exception;

use RuntimeException;

/**
* Exception raised when a string $err is provided and raise throwables is enabled.
*
* @todo Remove for 2.0.0.
*/
class MiddlewareException extends RuntimeException
{
/**
* Create an instance based on an error value.
*
* @param mixed $err
* @return self
*/
public static function fromErrorValue($err)
{
if (is_object($err)) {
return self::fromType(get_class($err));
}

if (is_array($err)) {
return self::fromType(gettype($err));
}

if (is_string($err)) {
throw new self($err);
}

return self::fromType(var_export($err, true));
}

/**
* Create an instance using a templated error string.
*
* @param string $value
* @return self
*/
private static function fromType($value)
{
return new self(sprintf(
'Middleware raised an error condition: %s',
$value
));
}
}
20 changes: 17 additions & 3 deletions src/MiddlewarePipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@ public function __construct()
* Takes the pipeline, creates a Next handler, and delegates to the
* Next handler.
*
* If $out is a callable, it is used as the "final handler" when
* $next has exhausted the pipeline; otherwise, a FinalHandler instance
* is created and passed to $next during initialization.
* $delegate will be invoked if the internal queue is exhausted without
* returning a response; in such situations, $delegate will then be
* responsible for creating and returning the final response.
*
* $delegate may be either a DelegateInterface instance, or a callable
* accepting at least a request instance (in such cases, the delegate
* will be decorated using Delegate\CallableDelegateDecorator).
*
* @param Request $request
* @param Response $response
Expand Down Expand Up @@ -160,6 +164,16 @@ public function setCallableMiddlewareDecorator(Middleware\CallableMiddlewareWrap
$this->callableMiddlewareDecorator = $decorator;
}

/**
* Enable the "raise throwables" flag.
*
* @deprecated Since 2.0.0; this feature is now a no-op.
* @return void
*/
public function raiseThrowables()
{
}

/**
* @param Response $prototype
* @return void
Expand Down
20 changes: 20 additions & 0 deletions src/Next.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Psr\Http\Message\ServerRequestInterface;
use RuntimeException;
use SplQueue;
use Throwable;

/**
* Iterate a queue of middlewares and execute them.
Expand All @@ -34,6 +35,15 @@ class Next implements DelegateInterface
*/
private $queue;

/**
* Flag indicating whether or not the dispatcher should raise throwables
* when encountered, and whether or not $err arguments should raise them;
* defaults false.
*
* @var bool
*/
private $raiseThrowables = false;

/**
* @var string
*/
Expand Down Expand Up @@ -133,6 +143,16 @@ public function process(RequestInterface $request)
return $response;
}

/**
* Toggle the "raise throwables" flag on.
*
* @deprecated Since 2.0.0; this functionality is now a no-op.
* @return void
*/
public function raiseThrowables()
{
}

/**
* Reset the path, if a segment was previously stripped
*
Expand Down
1 change: 1 addition & 0 deletions test/MiddlewarePipeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use ReflectionProperty;
use RuntimeException;
use Zend\Diactoros\ServerRequest as Request;
use Zend\Diactoros\Response;
use Zend\Diactoros\Uri;
Expand Down
2 changes: 2 additions & 0 deletions test/NextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

class NextTest extends TestCase
{
protected $errorHandler;

public function setUp()
{
$this->queue = new SplQueue();
Expand Down