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

Commit 6329c34

Browse files
committed
Provide a dedicated service for the server request error response generator
Instead of re-using the existing `ErrorResponseGenerator`, this patch does the following: - Separates functionality for generating the error response into a trait, `Zend\Expressive\Response\ErrorResponseGeneratorTrait`. The method `prepareTemplatedResponse()` now expects the error, the response, and an array of data to provide to the template. - Updates `Zend\Expressive\Middleware\ErrorResponseGenerator` to use the new trait. - Creates a new class, `Zend\Expressive\Response\ServerRequestErrorResponseGenerator`, which expects a response factory, and optionally the debug flag, renderer, and template to render to. It composes the trait, and generates a response on-the-fly to pass to the trait methods. The `RequestHandlerRunnerFactory` has been updated to use this new service, and the `ServerRequestErrorResponseGeneratorFactory` was updated to generate an instance of the new class. These changes mean the constant SERVER_REQUEST_ERROR_RESPONSE_GENERATOR is no longer needed, and was thus removed.
1 parent ab064f2 commit 6329c34

10 files changed

+269
-139
lines changed

src/ConfigProvider.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public function __invoke() : array
3232

3333
public function getDependencies() : array
3434
{
35+
// @codingStandardsIgnoreStart
3536
return [
3637
'aliases' => [
3738
DEFAULT_DELEGATE => Handler\NotFoundHandler::class,
@@ -53,10 +54,11 @@ public function getDependencies() : array
5354
Middleware\ErrorResponseGenerator::class => Container\ErrorResponseGeneratorFactory::class,
5455
RequestHandlerRunner::class => Container\RequestHandlerRunnerFactory::class,
5556
ResponseInterface::class => Container\ResponseFactoryFactory::class,
56-
SERVER_REQUEST_ERROR_RESPONSE_GENERATOR => Container\ServerRequestErrorResponseGeneratorFactory::class,
57+
Response\ServerRequestErrorResponseGenerator::class => Container\ServerRequestErrorResponseGeneratorFactory::class,
5758
ServerRequestInterface::class => Container\ServerRequestFactoryFactory::class,
5859
StreamInterface::class => Container\StreamFactoryFactory::class,
5960
],
6061
];
62+
// @codingStandardsIgnoreEnd
6163
}
6264
}

src/Container/RequestHandlerRunnerFactory.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,26 @@
1212
use Psr\Container\ContainerInterface;
1313
use Psr\Http\Message\ServerRequestInterface;
1414
use Zend\Expressive\ApplicationPipeline;
15-
use Zend\Expressive\ServerRequestErrorResponseGenerator;
15+
use Zend\Expressive\Response\ServerRequestErrorResponseGenerator;
1616
use Zend\HttpHandlerRunner\Emitter\EmitterInterface;
1717
use Zend\HttpHandlerRunner\RequestHandlerRunner;
1818

