From 25a3a5046bec34e25b8b00b6771df6c7d05da959 Mon Sep 17 00:00:00 2001 From: Michael Dowling Date: Sat, 15 Feb 2014 14:15:27 -0800 Subject: [PATCH] Adding more event docs, and passing the emitter to listeners when they are triggered. --- docs/clients.rst | 4 +- docs/events.rst | 215 +++++++++++++++++++++---- src/Guzzle/Common/Emitter.php | 2 +- src/Guzzle/Common/EmitterInterface.php | 3 + 4 files changed, 188 insertions(+), 36 deletions(-) diff --git a/docs/clients.rst b/docs/clients.rst index 2cafacd9..2e12ece5 100644 --- a/docs/clients.rst +++ b/docs/clients.rst @@ -119,7 +119,9 @@ errors as well: ``GuzzleHttp\Exception\ClientErrorResponseException`` for $client->get('http://httpbin.org'); } catch (RequestException $e) { echo $e->getRequest() . "\n"; - echo $e->getResponse() . "\n"; + if ($e->hasResponse()) { + echo $e->getResponse() . "\n"; + } } A ``GuzzleHttp\Exception\RequestException`` always contains a diff --git a/docs/events.rst b/docs/events.rst index 3e20d49a..6edac168 100644 --- a/docs/events.rst +++ b/docs/events.rst @@ -4,39 +4,186 @@ Event System Requests emit lifecycle events when they are transferred. -- **before**: Emitted before a request is sent. The event is a - ``GuzzleHttp\Event\BeforeEvent``. -- **headers**: Emitted after the headers of a response have been received. This - event is emitted before any of the response body has been downloaded. The - event is a ``GuzzleHttp\Event\HeadersEvent``. -- **complete**: Emitted after an event completes. The event is a - ``GuzzleHttp\Event\CompleteEvent``. -- **error**: 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``. - -A client object has a ``GuzzleHttp\Common\EventEmitter`` object that can be -used to add event *listeners* and event *subscribers* to all requests created -by the client. - -- **event listeners** are callable functions that are registered on an event - emitter for specific events. -- **event subscribers** are 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 register event listeners with an event emitter. - They should be used when created more complex event based logic to - 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). - -before Event -============ - -headers Event -============= - -complete Event +Request Events ============== -error event -=========== +before + Event emitted before a request is sent. The event emitter is a + ``GuzzleHttp\Event\BeforeEvent``. + +headers + event 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``. + +complete + Event emitted after a transaction completes and an entire response has been + received. The event is a ``GuzzleHttp\Event\CompleteEvent``. + +error + Event 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``. + +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 `_. + 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. + +Working With Request Events +=========================== + +before +------ + +headers +------- + +complete +-------- + +error +----- diff --git a/src/Guzzle/Common/Emitter.php b/src/Guzzle/Common/Emitter.php index cdc96630..f7dcacad 100644 --- a/src/Guzzle/Common/Emitter.php +++ b/src/Guzzle/Common/Emitter.php @@ -34,7 +34,7 @@ class Emitter implements EmitterInterface { $onceListener = function (Event $event, $eventName) use (&$onceListener, $eventName, $listener, $priority) { $this->removeListener($eventName, $onceListener); - $listener($event, $eventName); + $listener($event, $eventName, $this); }; $this->on($eventName, $onceListener, $priority); diff --git a/src/Guzzle/Common/EmitterInterface.php b/src/Guzzle/Common/EmitterInterface.php index 76fe05ce..50599389 100644 --- a/src/Guzzle/Common/EmitterInterface.php +++ b/src/Guzzle/Common/EmitterInterface.php @@ -47,6 +47,9 @@ interface EmitterInterface /** * Emits an event to all registered listeners. * + * Each event that is bound to the emitted eventName receives a + * EventInterface, the name of the event, and the event emitter. + * * @param string $eventName The name of the event to dispatch. * @param EventInterface $event The event to pass to the event handlers/listeners. *