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

Commit 9fb8ce0

Browse files
committed
Implement an ErrorMiddlewarePipe
This patch implements an `ErrorMiddlewarePipe`. Unfortunately, `MiddlewarePipe` implements `MiddlewareInterface`, which makes it impossible to extend it and re-purpose it as error middleware. As such, this implementation wraps the `MiddlewarePipe` instance, and consumes its internal pipeline in order to dispatch a pipeline of error middleware.
1 parent 320031a commit 9fb8ce0

File tree

3 files changed

+100
-6
lines changed

3 files changed

+100
-6
lines changed

src/ErrorMiddlewarePipe.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @see https://github.com/zendframework/zend-expressive for the canonical source repository
6+
* @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license https://github.com/zendframework/zend-expressive/blob/master/LICENSE.md New BSD License
8+
*/
9+
10+
namespace Zend\Expressive;
11+
12+
use Psr\Http\Message\ServerRequestInterface as Request;
13+
use Psr\Http\Message\ResponseInterface as Response;
14+
use Zend\Stratigility\FinalHandler;
15+
use Zend\Stratigility\MiddlewarePipe;
16+
use Zend\Stratigility\Next;
17+
18+
/**
19+
* MiddlewarePipe implementation that acts as error middleware.
20+
*
21+
* Normal MiddlewarePipe implementations implement Zend\Stratigility\MiddlewareInterface,
22+
* which can be consumed as normal middleware, but not as error middleware, as
23+
* the signature for error middleware differs.
24+
*
25+
* This class wraps a MiddlewarePipe, and consumes its internal pipeline
26+
* within a functor signature that works for error middleware.
27+
*
28+
* It is not implemented as an extension of MiddlewarePipe, as that class
29+
* implements the MiddlewareInterface, which prevents its use as error
30+
* middleware.
31+
*/
32+
class ErrorMiddlewarePipe
33+
{
34+
/**
35+
* @var MiddlewarePipe
36+
*/
37+
private $pipeline;
38+
39+
/**
40+
* @param MiddlewarePipe $pipe
41+
*/
42+
public function __construct(MiddlewarePipe $pipeline)
43+
{
44+
$this->pipeline = $pipeline;
45+
}
46+
47+
/**
48+
* Handle an error request.
49+
*
50+
* This is essentially a version of the MiddlewarePipe that acts as a pipeline
51+
* for solely error middleware; it's primary use case is to allow configuring
52+
* arrays of error middleware as a single pipeline.
53+
*
54+
* Operation is identical to MiddlewarePipe, with the single exception that
55+
* $next is called with the $error argument.
56+
*
57+
* @param mixed $error
58+
* @param Request $request
59+
* @param Response $response
60+
* @param callable $out
61+
* @return Response
62+
*/
63+
public function __invoke($error, Request $request, Response $response, callable $out = null)
64+
{
65+
$pipeline = $this->getInternalPipeline();
66+
$done = $out ?: new FinalHandler([], $response);
67+
$next = new Next($pipeline, $done);
68+
$result = $next($request, $response, $error);
69+
70+
return ($result instanceof Response ? $result : $response);
71+
}
72+
73+
/**
74+
* Retrieve the internal pipeline from the composed MiddlewarePipe.
75+
*
76+
* Uses reflection to retrieve the internal pipeline from the composed
77+
* MiddlewarePipe, in order to allow using it to create a Next instance.
78+
*
79+
* @return \SplQueue
80+
*/
81+
private function getInternalPipeline()
82+
{
83+
$r = new ReflectionProperty($this->pipeline, 'pipeline');
84+
$r->setAccessible();
85+
return $r->getValue($this->pipeline);
86+
}
87+
}

src/MarshalMiddlewareTrait.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private function prepareMiddleware($middleware, ContainerInterface $container =
8787
* @param null|ContainerInterface $container
8888
* @param bool $forError Whether or not the middleware pipe generated is
8989
* intended to be populated with error middleware; defaults to false.
90-
* @return MiddlewarePipe
90+
* @return MiddlewarePipe When $forError is true, returns an ErrorMiddlewarePipe.
9191
* @throws Exception\InvalidMiddlewareException for any invalid middleware items.
9292
*/
9393
private function marshalMiddlewarePipe(array $middlewares, ContainerInterface $container = null, $forError = false)
@@ -100,6 +100,10 @@ private function marshalMiddlewarePipe(array $middlewares, ContainerInterface $c
100100
);
101101
}
102102

103+
if ($forError) {
104+
return new ErrorMiddlewarePipe($middlewarePipe);
105+
}
106+
103107
return $middlewarePipe;
104108
}
105109

test/Container/ApplicationFactoryTest.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Zend\Expressive\Container\ApplicationFactory;
2424
use Zend\Expressive\Container\Exception as ContainerException;
2525
use Zend\Expressive\Emitter\EmitterStack;
26+
use Zend\Expressive\ErrorMiddlewarePipe;
2627
use Zend\Expressive\Exception\InvalidMiddlewareException;
2728
use Zend\Expressive\Router\FastRouteRouter;
2829
use Zend\Expressive\Router\Route;
@@ -1280,14 +1281,16 @@ public function testProperlyRegistersNestedErrorMiddlewareAsLazyErrorMiddleware(
12801281

12811282
$nestedPipeline = $pipeline->dequeue()->handler;
12821283

1283-
$this->assertInstanceOf(MiddlewarePipe::class, $nestedPipeline);
1284-
$r = new ReflectionMethod($nestedPipeline, '__invoke');
1285-
$this->assertEquals(4, $r->getNumberOfParameters(), 'Error pipeline is not error middleware');
1284+
$this->assertInstanceOf(ErrorMiddlewarePipe::class, $nestedPipeline);
12861285

12871286
$r = new ReflectionProperty($nestedPipeline, 'pipeline');
12881287
$r->setAccessible(true);
1289-
$pipeline = $r->getValue($nestedPipeline);
1290-
$middleware = $pipeline->dequeue()->handler;
1288+
$internalPipeline = $r->getValue($nestedPipeline);
1289+
$this->assertInstanceOf(MiddlewarePipe::class, $internalPipeline);
1290+
1291+
$r = new ReflectionProperty($internalPipeline, 'pipeline');
1292+
$r->setAccessible(true);
1293+
$middleware = $r->getValue($internalPipeline)->dequeue()->handler;
12911294

12921295
$this->assertInstanceOf(Closure::class, $middleware);
12931296
$r = new ReflectionFunction($middleware);

0 commit comments

Comments
 (0)