PluginProbe ʕ •ᴥ•ʔ
ShareThis Dashboard for Google Analytics / 3.2.1
ShareThis Dashboard for Google Analytics v3.2.1
3.3.2 trunk 1.0.7 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2.5 2.3.5 2.3.6 2.3.7 2.3.8 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.0.0 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.3.0 3.3.1
googleanalytics / lib / analytics-admin / vendor / guzzlehttp / guzzle / src / Handler / CurlMultiHandler.php
googleanalytics / lib / analytics-admin / vendor / guzzlehttp / guzzle / src / Handler Last commit date
CurlFactory.php 3 years ago CurlFactoryInterface.php 3 years ago CurlHandler.php 3 years ago CurlMultiHandler.php 2 years ago EasyHandle.php 3 years ago HeaderProcessor.php 3 years ago MockHandler.php 3 years ago Proxy.php 3 years ago StreamHandler.php 3 years ago
CurlMultiHandler.php
263 lines
1 <?php
2
3 namespace GuzzleHttp\Handler;
4
5 use GuzzleHttp\Promise as P;
6 use GuzzleHttp\Promise\Promise;
7 use GuzzleHttp\Promise\PromiseInterface;
8 use GuzzleHttp\Utils;
9 use Psr\Http\Message\RequestInterface;
10
11 /**
12 * Returns an asynchronous response using curl_multi_* functions.
13 *
14 * When using the CurlMultiHandler, custom curl options can be specified as an
15 * associative array of curl option constants mapping to values in the
16 * **curl** key of the provided request options.
17 *
18 * @property resource|\CurlMultiHandle $_mh Internal use only. Lazy loaded multi-handle.
19 *
20 * @final
21 */
22 #[\AllowDynamicProperties]
23 class CurlMultiHandler
24 {
25 /**
26 * @var CurlFactoryInterface
27 */
28 private $factory;
29
30 /**
31 * @var int
32 */
33 private $selectTimeout;
34
35 /**
36 * @var int Will be higher than 0 when `curl_multi_exec` is still running.
37 */
38 private $active = 0;
39
40 /**
41 * @var array Request entry handles, indexed by handle id in `addRequest`.
42 *
43 * @see CurlMultiHandler::addRequest
44 */
45 private $handles = [];
46
47 /**
48 * @var array<int, float> An array of delay times, indexed by handle id in `addRequest`.
49 *
50 * @see CurlMultiHandler::addRequest
51 */
52 private $delays = [];
53
54 /**
55 * @var array<mixed> An associative array of CURLMOPT_* options and corresponding values for curl_multi_setopt()
56 */
57 private $options = [];
58
59 /**
60 * This handler accepts the following options:
61 *
62 * - handle_factory: An optional factory used to create curl handles
63 * - select_timeout: Optional timeout (in seconds) to block before timing
64 * out while selecting curl handles. Defaults to 1 second.
65 * - options: An associative array of CURLMOPT_* options and
66 * corresponding values for curl_multi_setopt()
67 */
68 public function __construct(array $options = [])
69 {
70 $this->factory = $options['handle_factory'] ?? new CurlFactory(50);
71
72 if (isset($options['select_timeout'])) {
73 $this->selectTimeout = $options['select_timeout'];
74 } elseif ($selectTimeout = Utils::getenv('GUZZLE_CURL_SELECT_TIMEOUT')) {
75 @trigger_error('Since guzzlehttp/guzzle 7.2.0: Using environment variable GUZZLE_CURL_SELECT_TIMEOUT is deprecated. Use option "select_timeout" instead.', \E_USER_DEPRECATED);
76 $this->selectTimeout = (int) $selectTimeout;
77 } else {
78 $this->selectTimeout = 1;
79 }
80
81 $this->options = $options['options'] ?? [];
82 }
83
84 /**
85 * @param string $name
86 *
87 * @return resource|\CurlMultiHandle
88 *
89 * @throws \BadMethodCallException when another field as `_mh` will be gotten
90 * @throws \RuntimeException when curl can not initialize a multi handle
91 */
92 public function __get($name)
93 {
94 if ($name !== '_mh') {
95 throw new \BadMethodCallException("Can not get other property as '_mh'.");
96 }
97
98 $multiHandle = \curl_multi_init();
99
100 if (false === $multiHandle) {
101 throw new \RuntimeException('Can not initialize curl multi handle.');
102 }
103
104 $this->_mh = $multiHandle;
105
106 foreach ($this->options as $option => $value) {
107 // A warning is raised in case of a wrong option.
108 curl_multi_setopt($this->_mh, $option, $value);
109 }
110
111 return $this->_mh;
112 }
113
114 public function __destruct()
115 {
116 if (isset($this->_mh)) {
117 \curl_multi_close($this->_mh);
118 unset($this->_mh);
119 }
120 }
121
122 public function __invoke(RequestInterface $request, array $options): PromiseInterface
123 {
124 $easy = $this->factory->create($request, $options);
125 $id = (int) $easy->handle;
126
127 $promise = new Promise(
128 [$this, 'execute'],
129 function () use ($id) {
130 return $this->cancel($id);
131 }
132 );
133
134 $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
135
136 return $promise;
137 }
138
139 /**
140 * Ticks the curl event loop.
141 */
142 public function tick(): void
143 {
144 // Add any delayed handles if needed.
145 if ($this->delays) {
146 $currentTime = Utils::currentTime();
147 foreach ($this->delays as $id => $delay) {
148 if ($currentTime >= $delay) {
149 unset($this->delays[$id]);
150 \curl_multi_add_handle(
151 $this->_mh,
152 $this->handles[$id]['easy']->handle
153 );
154 }
155 }
156 }
157
158 // Step through the task queue which may add additional requests.
159 P\Utils::queue()->run();
160
161 if ($this->active && \curl_multi_select($this->_mh, $this->selectTimeout) === -1) {
162 // Perform a usleep if a select returns -1.
163 // See: https://bugs.php.net/bug.php?id=61141
164 \usleep(250);
165 }
166
167 while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM);
168
169 $this->processMessages();
170 }
171
172 /**
173 * Runs until all outstanding connections have completed.
174 */
175 public function execute(): void
176 {
177 $queue = P\Utils::queue();
178
179 while ($this->handles || !$queue->isEmpty()) {
180 // If there are no transfers, then sleep for the next delay
181 if (!$this->active && $this->delays) {
182 \usleep($this->timeToNext());
183 }
184 $this->tick();
185 }
186 }
187
188 private function addRequest(array $entry): void
189 {
190 $easy = $entry['easy'];
191 $id = (int) $easy->handle;
192 $this->handles[$id] = $entry;
193 if (empty($easy->options['delay'])) {
194 \curl_multi_add_handle($this->_mh, $easy->handle);
195 } else {
196 $this->delays[$id] = Utils::currentTime() + ($easy->options['delay'] / 1000);
197 }
198 }
199
200 /**
201 * Cancels a handle from sending and removes references to it.
202 *
203 * @param int $id Handle ID to cancel and remove.
204 *
205 * @return bool True on success, false on failure.
206 */
207 private function cancel($id): bool
208 {
209 if (!is_int($id)) {
210 trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an integer to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__);
211 }
212
213 // Cannot cancel if it has been processed.
214 if (!isset($this->handles[$id])) {
215 return false;
216 }
217
218 $handle = $this->handles[$id]['easy']->handle;
219 unset($this->delays[$id], $this->handles[$id]);
220 \curl_multi_remove_handle($this->_mh, $handle);
221 \curl_close($handle);
222
223 return true;
224 }
225
226 private function processMessages(): void
227 {
228 while ($done = \curl_multi_info_read($this->_mh)) {
229 if ($done['msg'] !== \CURLMSG_DONE) {
230 // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216
231 continue;
232 }
233 $id = (int) $done['handle'];
234 \curl_multi_remove_handle($this->_mh, $done['handle']);
235
236 if (!isset($this->handles[$id])) {
237 // Probably was cancelled.
238 continue;
239 }
240
241 $entry = $this->handles[$id];
242 unset($this->handles[$id], $this->delays[$id]);
243 $entry['easy']->errno = $done['result'];
244 $entry['deferred']->resolve(
245 CurlFactory::finish($this, $entry['easy'], $this->factory)
246 );
247 }
248 }
249
250 private function timeToNext(): int
251 {
252 $currentTime = Utils::currentTime();
253 $nextTime = \PHP_INT_MAX;
254 foreach ($this->delays as $time) {
255 if ($time < $nextTime) {
256 $nextTime = $time;
257 }
258 }
259
260 return ((int) \max(0, $nextTime - $currentTime)) * 1000000;
261 }
262 }
263