PluginProbe
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more / 2.5.5
StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more v2.5.5
3.7.2 3.7.1 3.7.0 3.6.0 3.6.1 3.5.2 trunk 2.0 2.1 2.2 2.3 2.4 2.4.1 2.4.2 2.4.3 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.6.0 2.6.1 All 73 releases
stockpack / vendor / guzzlehttp / guzzle / src / Handler / CurlMultiHandler.php

CurlMultiHandler.php in StockPack – Stock photos and AI images from Unsplash, Adobe Stock, Freepik and more 2.5.5, at vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php

220 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace GuzzleHttp\Handler;
3
4 use GuzzleHttp\Exception\InvalidArgumentException;
5 use GuzzleHttp\Promise as P;
6 use GuzzleHttp\Promise\Promise;
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 private $options = [];
27
28 /**
29 * This handler accepts the following options:
30 *
31 * - handle_factory: An optional factory used to create curl handles
32 * - select_timeout: Optional timeout (in seconds) to block before timing
33 * out while selecting curl handles. Defaults to 1 second.
34 * - options: An associative array of CURLMOPT_* options and
35 * corresponding values for curl_multi_setopt()
36 *
37 * @param array $options
38 */
39 public function __construct(array $options = [])
40 {
41 $this->factory = isset($options['handle_factory'])
42 ? $options['handle_factory'] : new CurlFactory(50);
43
44 if (isset($options['select_timeout'])) {
45 $this->selectTimeout = $options['select_timeout'];
46 } elseif ($selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT')) {
47 $this->selectTimeout = $selectTimeout;
48 } else {
49 $this->selectTimeout = 1;
50 }
51
52 $this->options = isset($options['options']) ? $options['options'] : [];
53 }
54
55 public function __get($name)
56 {
57 if ($name === '_mh') {
58 $this->_mh = curl_multi_init();
59
60 foreach ($this->options as $option => $value) {
61 // A warning is raised in case of a wrong option.
62 curl_multi_setopt($this->_mh, $option, $value);
63 }
64
65 // Further calls to _mh will return the value directly, without entering the
66 // __get() method at all.
67 return $this->_mh;
68 }
69
70 throw new \BadMethodCallException();
71 }
72
73 public function __destruct()
74 {
75 if (isset($this->_mh)) {
76 curl_multi_close($this->_mh);
77 unset($this->_mh);
78 }
79 }
80
81 public function __invoke(RequestInterface $request, array $options)
82 {
83 $easy = $this->factory->create($request, $options);
84 $id = (int) $easy->handle;
85
86 $promise = new Promise(
87 [$this, 'execute'],
88 function () use ($id) {
89 return $this->cancel($id);
90 }
91 );
92
93 $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
94
95 return $promise;
96 }
97
98 /**
99 * Ticks the curl event loop.
100 */
101 public function tick()
102 {
103 // Add any delayed handles if needed.
104 if ($this->delays) {
105 $currentTime = \GuzzleHttp\_current_time();
106 foreach ($this->delays as $id => $delay) {
107 if ($currentTime >= $delay) {
108 unset($this->delays[$id]);
109 curl_multi_add_handle(
110 $this->_mh,
111 $this->handles[$id]['easy']->handle
112 );
113 }
114 }
115 }
116
117 // Step through the task queue which may add additional requests.
118 P\queue()->run();
119
120 if ($this->active &&
121 curl_multi_select($this->_mh, $this->selectTimeout) === -1
122 ) {
123 // Perform a usleep if a select returns -1.
124 // See: https://bugs.php.net/bug.php?id=61141
125 usleep(250);
126 }
127
128 while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
129
130 $this->processMessages();
131 }
132
133 /**
134 * Runs until all outstanding connections have completed.
135 */
136 public function execute()
137 {
138 $queue = P\queue();
139
140 while ($this->handles || !$queue->isEmpty()) {
141 // If there are no transfers, then sleep for the next delay
142 if (!$this->active && $this->delays) {
143 usleep($this->timeToNext());
144 }
145 $this->tick();
146 }
147 }
148
149 private function addRequest(array $entry)
150 {
151 $easy = $entry['easy'];
152 $id = (int) $easy->handle;
153 $this->handles[$id] = $entry;
154 if (empty($easy->options['delay'])) {
155 curl_multi_add_handle($this->_mh, $easy->handle);
156 } else {
157 $this->delays[$id] = \GuzzleHttp\_current_time() + ($easy->options['delay'] / 1000);
158 }
159 }
160
161 /**
162 * Cancels a handle from sending and removes references to it.
163 *
164 * @param int $id Handle ID to cancel and remove.
165 *
166 * @return bool True on success, false on failure.
167 */
168 private function cancel($id)
169 {
170 // Cannot cancel if it has been processed.
171 if (!isset($this->handles[$id])) {
172 return false;
173 }
174
175 $handle = $this->handles[$id]['easy']->handle;
176 unset($this->delays[$id], $this->handles[$id]);
177 curl_multi_remove_handle($this->_mh, $handle);
178 curl_close($handle);
179
180 return true;
181 }
182
183 private function processMessages()
184 {
185 while ($done = curl_multi_info_read($this->_mh)) {
186 $id = (int) $done['handle'];
187 curl_multi_remove_handle($this->_mh, $done['handle']);
188
189 if (!isset($this->handles[$id])) {
190 // Probably was cancelled.
191 continue;
192 }
193
194 $entry = $this->handles[$id];
195 unset($this->handles[$id], $this->delays[$id]);
196 $entry['easy']->errno = $done['result'];
197 $entry['deferred']->resolve(
198 CurlFactory::finish(
199 $this,
200 $entry['easy'],
201 $this->factory
202 )
203 );
204 }
205 }
206
207 private function timeToNext()
208 {
209 $currentTime = \GuzzleHttp\_current_time();
210 $nextTime = PHP_INT_MAX;
211 foreach ($this->delays as $time) {
212 if ($time < $nextTime) {
213 $nextTime = $time;
214 }
215 }
216
217 return max(0, $nextTime - $currentTime) * 1000000;
218 }
219 }
220