1919
/**
2020
* Create an ApplicationRunner instance.
2121
*
22-
* This class consumes three pseudo-services (services that look like class
23-
* names, but resolve to other artifacts):
22+
* This class consumes two pseudo-services (services that look like class
23+
* names, but resolve to other artifacts) and two services provided within
24+
* this package:
2425
*
2526
* - Zend\Expressive\ApplicationPipeline, which should resolve to a
2627
* Zend\Stratigility\MiddlewarePipeInterface and/or
2728
* Psr\Http\Server\RequestHandlerInterface instance.
29+
* - Zend\HttpHandlerRunner\Emitter\EmitterInterface.
2830
* - Psr\Http\Message\ServerRequestInterface, which should resolve to a PHP
2931
* callable that will return a Psr\Http\Message\ServerRequestInterface
3032
* instance.
31-
* - Zend\Expressive\ServerRequestErrorResponseGenerator, which should resolve
32-
* to a PHP callable that accepts a Throwable argument, and which will return
33-
* a Psr\Http\Message\ResponseInterface instance.
33+
* - Zend\Expressive\Response\ServerRequestErrorResponseGeneratorFactory,
3434
*
35-
* It also consumes the service Zend\HttpHandlerRunner\Emitter\EmitterInterface.
3635
*/
3736
class RequestHandlerRunnerFactory
3837
{

src/Container/ServerRequestErrorResponseGeneratorFactory.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,29 @@
1111

1212
use Psr\Container\ContainerInterface;
1313
use Psr\Http\Message\ResponseInterface;
14-
use Throwable;
15-
use Zend\Diactoros\Response;
16-
use Zend\Diactoros\ServerRequest;
1714
use Zend\Expressive\Middleware\ErrorResponseGenerator;
15+
use Zend\Expressive\Response\ServerRequestErrorResponseGenerator;
16+
use Zend\Expressive\Template\TemplateRendererInterface;
1817

1918
class ServerRequestErrorResponseGeneratorFactory
2019
{
21-
public function __invoke(ContainerInterface $container) : callable
20+
public function __invoke(ContainerInterface $container) : ServerRequestErrorResponseGenerator
2221
{
23-
return function (Throwable $e) use ($container) : ResponseInterface {
24-
$generator = $container->get(ErrorResponseGenerator::class);
25-
return $generator($e, new ServerRequest(), new Response());
26-
};
22+
$config = $container->has('config') ? $container->get('config') : [];
23+
$debug = $config['debug'] ?? false;
24+
25+
$renderer = $container->has(TemplateRendererInterface::class)
26+
? $container->get(TemplateRendererInterface::class)
27+
: null;
28+
29+
$template = $config['zend-expressive']['error_handler']['template_error']
30+
?? ServerRequestErrorResponseGenerator::TEMPLATE_DEFAULT;
31+
32+
return new ServerRequestErrorResponseGenerator(
33+
$container->get(ResponseInterface::class),
34+
$debug,
35+
$renderer,
36+
$template
37+
);
2738
}
2839
}

src/Middleware/ErrorResponseGenerator.php

Lines changed: 10 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,15 @@
1212
use Psr\Http\Message\ResponseInterface;
1313
use Psr\Http\Message\ServerRequestInterface;
1414
use Throwable;
15+
use Zend\Expressive\Response\ErrorResponseGeneratorTrait;
1516
use Zend\Expressive\Template\TemplateRendererInterface;
1617
use Zend\Stratigility\Utils;
1718

1819
class ErrorResponseGenerator
1920
{
20-
public const TEMPLATE_DEFAULT = 'error::error';
21-
22-
/**
23-
* @var bool
24-
*/
25-
private $debug;
26-
27-
/**
28-
* @var TemplateRendererInterface
29-
*/
30-
private $renderer;
31-
32-
/**
33-
* @var string
34-
*/
35-
private $stackTraceTemplate = <<<'EOT'
36-
%s raised in file %s line %d:
37-
Message: %s
38-
Stack Trace:
39-
%s
21+
use ErrorResponseGeneratorTrait;
4022

41-
EOT;
42-
43-
/**
44-
* @var string
45-
*/
46-
private $template;
23+
public const TEMPLATE_DEFAULT = 'error::error';
4724

4825
public function __construct(
4926
bool $isDevelopmentMode = false,
@@ -63,66 +40,15 @@ public function __invoke(
6340
$response = $response->withStatus(Utils::getStatusCode($e, $response));
6441

6542
if ($this->renderer) {
66-
return $this->prepareTemplatedResponse($e, $request, $response);
43+
return $this->prepareTemplatedResponse($e, $response, [
44+
'response' => $response,
45+
'request' => $request,
46+
'uri' => (string) $request->getUri(),
47+
'status' => $response->getStatusCode(),
48+
'reason' => $response->getReasonPhrase(),
49+
]);
6750
}
6851

6952
return $this->prepareDefaultResponse($e, $response);
7053
}
71-
72-
private function prepareTemplatedResponse(
73-
Throwable $e,
74-
ServerRequestInterface $request,
75-
ResponseInterface $response
76-
) : ResponseInterface {
77-
$templateData = [
78-
'response' => $response,
79-
'request' => $request,
80-
'uri' => (string) $request->getUri(),
81-
'status' => $response->getStatusCode(),
82-
'reason' => $response->getReasonPhrase(),
83-
];
84-
85-
if ($this->debug) {
86-
$templateData['error'] = $e;
87-
}
88-
89-
$response->getBody()->write(
90-
$this->renderer->render($this->template, $templateData)
91-
);
92-
93-
return $response;
94-
}
95-
96-
private function prepareDefaultResponse(Throwable $e, ResponseInterface $response) : ResponseInterface
97-
{
98-
$message = 'An unexpected error occurred';
99-
100-
if ($this->debug) {
101-
$message .= "; strack trace:\n\n" . $this->prepareStackTrace($e);
102-
}
103-
104-
$response->getBody()->write($message);
105-
106-
return $response;
107-
}
108-
109-
/**
110-
* Prepares a stack trace to display.
111-
*/
112-
private function prepareStackTrace(Throwable $e) : string
113-
{
114-
$message = '';
115-
do {
116-
$message .= sprintf(
117-
$this->stackTraceTemplate,
118-
get_class($e),
119-
$e->getFile(),
120-
$e->getLine(),
121-
$e->getMessage(),
122-
$e->getTraceAsString()
123-
);
124-
} while ($e = $e->getPrevious());
125-
126-
return $message;
127-
}
12854
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Zend\Expressive\Response;
11+
12+
use Psr\Http\Message\ResponseInterface;
13+
use Throwable;
14+
use Zend\Expressive\Template\TemplateRendererInterface;
15+
16+
trait ErrorResponseGeneratorTrait
17+
{
18+
/**
19+
* Whether or not we are in debug/development mode.
20+
*
21+
* @var bool
22+
*/
23+
private $debug;
24+
25+
/**
26+
* @param TemplateRendererInterface
27+
*/
28+
private $renderer;
29+
30+
/**
31+
* @var string
32+
*/
33+
private $stackTraceTemplate = <<<'EOT'
34+
%s raised in file %s line %d:
35+
Message: %s
36+
Stack Trace:
37+
%s
38+
39+
EOT;
40+
41+
/**
42+
* Name of the template to render.
43+
*
44+
* @var string
45+
*/
46+
private $template;
47+
48+
private function prepareTemplatedResponse(
49+
Throwable $e,
50+
ResponseInterface $response,
51+
array $templateData
52+
) : ResponseInterface {
53+
if ($this->debug) {
54+
$templateData['error'] = $e;
55+
}
56+
57+
$response->getBody()
58+
->write($this->renderer->render($this->template, $templateData));
59+
60+
return $response;
61+
}
62+
63+
private function prepareDefaultResponse(Throwable $e, ResponseInterface $response) : ResponseInterface
64+
{
65+
$message = 'An unexpected error occurred';
66+
67+
if ($this->debug) {
68+
$message .= "; strack trace:\n\n" . $this->prepareStackTrace($e);
69+
}
70+
71+
$response->getBody()->write($message);
72+
73+
return $response;
74+
}
75+
76+
/**
77+
* Prepares a stack trace to display.
78+
*/
79+
private function prepareStackTrace(Throwable $e) : string
80+
{
81+
$message = '';
82+
do {
83+
$message .= sprintf(
84+
$this->stackTraceTemplate,
85+
get_class($e),
86+
$e->getFile(),
87+
$e->getLine(),
88+
$e->getMessage(),
89+
$e->getTraceAsString()
90+
);
91+
} while ($e = $e->getPrevious());
92+
93+
return $message;
94+
}
95+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Zend\Expressive\Response;
11+
12+
use Psr\Http\Message\ResponseInterface;
13+
use Throwable;
14+
use Zend\Expressive\Template\TemplateRendererInterface;
15+
use Zend\Stratigility\Utils;
16+
17+
/**
18+
* Generates a response for use when the server request factory fails.
19+
*/
20+
class ServerRequestErrorResponseGenerator
21+
{
22+
use ErrorResponseGeneratorTrait;
23+
24+
public const TEMPLATE_DEFAULT = 'error::error';
25+
26+
/**
27+
* Factory capable of generating a ResponseInterface instance.
28+
*
29+
* @param callable
30+
*/
31+
private $responseFactory;
32+
33+
public function __construct(
34+
callable $responseFactory,
35+
bool $isDevelopmentMode = false,
36+
TemplateRendererInterface $renderer = null,
37+
string $template = self::TEMPLATE_DEFAULT
38+
) {
39+
$this->responseFactory = function () use ($responseFactory) : ResponseInterface {
40+
return $responseFactory();
41+
};
42+
43+
$this->debug = $isDevelopmentMode;
44+
$this->renderer = $renderer;
45+
$this->template = $template;
46+
}
47+
48+
public function __invoke(Throwable $e)
49+
{
50+
$response = ($this->responseFactory)()
51+
->withStatus(Utils::getStatusCode($e, $response));
52+
53+
if ($this->renderer) {
54+
return $this->prepareTemplatedResponse($e, $response, [
55+
'response' => $response,
56+
'status' => $response->getStatusCode(),
57+
'reason' => $response->getReasonPhrase(),
58+
]);
59+
}
60+
61+
return $this->prepareDefaultResponse($e, $response);
62+
}
63+
}

src/constants.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,6 @@
6969
*/
7070
const ROUTE_MIDDLEWARE = __NAMESPACE__ . '\Middleware\RouteMiddleware';
7171

72-
/**
73-
* Virtual service name that should resolve to a service returning a response
74-
* based on a `Throwable` argument produced when generating the application
75-
* request.
76-
*
77-
* @var string
78-
*/
79-
const SERVER_REQUEST_ERROR_RESPONSE_GENERATOR = __NAMESPACE__ . '\ServerRequestErrorResponseGenerator';
80-
8172
/**
8273
* Legacy/transitional service name for the ServerRequestFactory virtual
8374
* service introduced in 3.0.0alpha6. Should resolve to the

0 commit comments

Comments
 (0)