Mercurial > hg > isophonics-drupal-site
comparison vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 5fb285c0d0e3 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 namespace GuzzleHttp\Handler; | |
3 | |
4 use GuzzleHttp\Promise as P; | |
5 use GuzzleHttp\Promise\Promise; | |
6 use GuzzleHttp\Psr7; | |
7 use Psr\Http\Message\RequestInterface; | |
8 | |
9 /** | |
10 * Returns an asynchronous response using curl_multi_* functions. | |
11 * | |
12 * When using the CurlMultiHandler, custom curl options can be specified as an | |
13 * associative array of curl option constants mapping to values in the | |
14 * **curl** key of the provided request options. | |
15 * | |
16 * @property resource $_mh Internal use only. Lazy loaded multi-handle. | |
17 */ | |
18 class CurlMultiHandler | |
19 { | |
20 /** @var CurlFactoryInterface */ | |
21 private $factory; | |
22 private $selectTimeout; | |
23 private $active; | |
24 private $handles = []; | |
25 private $delays = []; | |
26 | |
27 /** | |
28 * This handler accepts the following options: | |
29 * | |
30 * - handle_factory: An optional factory used to create curl handles | |
31 * - select_timeout: Optional timeout (in seconds) to block before timing | |
32 * out while selecting curl handles. Defaults to 1 second. | |
33 * | |
34 * @param array $options | |
35 */ | |
36 public function __construct(array $options = []) | |
37 { | |
38 $this->factory = isset($options['handle_factory']) | |
39 ? $options['handle_factory'] : new CurlFactory(50); | |
40 $this->selectTimeout = isset($options['select_timeout']) | |
41 ? $options['select_timeout'] : 1; | |
42 } | |
43 | |
44 public function __get($name) | |
45 { | |
46 if ($name === '_mh') { | |
47 return $this->_mh = curl_multi_init(); | |
48 } | |
49 | |
50 throw new \BadMethodCallException(); | |
51 } | |
52 | |
53 public function __destruct() | |
54 { | |
55 if (isset($this->_mh)) { | |
56 curl_multi_close($this->_mh); | |
57 unset($this->_mh); | |
58 } | |
59 } | |
60 | |
61 public function __invoke(RequestInterface $request, array $options) | |
62 { | |
63 $easy = $this->factory->create($request, $options); | |
64 $id = (int) $easy->handle; | |
65 | |
66 $promise = new Promise( | |
67 [$this, 'execute'], | |
68 function () use ($id) { return $this->cancel($id); } | |
69 ); | |
70 | |
71 $this->addRequest(['easy' => $easy, 'deferred' => $promise]); | |
72 | |
73 return $promise; | |
74 } | |
75 | |
76 /** | |
77 * Ticks the curl event loop. | |
78 */ | |
79 public function tick() | |
80 { | |
81 // Add any delayed handles if needed. | |
82 if ($this->delays) { | |
83 $currentTime = microtime(true); | |
84 foreach ($this->delays as $id => $delay) { | |
85 if ($currentTime >= $delay) { | |
86 unset($this->delays[$id]); | |
87 curl_multi_add_handle( | |
88 $this->_mh, | |
89 $this->handles[$id]['easy']->handle | |
90 ); | |
91 } | |
92 } | |
93 } | |
94 | |
95 // Step through the task queue which may add additional requests. | |
96 P\queue()->run(); | |
97 | |
98 if ($this->active && | |
99 curl_multi_select($this->_mh, $this->selectTimeout) === -1 | |
100 ) { | |
101 // Perform a usleep if a select returns -1. | |
102 // See: https://bugs.php.net/bug.php?id=61141 | |
103 usleep(250); | |
104 } | |
105 | |
106 while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM); | |
107 | |
108 $this->processMessages(); | |
109 } | |
110 | |
111 /** | |
112 * Runs until all outstanding connections have completed. | |
113 */ | |
114 public function execute() | |
115 { | |
116 $queue = P\queue(); | |
117 | |
118 while ($this->handles || !$queue->isEmpty()) { | |
119 // If there are no transfers, then sleep for the next delay | |
120 if (!$this->active && $this->delays) { | |
121 usleep($this->timeToNext()); | |
122 } | |
123 $this->tick(); | |
124 } | |
125 } | |
126 | |
127 private function addRequest(array $entry) | |
128 { | |
129 $easy = $entry['easy']; | |
130 $id = (int) $easy->handle; | |
131 $this->handles[$id] = $entry; | |
132 if (empty($easy->options['delay'])) { | |
133 curl_multi_add_handle($this->_mh, $easy->handle); | |
134 } else { | |
135 $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000); | |
136 } | |
137 } | |
138 | |
139 /** | |
140 * Cancels a handle from sending and removes references to it. | |
141 * | |
142 * @param int $id Handle ID to cancel and remove. | |
143 * | |
144 * @return bool True on success, false on failure. | |
145 */ | |
146 private function cancel($id) | |
147 { | |
148 // Cannot cancel if it has been processed. | |
149 if (!isset($this->handles[$id])) { | |
150 return false; | |
151 } | |
152 | |
153 $handle = $this->handles[$id]['easy']->handle; | |
154 unset($this->delays[$id], $this->handles[$id]); | |
155 curl_multi_remove_handle($this->_mh, $handle); | |
156 curl_close($handle); | |
157 | |
158 return true; | |
159 } | |
160 | |
161 private function processMessages() | |
162 { | |
163 while ($done = curl_multi_info_read($this->_mh)) { | |
164 $id = (int) $done['handle']; | |
165 curl_multi_remove_handle($this->_mh, $done['handle']); | |
166 | |
167 if (!isset($this->handles[$id])) { | |
168 // Probably was cancelled. | |
169 continue; | |
170 } | |
171 | |
172 $entry = $this->handles[$id]; | |
173 unset($this->handles[$id], $this->delays[$id]); | |
174 $entry['easy']->errno = $done['result']; | |
175 $entry['deferred']->resolve( | |
176 CurlFactory::finish( | |
177 $this, | |
178 $entry['easy'], | |
179 $this->factory | |
180 ) | |
181 ); | |
182 } | |
183 } | |
184 | |
185 private function timeToNext() | |
186 { | |
187 $currentTime = microtime(true); | |
188 $nextTime = PHP_INT_MAX; | |
189 foreach ($this->delays as $time) { | |
190 if ($time < $nextTime) { | |
191 $nextTime = $time; | |
192 } | |
193 } | |
194 | |
195 return max(0, $nextTime - $currentTime) * 1000000; | |
196 } | |
197 } |