If you use the Ivory\HttpAdapter\EventDispatcherHttpAdapter
, then, when a request is sent, some events are triggered
through the Symfony2 event dispatcher
and so, you can hook into them pretty easily.
All available events are described by the constants wrapped in the Ivory\HttpAdapter\Event\Events
class. There are
two types of events:
- Serial events
- Parallel events
The Ivory\HttpAdapter\Event\Events::REQUEST_CREATED
describes the event trigger just before a request is sent serially. It
is represented by the Ivory\HttpAdapter\Event\RequestCreatedEvent
and wraps the http adapter and the internal request.
To get/set them, you can use:
use Ivory\HttpAdapter\Event\RequestCreatedEvent;
$requestCreatedEvent = new RequestCreatedEvent($httpAdapter, $request);
$httpAdapter = $requestCreatedEvent->getHttpAdapter();
$request = $requestCreatedEvent->getRequest();
$requestCreatedEvent->setRequest($request);
The Ivory\HttpAdapter\Event\Events::REQUEST_SENT
describes the event trigger just after the request is sent serially. It
is described by the Ivory\HttpAdapter\Event\RequestSentEvent
and wraps the http adapter, the internal request and the
response. To get/set them, you can use:
use Ivory\HttpAdapter\Event\RequestSentEvent;
$requestSentEvent = new RequestSentEvent($httpAdapter, $request, $response);
$httpAdapter = $requestSentEvent->getHttpAdapter();
$request = $requestSentEvent->getRequest();
$requestSentEvent->setRequest($request);
$response = $requestSentEvent->getResponse();
$requestSentEvent->setResponse($response);
The Ivory\HttpAdapter\Event\Events::REQUEST_ERRORED
describes the event trigger if an error occurred in a serial request
context. It is represented by the Ivory\HttpAdapter\Event\RequestErroredEvent
and wraps the http adapter and the
exception. To get/set them, you can use:
use Ivory\HttpAdapter\Event\RequestErroredEvent;
$requestErroredEvent = new RequestErroredEvent($httpAdapter, $request, $exception);
$httpAdapter = $requestErroredEvent->getHttpAdapter();
$exception = $requestErroredEvent->getException();
$requestErroredEvent->setException($exception);
$request = $exception->getRequest();
$response = $exception->getResponse();
Additionally, this event allows you to manage a response. That means if you want to by-pass the exception, just set the response on the event and it will be returned by the http adapter instead of throwing the exception. The following API is available:
$hasResponse = $requestErroredEvent->hasResponse();
$response = $requestErroredEvent->getResponse();
$requestErroredEvent->setResponse($response);
The Ivory\HttpAdapter\Event\Events::MULTI_REQUEST_CREATED
describes the event trigger just before multiple requests are sent
in parallel. It is represented by the Ivory\HttpAdapter\Event\MultiRequestCreatedEvent
and wraps the http adapter and
the internal requests. To get/set them, you can use:
use Ivory\HttpAdapter\Event\MultiRequestCreatedEvent;
$multiRequestCreatedEvent = new MultiRequestCreatedEvent($httpAdapter, $requests);
$httpAdapter = $multiRequestCreatedEvent->getHttpAdapter();
$requests = $multiRequestCreatedEvent->getRequests();
$multiRequestCreatedEvent->setRequests($requests);
The Ivory\HttpAdapter\Event\Events::MULTI_REQUEST_SENT
describes the event trigger just after multiple requests are sent
fully successfully in parallel. To be even more precise, if only one request is errored, this event is not
triggered. It is described by the Ivory\HttpAdapter\Event\MultiRequestSentEvent
and wraps the http adapter and the
responses. To get/set them, you can use:
use Ivory\HttpAdapter\Event\MultiRequestSentEvent;
$multiRequestSentEvent = new MultiRequestSentEvent($httpAdapter, $responses);
$httpAdapter = $multiRequestSentEvent->getHttpAdapter();
$responses = $multiRequestSentEvent->getResponses();
$multiRequestSentEvent->setResponses($responses);
All responses wrap the linked request as parameter:
foreach ($responses as $response) {
$request = $response->getParameter('request');
}
The Ivory\HttpAdapter\Event\Events::MULTI_REQUEST_ERRORED
describes the event trigger if an error occurred in a parallel
requests context. It is represented by the Ivory\HttpAdapter\Event\MultiRequestErroredEvent
and wraps the http adapter
exceptions. To get/set them, you can use:
use Ivory\HttpAdapter\Event\MultiRequestErroredEvent;
$multiRequestErroredEvent = new MultiRequestErroredEvent($httpAdapter, $multiException);
$httpAdapter = $multiRequestErroredEvent->getHttpAdapter();
$exceptions = $multiRequestErroredEvent->getExceptions();
$multiRequestErroredEvent->setExceptions($exceptions);
The exception is an instance of Ivory\HttpAdapter\HttpAdapterException
which wraps the request and the responses if
available. To get/set them, you can use:
foreach ($mutliRequestErroredEvent->getExceptions() as $exception) {
$request = $exception->getRequest();
if ($exception->hasResponses()) {
$responses = $exception->getResponse();
}
}
The library provides some useful built-in subscribers you can directly use. Obviously, you can define your own and propose to add them in the core. The built-in ones are:
Some other event subscribers are available as dedicated packages:
The basic authentication subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\BasicAuthSubscriber
and
allows you to do an HTTP basic authentication. To use it:
use Ivory\HttpAdapter\Event\BasicAuth\BasicAuth;
use Ivory\HttpAdapter\Event\Subscriber\BasicAuthSubscriber;
$basicAuthSubscriber = new BasicAuthSubscriber(new BasicAuth('username', 'password'));
Internally, the basic auth subscriber uses the Ivory\HttpAdapter\Event\BasicAuth\BasicAuth
in order to do the
authentication. Since the username and passord is mandatory, the basic auth is too. It takes respectively as first and
second argument the username and the password. The basic authentication accepts a third argument known as matcher. A
matcher is responsible to check if the request should be authenticated according to your rules. It can be either:
null
: all requests are authenticated (default).string
: only requests with the url matching the string (regex pattern) are authenticated.callable
: only requests matching your callable are authenticated (the callable receives the event request as argument and should return true/false).
Finally, all constructor arguments can be updated at runtime:
$username = $basicAuth->getUsername();
$basicAuth->setUsername($username);
$password = $basicAuth->getPassword();
$basicAuth->setPassword($password);
$hasMatcher = $basicAuth->hasMatcher();
$matcher = $basicAuth->getMatcher();
$basicAuth->setMatcher($matcher);
The cache subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\CacheSubscriber
and allow you to cache
responses/exceptions. We are not talking about http cache mechanism but a cache layer which pick response/exceptions
according to a request and save them the first time requests are sent. To use it:
use Doctrine\Common\Cache\FilesystemCache;
use Ivory\HttpAdapter\Event\Cache\Adapter\DoctrineCacheAdapter;
use Ivory\HttpAdapter\Event\Cache\Cache;
use Ivory\HttpAdapter\Event\Subscriber\CacheSubscriber;
$filesystemCache = new FilesystemCache(__DIR__.'/cache');
$doctrineCache = new DoctrineCacheAdapter($filesystemCache);
$cache = new Cache($doctrineCache);
$cacheSubscriber = new CacheSubscriber($cache);
At a first read, it seems complex but it is pretty easy and allows you to use any kind of caching system. Basically,
the subscriber needs a cache system represented by the Ivory\HttpAdapter\Event\Cache\CacheInterface
in order to
get/save responses/exceptions. The default implementation is the Ivory\HttpAdapter\Event\Cache\Cache
. The cache
system needs a cache adapter defined by the Ivory\HttpAdapter\Event\Cache\Adapter\CacheAdapterInterface
which is
responsible to get/save scalar data. Currently, there are the
Ivory\HttpAdapter\Event\Cache\Adapter\DoctrineCacheAdapter
and the
Ivory\HttpAdapter\Event\Cache\Adapter\StashCacheAdapter
. Then, the cache adapters requires the underlying cache
(doctrine, stash, ...).
The cache system serializes/unserializes responses/exceptions through the formatter represented by the
Ivory\HttpAdapter\Event\Formatter\FormatterInterface
. Its default implementation is the
Ivory\HttpAdapter\Event\Formatter\Formatter
and can be passed as second constructor argument:
use Doctrine\Common\Cache\ArrayCache;
use Ivory\HttpAdapter\Event\Cache\Adapter\DoctrineCacheAdapter;
use Ivory\HttpAdapter\Event\Cache\Cache;
use Ivory\HttpAdapter\Event\Formatter\Formatter;
use Ivory\HttpAdapter\Event\Subscriber\CacheSubscriber;
$arrayCache = new ArrayCache();
$doctrineCache = new DoctrineCacheAdapter($arrayCache);
$formatter = new Formatter();
$cache = new Cache($doctrineCache, $formatter);
$cacheSubscriber = new CacheSubscriber($cache);
You can specify the lifetime of the cache by passing it as third argument:
use Doctrine\Common\Cache\ArrayCache;
use Ivory\HttpAdapter\Event\Cache\Adapter\DoctrineCacheAdapter;
use Ivory\HttpAdapter\Event\Cache\Cache;
use Ivory\HttpAdapter\Event\Subscriber\CacheSubscriber;
$arrayCache = new ArrayCache();
$doctrineCache = new DoctrineCacheAdapter($arrayCache);
$cache = new Cache($doctrineCache, null, 100);
$cacheSubscriber = new CacheSubscriber($cache);
or at runtime:
$cache->setLifetime(100);
You can also disable exceptions caching only (keeping responses caching) by passing false as fourth argument:
use Doctrine\Common\Cache\ArrayCache;
use Ivory\HttpAdapter\Event\Cache\Adapter\DoctrineCacheAdapter;
use Ivory\HttpAdapter\Event\Cache\Cache;
use Ivory\HttpAdapter\Event\Subscriber\CacheSubscriber;
$arrayCache = new ArrayCache();
$doctrineCache = new DoctrineCacheAdapter($arrayCache);
$cache = new Cache($doctrineCache, null, null, false);
$cacheSubscriber = new CacheSubscriber($cache);
or at runtime:
$cache->cacheException(false);
If you prefer using stash, an other example:
use Ivory\HttpAdapter\Event\Cache\Adapter\StashCacheAdapter;
use Ivory\HttpAdapter\Event\Cache\Cache;
use Ivory\HttpAdapter\Event\Subscriber\CacheSubscriber;
use Stash\Pool
$pool = new Stash\Pool();
$stashCache = new StashCacheAdapter($pool);
$cache = new Cache($stashCache);
$cacheSubscriber = new CacheSubscriber($cache);
The cookie subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\CookieSubscriber
and allow you to manage
cookies through a jar. To use it:
use Ivory\HttpAdapter\Event\Subscriber\CookieSubscriber;
$cookieSubscriber = new CookieSubscriber();
By default, a cookie jar is created by the subscriber but you can specify it in its constructor:
use Ivory\HttpAdapter\Event\Cookie\Jar\CookieJar;
use Ivory\HttpAdapter\Event\Subscriber\CookieSubscriber;
$cookieJar = new CookieJar();
$cookieSubscriber = new CookieSubscriber($cookieJar);
Finally, the cookie jar can be access or change at runtime with:
$cookieJar = $cookieSubscriber->getCookieJar();
$cookieSubscriber->setCookieJar($cookieJar);
A cookie jar is described by the Ivory\HttpAdapter\Event\Cookie\Jar\CookieJarInterface
and its default implementation
is Ivory\HttpAdapter\Event\Cookie\Jar\CookieJar
. As there is an interface, you can define your own implementation.
First, a cookie jar stores/manages the cookies. You can use the following API:
$hasCookies = $cookieJar->hasCookies();
$cookies = $cookieJar->getCookies();
$cookieJar->setCookies($cookies);
$cookieJar->addCookies($cookies);
$cookieJar->removeCookies($cookies);
$hasCookie = $cookieJar->hasCookie($cookie);
$cookieJar->addCookie($cookie);
$cookieJar->removeCookie($cookie);
// Clean expired cookies
$cookieJar->clean();
// Clear cookies
$cookieJar->clear($domain, $path, $name);
Second, the cookie jar creates the cookies through the Ivory\HttpAdapter\Event\Cookie\CookieFactoryInterface
and its default implementation is Ivory\HttpAdapter\Event\Cookie\CookieFactory
. It can be configured via the
constructor or getter/setter:
use Ivory\HttpAdapter\Event\Cookie\CookieFactory;
use Ivory\HttpAdapter\Event\Cookie\Jar\CookieJar;
$cookieJar = new CookieJar(new CookieFactory());
$cookieFactory = $cookieJar->getCookieFactory();
$cookieJar->setCookieFactory($cookieFactory);
Third, it is responsible to populate cookies in a request and extract them from a response with the following API:
$cookieJar->populate($request);
$cookieJar->extract($request, $response);
Fourth, the cookie jar implements the Countable
interface, so if you wants to know how many cookies are in it, you
can use:
$count = count($cookieJar);
Fifth, the cookies implements the IteratorAggregator
interface, so, you can directly access cookies with the
following code:
foreach ($cookieJar as $cookie) {
// Do what you want with the cookie
}
// or
$cookies = iterator_to_array($cookieJar);
The persistent cookie jar is described by the Ivory\HttpAdapter\Event\Cookie\Jar\PersistentCookieJarInterface
and its
default implementation is the Ivory\HttpAdapter\Event\Cookie\Jar\AbstractPersistentCookieJar
(not directly usable).
Basically, it allows you to load/save cookies from/to somewhere. All persistent cookie jars share the following API:
// Loads the cookie jar from the underlying resource
$cookieJar->load();
// Saves the cookie jar on the underlying resource
$cookieJar->save();
Additionally, the persistent cookie jar will automatically try to load it when it is instantiated and will save it when it is destroyed.
The file cookie jar is a persistent cookie jar which stores/retrieves cookies from a file. To use it:
use Ivory\HttpAdapter\Event\Cookie\Jar\FileCookieJar;
$cookieJar = new FileCookieJar('path/to/the/file');
The session cookie jar is a persistent cookie jar which stores/retrieves cookies from the session. To use it:
use Ivory\HttpAdapter\Event\Cookie\Jar\SessionCookieJar;
$cookieJar = new SessionCookieJar('session_key');
As already explained, the cookie factory is defined by the Ivory\HttpAdapter\Event\Cookie\CookieFactoryInterface
and its default implementation is Ivory\HttpAdapter\Event\Cookie\CookieFactory
. It is responsible to instantiate a
cookie. For that, you can directly instantiate it or parse an header:
$cookie = $cookieFactory->create($name, $value, $attributes, $createdAt);
// or
$cookie = $cookieFactory->parse($header);
A cookie is describes by the Ivory\HttpAdapter\Event\Cookie\CookieInterface
and its default implemenation is
Ivory\HttpAdapter\Event\Cookie\Cookie
. As there is an interface, you can define your own implementation.
A cookie stores all the informations related to it. You can get/set them with:
use Ivory\HttpAdapter\Event\Cookie\Cookie;
$cookie = new Cookie($name, $value, $attributes, $createdAt);
$hasName = $cookie->hasName();
$name = $cookie->getName();
$cookie->setName($name);
$hasValue = $cookie->hasValue();
$value = $cookie->getValue();
$cookie->setValue($value);
$hasAttributes = $cookie->hasAttributes();
$attributes = $cookie->getAttributes();
$cookie->setAttributes($attributes);
$cookie->addAttributes($attributes);
$cookie->removeAttributes($names);
$cookie->clearAttributes();
$hasAttribute = $cookie->hasAttributes($name);
$value = $cookie->getAttribute($name);
$cookie->setAttribute($name, $value);
$cookie->removeAttribute($name);
$createdAt = $cookie->getCreatedAt();
$cookie->setCreatedAt($createdAt);
All attribute names are described by the Ivory\HttpAdapter\Event\Cookie\Cookie::ATTR_*
constants.
To check/get the expiration date according to the Expires
pr Max-Age
attribute, you can use the following method:
$expired = $cookie->isExpired();
$expires = $cookie->getExpires();
Additionally, you can check if the request matches a request or just matches a part of the request:
$match = $cookie->match($request);
$match = $cookie->matchDomain($domain);
$match = $cookie->matchPath($path);
$match = $cookie->matchScheme($scheme);
Furthermore, you can compare two cookies:
$compare = $cookie->compare($otherCookie);
You can also convert the cookie into an array or a string:
$array = $cookie->toArray();
$string = (string) $cookie;
The history subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\HistorySubscriber
and allow you to
maintain an history of all requests/responses sent through a journal. To use it:
use Ivory\HttpAdapter\Event\Subscriber\HistorySubscriber;
$historySubscriber = new HistorySubscriber();
By default, a journal is created by the subscriber but you can specify it in its constructor:
use Ivory\HttpAdapter\Event\Subscriber\History\Journal;
use Ivory\HttpAdapter\Event\Subscriber\HistorySubscriber;
$journal = new Journal();
$historySubscriber = new HistorySubscriber($journal);
Finally, the journal can be access or change at runtime with:
$journal = $historySubscriber->getJournal();
$historySubscriber->setJournal($journal);
A journal is described by the Ivory\HttpAdapter\Event\Subscriber\History\JournalInterface
and its default
implementation is Ivory\HttpAdapter\Event\Subscriber\History\Journal
. As there is an interface, you can define your
own implementation.
So, a journal wraps a limit which represents the maximum number of allowed entries in the journal (default: 10) but can be configured via getter/setter:
$limit = $journal->getLimit();
$journal->setLimit($limit);
Second, the journal wraps all entries of the history according to the limit (the last entries are kept in the journal and the last ones are dropped when a new one is added). The following API allows you to check/get/set/clear the entries:
$hasEntries = $journal->hasEntries();
$entries = $journal->getEntries();
$journal->setEntries($entries);
$journal->addEntries($entries);
$journal->removeEntries($entries);
$hasEntry = $journal->hasEntry($entry);
$journal->addEntry($entry);
$journal->removeEntry($entry);
$journal->clear();
Third, a journal creates the entries through the Ivory\HttpAdapter\Event\History\JournalEntryFactoryInterface
and
its default implementation is Ivory\HttpAdapter\Event\History\JournalEntryFactory
. It can be configured via the
constructor or getter/setter:
use Ivory\HttpAdapter\Event\Subscriber\History\Journal;
use Ivory\HttpAdapter\Event\Subscriber\History\JournalEntryFactory;
$journal = new Journal(new JournalEntryFactory());
$entryJournalFactory = $journal->getJournalEntryFactory();
$journal->setJournalEntryFactory($entryJournalFactory);
Fourth, the journal implements the Countable
interface, so if you wants to know how many entries are in the journal,
you can use:
$count = count($journal);
Fifth, the journal implements the IteratorAggregator
interface, so, you can directly access entries with the
following code but the entries are ordered from the most recent to the most old:
foreach ($journal as $entry) {
// Do what you want with the entry
}
// or
$entries = iterator_to_array($journal);
A journal entry is described by the Ivory\HttpAdapter\Event\Subscriber\History\JournalEntryInterface
and its default
implementation is Ivory\HttpAdapter\Event\Subscriber\History\JournalEntry
. As there is an interface, you can define
your own implementation through the factory.
It wraps the request and the response. To get/set them, you can use the following API:
use Ivory\HttpAdapter\Event\Subscriber\History\JournalEntry;
$entry = new JournalEntry($request, $response);
$request = $entry->getRequest();
$entry->setRequest($request);
$response = $entry->getResponse();
$entry->setResponse($response);
The logger subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\LoggerSubscriber
and allows you to log all
requests sent/errorred through a PSR logger. As Monolog follows the PSR-3 Standard,
here an example using it and its stream handler but you can use any PSR compliant logger:
use Ivory\HttpAdapter\Event\Subscriber\LoggerSubscriber;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$monolog = new Logger('name');
$monolog->pushHandler(new StreamHandler('path/to/your.log'));
$loggerSubscriber = new LoggerSubscriber($monolog);
You can also change the logger at runtime:
$logger = $loggerSubscriber->getLogger();
$loggerSubscriber->setLogger($logger);
Internally, the logger subscriber uses the Ivory\HttpAdapter\Event\Formatter\Formatter
in order to format the log
context and and the Ivory\HttpAdapter\Timer\Timer
in order to time the request. By default, if you don't provide
them, it will be created automatically. If you want to use your own, you can pass them via constructor or getter/setter:
use Ivory\HttpAdapter\Event\Formatter\Formatter;
use Ivory\HttpAdapter\Event\Subscriber\LoggerSubscriber;
use Ivory\HttpAdapter\Event\Timer\Timer;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$monolog = new Logger('name');
$monolog->pushHandler(new StreamHandler('path/to/your.log'));
$loggerSubscriber = new LoggerSubscriber($monolog, new Formatter(), new Timer());
$formatter = $loggerSubscriber->getFormatter();
$loggerSubscriber->setFormatter($formatter);
$timer = $loggerSubscriber->getTimer();
$loggerSubscriber->setTimer($timer);
The redirect subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\RedirectSubscriber
and allow you to
follow redirects. Basically, by default, all http adapters don't follow the redirect and will give you the 3xx
response. Then, if you want to follow redirect, just register the redirect subscriber:
use Ivory\HttpAdapter\Event\Subscriber\RedirectSubscriber;
$redirectSubscriber = new RedirectSubscriber();
When you use the redirect subscriber, some parameters are available on the response:
effective_url
: The final url of the redirection.redirect_count
: The number of redirects which have been followed.
Internally, the redirect subscriber uses the Ivory\HttpAdapter\Event\Redirect\Redirect
in order to do a redirect.
By default, if you don't provide it, it will be created automatically. If you want to use your own, you can pass it via
constructor or getter/setter:
use Ivory\HttpAdapter\Redirect\Redirect;
use Ivory\HttpAdapter\Event\Subscriber\RedirectSubscriber;
$redirectSubscriber = new RedirectSubscriber(new Redirect());
$redirect = $redirectSubscriber->getRedirect();
$redirectSubscriber->setRedirect($redirect);
The redirect is also configurable and allow you to modify its behavior.
First, by default, the redirect allows you to follow 5 redirects. If you want to increase or decrease it, you can specify it as first constructor argument or via getter/setter:
use Ivory\HttpAdapter\Event\Redirect\Redirect;
$redirect = new Redirect(10);
$max = $redirect->getMax();
$redirect->setMax($max);
Second, by default, the redirect does not follow strictly the RFC and will prefer to do what most browser does (convert to a GET request). If you want to follow strictly the RFC, you can specify it as second constructor argument or via getter/setter:
use Ivory\HttpAdapter\Event\Redirect\Redirect;
$redirect = new Redirect(5, true);
$strict = $redirect->isStrict();
$redirect->setStrict($strict);
Third, by default, the redirect will throw an exception when the maximum number of redirects is exceeded. If you want to just stop the redirection and return the redirect response reached, you can specify it as third constructor argument or via getter/setter:
use Ivory\HttpAdapter\Event\Redirect\Redirect;
$redirect = new Redirect(5, false, false);
$throwException = $redirect->getThrowException();
$redirect->setThrowException($throwException);
The retry subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\RetrySubscriber
and allow you to retry an
errored request (more precisely when an exception is thrown). To use it:
use Ivory\HttpAdapter\Event\Subscriber\RetrySubscriber;
$retrySubscriber = new RetrySubscriber();
When you use the retry subscriber, some parameters are available on the request:
retry_count
: The number of retries which have been done.
Internally, the retry subscriber uses the Ivory\HttpAdapter\Event\Retry\Retry
in order to do a retry. By default, if
you don't provide it, it will be created automatically. If you want to use your own, you can pass it via constructor or
getter/setter:
use Ivory\HttpAdapter\Event\Subscriber\RetrySubscriber;
use Ivory\HttpAdapter\Event\Retry\Retry;
$retrySubscriber = new RetrySubscriber(new Retry());
$retry = $retrySubscriber->getRetry();
$retrySubscriber->setRetry($retry);
The retry is also configurable and allows you to modify its behavior.
By default, the retry uses a limited retry strategy of 3 retries combined to an exponential delayed one. Basically, that means it will retry 3 times maximum and wait more and more between each retry (2 ^ retry_count). This strategy can be configured through the constructor or via getter/setter:
use Ivory\HttpAdapter\Event\Retry\Strategy\ExponentialDelayedRetryStrategy;
use Ivory\HttpAdapter\Event\Retry\Strategy\LimitedRetryStrategy;
$retry = new Retry(new LimitedRetryStrategy(3, new ExponentialDelayedRetryStrategy()));
$strategy = $retry->getStrategy();
$retry->setStrategy($strategy);
If you want to know more about the retry strategies, the next sections are for you.
The constant delayed retry strategy is defined by the Ivory\HttpAdapter\Event\Retry\Strategy\ConstantDelayedRetryStrategy
and allow you to retry a request following the exact same delay between each retry (by default: 5 seconds).
use Ivory\HttpAdapter\Event\Retry\Strategy\ConstantDelayedRetryStrategy;
$retryStrategy = new ConstantDelayedRetryStrategy();
The delay can be configured through the constructor or via getter/setter:
use Ivory\HttpAdapter\Event\Retry\Strategy\ConstantDelayedRetryStrategy;
$retryStrategy = new ConstantDelayedRetryStrategy(2);
$delay = $retryStrategy->getDelay();
$retryStrategy->setDelay(2);
The linear delayed retry strategy is defined by the Ivory\HttpAdapter\Event\Retry\Strategy\LinearDelayedRetryStrategy
and allow you to retry a request following a linear delay between each retry (delay = configured delay * retry count).
By default, the configured delay is 5 seconds.
use Ivory\HttpAdapter\Event\Retry\Strategy\LinearDelayedRetryStrategy;
$retryStrategy = new LinearDelayedRetryStrategy();
The delay can be configured through the constructor or via getter/setter:
use Ivory\HttpAdapter\Event\Retry\Strategy\LinearDelayedRetryStrategy;
$retryStrategy = new LinearDelayedRetryStrategy(2);
$delay = $retryStrategy->getDelay();
$retryStrategy->setDelay(2);
The exponential retry strategy is defined by the Ivory\HttpAdapter\Event\Retry\Strategy\ExponentialRetryStrategy
and
allow you to retry a request following an exponential delay between each retry (delay = 2 ^ redirect count).
use Ivory\HttpAdapter\Event\Retry\Strategy\ExponentialDelayedRetryStrategy;
$retryStrategy = new ExponentialDelayedRetryStrategy();
The limited retry strategy is defined by the Ivory\HttpAdapter\Event\Retry\Strategy\LimitedRetryStrategy
and allow
you to limit the number of retry (by default: 3).
use Ivory\HttpAdapter\Event\Retry\Strategy\LimitedRetryStrategy;
$retryStrategy = new LimitedRetryStrategy();
The limit can be configured through the constructor or via getter/setter:
use Ivory\HttpAdapter\Event\Retry\Strategy\LimitedRetryStrategy;
$retryStrategy = new LimitedRetryStrategy(5);
$limit = $retryStrategy->getLimit();
$retryStrategy->setLimit(5);
The callback retry strategy is defined by the Ivory\HttpAdapter\Event\Retry\Strategy\CallbackRetryStrategy
and allow
you to limit the number of retry or specify the delay between each retry through two callbacks. They can be configured
through the constructor or via getter/setter:
use Ivory\HttpAdapter\Event\Retry\Strategy\CallbackRetryStrategy;
use Ivory\HttpAdapter\HttpAdapterException;
use Ivory\HttpAdapter\Message\InternalRequestInterface;
$retryStrategy = new CallbackRetryStrategy(
// Verify callback
function (InternalRequestInterface $request, HttpAdapterException $exception) {
// Return TRUE if you want to retry the request otherwise FALSE.
},
// Delay callback
function (InternalRequestInterface $request, HttpAdapterException $exception) {
// Return the delay to wait before retrying the request.
}
);
$hasVerifyCallback = $retryStrategy->hasVerifyCallback();
$verifyCallback = $retryStrategy->getVerifyCallback();
$retryStrategy->setVerifyCallback($verifyCallback);
$hasDelayCallback = $retryStrategy->hasDelayCallback();
$delayCallback = $retryStrategy->getDelayCallback();
$retryStrategy->setDelayCallback($delayCallback);
The two callbacks are not mandatory. If you don't provide the verify callback, it will consider it should retry the request and if you don't provide the delay callback, it will consider it should not wait before retrying the request.
All retry strategies implement the Ivory\HttpAdapter\Event\Retry\Strategy\RetryStrategyInterface
. Then, if you want
to define your own strategy, you will need to implement this interface. Here, an example which rely on information
wraps in the header:
namespace My\Own\Namespace;
use Ivory\HttpAdapter\Event\Retry\Strategy\RetryStrategyInterface;
use Ivory\HttpAdapter\Message\InternalRequestInterface;
class MyOwnRetryStrategy implements RetryStrategyInterface
{
public function verify(InternalRequestInterface $request)
{
return $request->getHeader('X-Can-Retry') === 'true';
}
public function delay(InternalRequestInterface $request)
{
return (float) $request->getHeader('X-Retry-Delay');
}
}
All retry strategies implement a chain of responsibility. That means you can combine strategies together in order to archive more complex behavior. For example, by default, the retry subscriber combines the limited retry strategy with the exponential delayed one.
To do that, all retry strategies implement the Ivory\HttpAdapter\Event\Retry\Strategy\RetryStrategyChainInterface
and
extend the Ivory\HttpAdapter\Event\Retry\Strategy\AbstractRetryStrategyChain
where the default chain of responsibility
implementation is done. Then, a chained retry strategy wraps a next strategy which is involved when it decides if it
should retry the request and for calculating the delay to wait if it should retry.
The behavior of the chain of responsibility is the following:
- It retries a request if all retry strategies in the chain of responsibility agree about retrying it.
- It waits the biggest delay stored in the chain of responsibility.
So, technically, all retry strategies accept the next strategy as second constructor argument. Here, an example on how you should instantiate them:
Ivory\HttpAdapter\Event\Retry\Strategy\LimitedRetryStrategy;
Ivory\HttpAdapter\Event\Retry\Strategy\LinearDelayedRetryStrategy;
$retryStrategy = new LimitedRetryStrategy(5, new LinearDelayedRetryStrategy(2));
Additionally, you can update the chain of responsibility at runtime with the following API:
$hasNext = $retryStrategy->hasNext();
$next = $retryStrategy->getNext();
$retryStrategy->setNext($next);
The status code subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\StatusCodeSubscriber
and allow you
to detect errored response. Basically, by default, all http adapters don't throw an exception if a 4xx or 5xx response
is returned. Then, if you want to throw an exception for this kind of response, just register the status code
subscriber:
use Ivory\HttpAdapter\Event\Subscriber\StatusCodeSubscriber;
$statusCodeSubscriber = new StatusCodeSubscriber();
Internally, the status code subscriber uses the Ivory\HttpAdapter\Event\StatusCode\StatusCode
in order to detect if
a request is valid. By default, if you don't provide it, it will be created automatically. If you want to use your own,
you can pass it via constructor or getter/setter:
use Ivory\HttpAdapter\Event\StatusCode\StatusCode;
use Ivory\HttpAdapter\Event\Subscriber\StatusCodeSubscriber;
$statusCodeSubscriber = new StatusCodeSubscriber(new StatusCode());
$statusCode = $statusCodeSubscriber->getStatusCode();
$statusCodeSubscriber->setStatusCode($statusCode);
The stopwatch subscriber is defined by the Ivory\HttpAdapter\Event\Subscriber\StopwatchSubscriber
and allows you to
time the http adapter sending through the Symfony2 Stopwatch component. To use it:
use Ivory\HttpAdapter\Event\Subscriber\StopwatchSubscriber;
use Symfony\Component\Stopwatch\Stopwatch;
$stopwatch = new Stopwatch();
$stopwatchSubscriber = new StopwatchSubscriber($stopwatch);
You can also change the stopwatch at runtime:
$stopwatch = $stopwatchSubscriber->getStopwatch();
$stopwatchSubscriber->setStopwatch($stopwatch);
All event subscribers can work together (thanks to the event priorities). Here, the summary:
Event Subscriber | Pre Send Event | Post Send Event | Exception Event |
---|---|---|---|
Stopwatch | 10000 | -10000 | -10000 |
Basic Auth | 300 | - | - |
Cookie | 300 | 300 | - |
Status Code | - | 200 | - |
History | 100 | 100 | - |
Logger | 100 | 100 | 100 |
Redirect | - | 0 | - |
Retry | - | - | 0 |
Cache | -100 | -100 | -100 |