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

Commit 22dbdfc

Browse files
committed
Documented listening for errors
- Added a section in the error handling documentation for "listening for errors". - The documentation kind of requires knowledge of delegator factories, so added a chapter on delegator factories as well.
1 parent 8fa5b8d commit 22dbdfc

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Delegator Factories
2+
3+
- Since 2.0.
4+
5+
Starting with the 2.0 version of the Expressive skeleton, we now support the
6+
concept of _delegator factories_, which allow decoration of services created by
7+
your dependency injection container, across all dependency injection containers
8+
supported by Expressive.
9+
10+
_Delegator factories_ accept the following arguments:
11+
12+
- The container itself;
13+
- The name of the service whose creation is being decrorated;
14+
- A callback that will produce the service being decorated.
15+
16+
As an example, let's say we have a `UserRepository` class that composes some sort of
17+
event manager. We might want to attach listeners to that event manager, but not
18+
wish to alter the basic creation logic for the repository itself. As such, we
19+
might write a _delegator factory_ as follows:
20+
21+
```php
22+
namespace Acme;
23+
24+
use Psr\Container\ContainerInterface;
25+
use Psr\Log\LoggerInterface;
26+
27+
class UserRepositoryListenerDelegatorFactory
28+
{
29+
/**
30+
* @param ContainerInterface $container
31+
* @param string $name
32+
* @param callable $callback
33+
* @return UserRepository
34+
*/
35+
public function __invoke(ContainerInterface $container, $name, callable $callback)
36+
{
37+
$listener = new LoggerListener($container->get(LoggerInterface::class));
38+
$repository = $callback();
39+
$repository->getEventManager()->attach($listener);
40+
return $repository;
41+
}
42+
}
43+
```
44+
45+
To notify the container about this delegator factory, we would add the following
46+
configuration to our application:
47+
48+
```php
49+
'dependencies' => [
50+
'delegators' => [
51+
Acme\UserRepository::class => [
52+
Acme\UserRepositoryListenerDelegatorFactory::class,
53+
],
54+
],
55+
],
56+
```
57+
58+
Note that you specify delegator factories using the service name being decorated
59+
as the key, with an _array_ of delegator factories as a value. **You may attach
60+
multiple delegator factories to any given service**, which can be a very
61+
powerful feature.
62+
63+
At the time of writing, this feature works for each of the Aura.Di, Pimple, and
64+
zend-servicemanager container implementations. Delegator factories have been
65+
supported with Pimple and zend-servicemanager since the 1.X series.

doc/book/features/error-handling.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,93 @@ return [
137137
> `config/autoload/*.local.php` file with the above configuration whenever you
138138
> want to enable whoops.
139139
140+
## Listening for errors
141+
142+
When errors occur, you may want to _listen_ for them in order to provide
143+
features such as logging. `Zend\Stratigility\Middleware\ErrorHandler` provides
144+
the ability to do so via its `attachListener()` method.
145+
146+
This method accepts a callable with the following signature:
147+
148+
```php
149+
function (
150+
Throwable|Exception $error,
151+
ServerRequestInterface $request,
152+
ResponseInterface $response
153+
) : void
154+
```
155+
156+
The response provided is the response returned by your error response generator,
157+
allowing the listener the ability to introspect the generated response as well.
158+
159+
As an example, you could create a logging listener as follows:
160+
161+
```php
162+
namespace Acme;
163+
164+
use Exception;
165+
use Psr\Log\LoggerInterface;
166+
use Psr\Http\Message\ResponseInterface;
167+
use Psr\Http\Message\ServerRequestInterface;
168+
use Throwable;
169+
170+
class LoggingErrorListener
171+
{
172+
/**
173+
* Log format for messages:
174+
*
175+
* STATUS [METHOD] path: message
176+
*/
177+
const LOG_FORMAT = '%d [%s] %s: %s';
178+
179+
private $logger;
180+
181+
public function __construct(LoggerInterface $logger)
182+
{
183+
$this->logger = $logger;
184+
}
185+
186+
public function __invoke($error, ServerRequestInterface $request, ResponseInterface $response)
187+
{
188+
$this->logger->error(sprintf(
189+
self::LOG_FORMAT,
190+
$response->getStatusCode(),
191+
$request->getMethod(),
192+
(string) $request->getUri(),
193+
$error->getMessage()
194+
));
195+
}
196+
}
197+
```
198+
199+
You could then use a [delegator factory](container/delegator-factories.md) to
200+
create your logger listener and attach it to your error handler:
201+
202+
```php
203+
namespace Acme;
204+
205+
use Psr\Container\ContainerInterface;
206+
use Psr\Log\LoggerInterface;
207+
use Zend\Stratigility\Middleware\ErrorHandler;
208+
209+
class LoggerErrorListenerDelegatorFactory
210+
{
211+
/**
212+
* @param ContainerInterface $container
213+
* @param string $name
214+
* @param callable $callback
215+
* @return ErrorHandler
216+
*/
217+
public function __invoke(ContainerInterface $container, $name, callable $callback)
218+
{
219+
$listener = new LoggerErrorListener($container->get(LoggerInterface::class));
220+
$errorHandler = $callback();
221+
$errorHandler->attachListener($listener);
222+
return $errorHandler;
223+
}
224+
}
225+
```
226+
140227
## Handling more specific error types
141228

142229
You could also write more specific error handlers. As an example, you might want

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pages:
1212
- Containers:
1313
- Introduction: features/container/intro.md
1414
- 'Container Factories': features/container/factories.md
15+
- 'Delegator Factories': features/container/delegator-factories.md
1516
- 'Using zend-servicemanager': features/container/zend-servicemanager.md
1617
- 'Using Pimple': features/container/pimple.md
1718
- 'Using Aura.Di': features/container/aura-di.md

0 commit comments

Comments
 (0)