1: <?php
2:
3: namespace Guzzle\Plugin\Backoff;
4:
5: use Guzzle\Common\Event;
6: use Guzzle\Common\AbstractHasDispatcher;
7: use Guzzle\Http\Message\EntityEnclosingRequestInterface;
8: use Guzzle\Http\Message\RequestInterface;
9: use Guzzle\Http\Message\Response;
10: use Guzzle\Http\Curl\CurlMultiInterface;
11: use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12:
13: 14: 15:
16: class BackoffPlugin extends AbstractHasDispatcher implements EventSubscriberInterface
17: {
18: const DELAY_PARAM = CurlMultiInterface::BLOCKING;
19: const RETRY_PARAM = 'plugins.backoff.retry_count';
20: const RETRY_EVENT = 'plugins.backoff.retry';
21:
22: 23: 24:
25: protected $strategy;
26:
27: 28: 29: 30:
31: public function __construct(BackoffStrategyInterface $strategy = null)
32: {
33: $this->strategy = $strategy;
34: }
35:
36: 37: 38: 39: 40: 41: 42: 43: 44:
45: public static function getExponentialBackoff(
46: $maxRetries = 3,
47: array $httpCodes = null,
48: array $curlCodes = null
49: ) {
50: return new self(new TruncatedBackoffStrategy($maxRetries,
51: new HttpBackoffStrategy($httpCodes,
52: new CurlBackoffStrategy($curlCodes,
53: new ExponentialBackoffStrategy()
54: )
55: )
56: ));
57: }
58:
59: 60: 61:
62: public static function getAllEvents()
63: {
64: return array(self::RETRY_EVENT);
65: }
66:
67: 68: 69:
70: public static function getSubscribedEvents()
71: {
72: return array(
73: 'request.sent' => 'onRequestSent',
74: 'request.exception' => 'onRequestSent',
75: CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll'
76: );
77: }
78:
79: 80: 81: 82: 83:
84: public function onRequestSent(Event $event)
85: {
86: $request = $event['request'];
87: $response = $event['response'];
88: $exception = $event['exception'];
89:
90: $params = $request->getParams();
91: $retries = (int) $params->get(self::RETRY_PARAM);
92: $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception);
93:
94: if ($delay !== false) {
95:
96: $params->set(self::RETRY_PARAM, ++$retries)
97: ->set(self::DELAY_PARAM, microtime(true) + $delay);
98:
99: $request->setState(RequestInterface::STATE_TRANSFER);
100: $this->dispatch(self::RETRY_EVENT, array(
101: 'request' => $request,
102: 'response' => $response,
103: 'handle' => $exception ? $exception->getCurlHandle() : null,
104: 'retries' => $retries,
105: 'delay' => $delay
106: ));
107: }
108: }
109:
110: 111: 112: 113: 114:
115: public function onRequestPoll(Event $event)
116: {
117: $request = $event['request'];
118: $delay = $request->getParams()->get(self::DELAY_PARAM);
119:
120:
121: if (null !== $delay && microtime(true) >= $delay) {
122:
123:
124: $request->getParams()->remove(self::DELAY_PARAM);
125:
126: if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) {
127: $request->getBody()->seek(0);
128: }
129: $multi = $event['curl_multi'];
130: $multi->remove($request);
131: $multi->add($request);
132: }
133: }
134: }
135: