mirror of
https://github.com/guzzle/guzzle.git
synced 2025-02-24 01:53:58 +01:00
- Moving various namespaces up a level - Updating docs - Renaming EventSubscriberInterface to SubscriberInterface
377 lines
13 KiB
ReStructuredText
377 lines
13 KiB
ReStructuredText
============
|
|
Event System
|
|
============
|
|
|
|
Guzzle uses an event emitter to allow you to easily extend the behavior of a
|
|
request, change the response associated with a request, and implement custom
|
|
error handling. All events in Guzzle are managed and emitted by an
|
|
**event emitter**.
|
|
|
|
Event Emitters
|
|
==============
|
|
|
|
Clients, requests, and any other class that implements the
|
|
``GuzzleHttp\Common\HasEmitterInterface`` interface have a
|
|
``GuzzleHttp\Common\EventEmitter`` object. You can add event *listeners* and
|
|
event *subscribers* to an event *emitter*.
|
|
|
|
emitter
|
|
An object that implements ``GuzzleHttp\Common\EventEmitterInterface``. This
|
|
object emits named events to event listeners. You may register event
|
|
listeners on subscribers on an emitter.
|
|
|
|
event listeners
|
|
Callable functions that are registered on an event emitter for specific
|
|
events. Event listeners are registered on an emitter with a *priority*
|
|
setting. If no priority is provided, ``0`` is used by default.
|
|
|
|
event subscribers
|
|
Classes that tell an event emitter what methods to listen to and what
|
|
functions on the class to invoke when the event is triggered. Event
|
|
subscribers subscribe event listeners to an event emitter. They should be
|
|
used when creating more complex event based logic in applications (i.e.,
|
|
cookie handling is implemented using an event subscriber because it's
|
|
easier to share a subscriber than an anonymous function and because
|
|
handling cookies is a complex process).
|
|
|
|
priority
|
|
Describes the order in which event listeners are invoked when an event is
|
|
emitted. The higher a priority value, the earlier the event listener will
|
|
be invoked (a higher priority means the listener is more important). If
|
|
no priority is provided, the priority is assumed to be ``0``.
|
|
|
|
propagation
|
|
Describes whether or not other event listeners are triggered. Event
|
|
emitters will trigger every event listener registered to a specific event
|
|
in priority order until all of the listeners have been triggered **or**
|
|
until the propagation of an event is stopped.
|
|
|
|
Getting an EventEmitter
|
|
-----------------------
|
|
|
|
You can get the event emitter of ``GuzzleHttp\Common\HasEmitterInterface``
|
|
object using the the ``getEmitter()`` method. Here's an example of getting a
|
|
client object's event emitter.
|
|
|
|
.. code-block:: php
|
|
|
|
$client = new GuzzleHttp\Client();
|
|
$emitter = $client->getEmitter();
|
|
|
|
.. note::
|
|
|
|
You'll notice that the event emitter used in Guzzle is very similar to the
|
|
`Symfony2 EventDispatcher component <https://github.com/symfony/symfony/tree/master/src/Symfony/Component/EventDispatcher>`_.
|
|
This is because the Guzzle event system is based on the Symfony2 event
|
|
system with several changes. Guzzle uses its own event emitter to improve
|
|
performance, isolate Guzzle from changes to the Symfony, and provide a few
|
|
improvements that make it easier to use for an HTTP client (e.g., the
|
|
addition of the ``once()`` method).
|
|
|
|
Adding Event Listeners
|
|
----------------------
|
|
|
|
After you have the emitter, you can register event listeners that listen to
|
|
specific events using the ``on()`` method. When registering an event listener,
|
|
you must tell the emitter what event to listen to (e.g., "before", "after",
|
|
"headers", "complete", "error", etc...), what callable to invoke when the
|
|
event is triggered, and optionally provide a priority.
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Event\BeforeEvent;
|
|
|
|
$emitter->on('before', function (BeforeEvent $event) {
|
|
echo $event->getRequest();
|
|
});
|
|
|
|
When a listener is triggered, it is passed an event that implements the
|
|
``GuzzleHttp\Common\EventInterface`` interface, the name of the event, and the
|
|
event emitter itself. The above example could more verbosely be written as
|
|
follows:
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Event\BeforeEvent;
|
|
|
|
$emitter->on('before', function (
|
|
BeforeEvent $event,
|
|
$name,
|
|
EmitterInterface $emitter
|
|
) {
|
|
echo $event->getRequest();
|
|
});
|
|
|
|
You can add an event listener that automatically removes itself after it is
|
|
triggered using the ``once()`` method of an event emitter.
|
|
|
|
.. code-block:: php
|
|
|
|
$client = new GuzzleHttp\Client();
|
|
$client->getEmitter()->once('before', function () {
|
|
echo 'This will only happen once... per request!';
|
|
});
|
|
|
|
Event Propagation
|
|
-----------------
|
|
|
|
Event listeners can prevent other event listeners from being triggered by
|
|
stopping an event's propagation.
|
|
|
|
Stopping event propagation can be useful, for example, if an event listener has
|
|
changed the state of the subject to such an extent that allowing subsequent
|
|
event listeners to be triggered could place the subject in an inconsistent
|
|
state. This technique is used in Guzzle extensively when intercepting error
|
|
events with responses.
|
|
|
|
You can stop the propagation of an event using the ``stopPropagation()`` method
|
|
of a ``GuzzleHttp\Common\EventInterface`` object:
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Event\ErrorEvent;
|
|
|
|
$emitter->on('error', function (ErrorEvent $event) {
|
|
$event->stopPropagation();
|
|
});
|
|
|
|
After stopping the propagation of an event, any subsequent event listeners that
|
|
have not yet been trigger will not be triggered. You can check to see if the
|
|
propagation of an event was stopped using the ``isPropagationStopped()`` method
|
|
of the event.
|
|
|
|
.. code-block:: php
|
|
|
|
$client = new GuzzleHttp\Client();
|
|
$emitter = $client->getEmitter();
|
|
// Note: assume that the $errorEvent was created
|
|
if ($emitter->emit('error', $errorEvent)->isPropagationStopped()) {
|
|
echo 'It was stopped!';
|
|
}
|
|
|
|
.. hint::
|
|
|
|
When emitting events, the event that was emitted is returned from the
|
|
emitter. This allows you to easily chain calls as shown in the above
|
|
example.
|
|
|
|
Event Subscribers
|
|
-----------------
|
|
|
|
Event subscribers are classes that implement the
|
|
``GuzzleHttp\Common\EventSubsriberInterface`` object. They are used to register
|
|
one or more event listeners to methods of the class. Event subscribers tell
|
|
event emitters exactly which events to listen to and what method to invoke on
|
|
the class when the event is triggered using the static method,
|
|
``getSubscribedEvents()``.
|
|
|
|
The following example registers event listeners to the ``before`` and
|
|
``complete`` event of a request. When the ``before`` event is emitted, the
|
|
``onBefore`` instance method of the subscriber is invoked. When the
|
|
``complete`` event is emitted, the ``onComplete`` event of the subscriber is
|
|
invoked. Each array value in the ``getSubscribedEvents()`` return value MUST
|
|
contain the name of the method to invoke and can optionally contain the
|
|
priority of the listener (as shown in the ``before`` listener in the example).
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Event\EventEmitterInterface;
|
|
use GuzzleHttp\Event\SubscriberInterface;
|
|
use GuzzleHttp\Event\BeforeEvent;
|
|
use GuzzleHttp\Event\CompleteEvent;
|
|
|
|
class SimpleSubscriber implements SubscriberInterface
|
|
{
|
|
public static function getSubscribedEvents()
|
|
{
|
|
return [
|
|
'before' => ['onBefore', 100], // Provide name and optional priority
|
|
'complete' => ['onComplete']
|
|
]
|
|
}
|
|
|
|
public function onBefore(BeforeEvent $event, $name, EmitterInterface $emitter)
|
|
{
|
|
echo 'Before!';
|
|
}
|
|
|
|
public function onComplete(CompleteEvent $event, $name, EmitterInterface $emitter)
|
|
{
|
|
echo 'Complete!';
|
|
}
|
|
}
|
|
|
|
Working With Request Events
|
|
===========================
|
|
|
|
Requests emit lifecycle events when they are transferred.
|
|
|
|
.. important::
|
|
|
|
Request lifecycle events may be triggered multiple times due to redirects,
|
|
retries, or reusing a request multiple times. Use the ``once()`` method
|
|
of an event emitter if you only want the event to be triggered once. You
|
|
can also remove an event listener from an emitter by using the emitter the
|
|
is provided to the listener.
|
|
|
|
before
|
|
------
|
|
|
|
The ``before`` event is emitted before a request is sent. The event emitted is
|
|
a ``GuzzleHttp\Event\BeforeEvent``.
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Common\EmitterInterface;
|
|
use GuzzleHttp\Event\BeforeEvent;
|
|
|
|
$client = new Client(['base_url' => 'http://httpbin.org']);
|
|
$request = $client->createRequest('GET', '/');
|
|
$request->getEmitter()->on(
|
|
'before',
|
|
function (BeforeEvent $e, $name, EmitterInterface $emitter) {
|
|
echo $name . "\n";
|
|
// "before"
|
|
echo $e->getRequest()->getMethod() . "\n";
|
|
// "GET" / "POST" / "PUT" / etc...
|
|
echo get_class($e->getClient());
|
|
// "GuzzleHttp\Client"
|
|
}
|
|
);
|
|
|
|
You can intercept a request with a response before the request is sent over the
|
|
wire. The ``intercept()`` method of the ``BeforeEvent`` accepts a
|
|
``GuzzleHttp\Message\ResponseInterface``. Intercepting the event will prevent
|
|
the request from being sent over the wire and stops the propagation of the
|
|
``before`` event, preventing subsequent event listeners from being invoked.
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Event\BeforeEvent;
|
|
use GuzzleHttp\Message\Response;
|
|
|
|
$client = new Client(['base_url' => 'http://httpbin.org']);
|
|
$request = $client->createRequest('GET', '/status/500');
|
|
$request->getEmitter()->on('before', function (BeforeEvent $e) {
|
|
$response = new Response(200);
|
|
$e->intercept($response);
|
|
});
|
|
|
|
$response = $client->send($request);
|
|
echo $response->getStatusCode();
|
|
// 200
|
|
|
|
.. attention::
|
|
|
|
Any exception encountered while executing the ``before`` event will trigger
|
|
the ``error`` event of a request.
|
|
|
|
headers
|
|
-------
|
|
|
|
The ``headers`` event is emitted after the headers of a response have been
|
|
received before any of the response body has been downloaded. The event
|
|
emitted is a ``GuzzleHttp\Event\HeadersEvent``.
|
|
|
|
This event can be useful if you need to conditionally wrap the response body
|
|
of a request in a special decorator or if you only want to conditionally
|
|
download a response body based on response headers.
|
|
|
|
This event cannot be intercepted.
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Event\HeadersEvent;
|
|
|
|
$client = new Client(['base_url' => 'http://httpbin.org']);
|
|
$request = $client->createRequest('GET', '/stream/100');
|
|
$request->getEmitter()->on('headers', function (HeadersEvent $e) {
|
|
echo $e->getResponse();
|
|
// Prints the response headers
|
|
|
|
// Wrap the response body in a custom decorator if the response has a body
|
|
if ($e->getResponse()->getHeader('Content-Length') ||
|
|
$e->getResponse()->getHeader('Content-Encoding')
|
|
) {
|
|
$customBody = new MyCustomStreamDecorator($e->getResponse()->getBody());
|
|
$e->getResponse()->setBody($customBody);
|
|
}
|
|
});
|
|
|
|
complete
|
|
--------
|
|
|
|
The ``complete`` event is emitted after a transaction completes and an entire
|
|
response has been received. The event is a ``GuzzleHttp\Event\CompleteEvent``.
|
|
|
|
You can intercept the ``complete`` event with a different response if needed
|
|
using the ``intercept()`` method of the event. This can be useful, for example,
|
|
for changing the response for caching.
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Event\CompleteEvent;
|
|
use GuzzleHttp\Message\Response;
|
|
|
|
$client = new Client(['base_url' => 'http://httpbin.org']);
|
|
$request = $client->createRequest('GET', '/status/302');
|
|
$cachedResponse = new Response(200);
|
|
|
|
$request->getEmitter()->on(
|
|
'complete',
|
|
function (CompleteEvent $e) use ($cachedResponse) {
|
|
if ($e->getResponse()->getStatusCode() == 302) {
|
|
// Intercept the original transaction with the new response
|
|
$e->intercept($cachedResponse);
|
|
}
|
|
}
|
|
);
|
|
|
|
$response = $client->send($request);
|
|
echo $response->getStatusCode();
|
|
// 200
|
|
|
|
.. attention::
|
|
|
|
Any ``GuzzleHttp\Exception\RequestException`` encountered while executing
|
|
the ``complete`` event will trigger the ``error`` event of a request.
|
|
|
|
error
|
|
-----
|
|
|
|
The ``error`` event is emitted when a request fails (whether it's from a
|
|
networking error or an HTTP protocol error). The event emitted is a
|
|
``GuzzleHttp\Event\ErrorEvent``.
|
|
|
|
This event is useful for retrying failed requests. Here's an example of
|
|
retrying failed basic auth requests by re-sending the original request with
|
|
a username and password.
|
|
|
|
.. code-block:: php
|
|
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Event\ErrorEvent;
|
|
|
|
$client = new Client(['base_url' => 'http://httpbin.org']);
|
|
$request = $client->createRequest('GET', '/basic-auth/foo/bar');
|
|
$request->getEmitter()->on('error', function (ErrorEvent $e) {
|
|
if ($e->getResponse()->getStatusCode() == 401) {
|
|
// Add authentication stuff as needed and retry the request
|
|
$e->getRequest()->setHeader('Authorization', 'Basic ' . base64_encode('foo:bar'));
|
|
// Get the client of the event and retry the request
|
|
$newResponse = $e->getClient()->send($e->getRequest());
|
|
// Intercept the original transaction with the new response
|
|
$e->intercept($newResponse);
|
|
}
|
|
});
|
|
|
|
.. attention::
|
|
|
|
If an ``error`` event is intercepted with a response, then the ``complete``
|
|
event of a request is triggered. If the ``complete`` event fails, then the
|
|
``error`` event is triggered once again.
|