PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
← All changes | vendor_prefixed/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php +0 -237 27.5 → trunk View file →
@@ -1,237 +1,0 @@
1 -<?php
2 -
3 -namespace YoastSEO_Vendor\GuzzleHttp\Handler;
4 -
5 -use Closure;
6 -use YoastSEO_Vendor\GuzzleHttp\Promise as P;
7 -use YoastSEO_Vendor\GuzzleHttp\Promise\Promise;
8 -use YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface;
9 -use YoastSEO_Vendor\GuzzleHttp\Utils;
10 -use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
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 - * @final
19 - */
20 -class CurlMultiHandler
21 -{
22 - /**
23 - * @var CurlFactoryInterface
24 - */
25 - private $factory;
26 - /**
27 - * @var int
28 - */
29 - private $selectTimeout;
30 - /**
31 - * @var int Will be higher than 0 when `curl_multi_exec` is still running.
32 - */
33 - private $active = 0;
34 - /**
35 - * @var array Request entry handles, indexed by handle id in `addRequest`.
36 - *
37 - * @see CurlMultiHandler::addRequest
38 - */
39 - private $handles = [];
40 - /**
41 - * @var array<int, float> An array of delay times, indexed by handle id in `addRequest`.
42 - *
43 - * @see CurlMultiHandler::addRequest
44 - */
45 - private $delays = [];
46 - /**
47 - * @var array<mixed> An associative array of CURLMOPT_* options and corresponding values for curl_multi_setopt()
48 - */
49 - private $options = [];
50 - /** @var resource|\CurlMultiHandle */
51 - private $_mh;
52 - /**
53 - * This handler accepts the following options:
54 - *
55 - * - handle_factory: An optional factory used to create curl handles
56 - * - select_timeout: Optional timeout (in seconds) to block before timing
57 - * out while selecting curl handles. Defaults to 1 second.
58 - * - options: An associative array of CURLMOPT_* options and
59 - * corresponding values for curl_multi_setopt()
60 - */
61 - public function __construct(array $options = [])
62 - {
63 - $this->factory = $options['handle_factory'] ?? new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactory(50);
64 - if (isset($options['select_timeout'])) {
65 - $this->selectTimeout = $options['select_timeout'];
66 - } elseif ($selectTimeout = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('GUZZLE_CURL_SELECT_TIMEOUT')) {
67 - @\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);
68 - $this->selectTimeout = (int) $selectTimeout;
69 - } else {
70 - $this->selectTimeout = 1;
71 - }
72 - $this->options = $options['options'] ?? [];
73 - // unsetting the property forces the first access to go through
74 - // __get().
75 - unset($this->_mh);
76 - }
77 - /**
78 - * @param string $name
79 - *
80 - * @return resource|\CurlMultiHandle
81 - *
82 - * @throws \BadMethodCallException when another field as `_mh` will be gotten
83 - * @throws \RuntimeException when curl can not initialize a multi handle
84 - */
85 - public function __get($name)
86 - {
87 - if ($name !== '_mh') {
88 - throw new \BadMethodCallException("Can not get other property as '_mh'.");
89 - }
90 - $multiHandle = \curl_multi_init();
91 - if (\false === $multiHandle) {
92 - throw new \RuntimeException('Can not initialize curl multi handle.');
93 - }
94 - $this->_mh = $multiHandle;
95 - foreach ($this->options as $option => $value) {
96 - // A warning is raised in case of a wrong option.
97 - \curl_multi_setopt($this->_mh, $option, $value);
98 - }
99 - return $this->_mh;
100 - }
101 - public function __destruct()
102 - {
103 - if (isset($this->_mh)) {
104 - \curl_multi_close($this->_mh);
105 - unset($this->_mh);
106 - }
107 - }
108 - public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
109 - {
110 - $easy = $this->factory->create($request, $options);
111 - $id = (int) $easy->handle;
112 - $promise = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise([$this, 'execute'], function () use($id) {
113 - return $this->cancel($id);
114 - });
115 - $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
116 - return $promise;
117 - }
118 - /**
119 - * Ticks the curl event loop.
120 - */
121 - public function tick() : void
122 - {
123 - // Add any delayed handles if needed.
124 - if ($this->delays) {
125 - $currentTime = \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime();
126 - foreach ($this->delays as $id => $delay) {
127 - if ($currentTime >= $delay) {
128 - unset($this->delays[$id]);
129 - \curl_multi_add_handle($this->_mh, $this->handles[$id]['easy']->handle);
130 - }
131 - }
132 - }
133 - // Run curl_multi_exec in the queue to enable other async tasks to run
134 - \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->add(\Closure::fromCallable([$this, 'tickInQueue']));
135 - // Step through the task queue which may add additional requests.
136 - \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->run();
137 - if ($this->active && \curl_multi_select($this->_mh, $this->selectTimeout) === -1) {
138 - // Perform a usleep if a select returns -1.
139 - // See: https://bugs.php.net/bug.php?id=61141
140 - \usleep(250);
141 - }
142 - while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
143 - // Prevent busy looping for slow HTTP requests.
144 - \curl_multi_select($this->_mh, $this->selectTimeout);
145 - }
146 - $this->processMessages();
147 - }
148 - /**
149 - * Runs \curl_multi_exec() inside the event loop, to prevent busy looping
150 - */
151 - private function tickInQueue() : void
152 - {
153 - if (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
154 - \curl_multi_select($this->_mh, 0);
155 - \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->add(\Closure::fromCallable([$this, 'tickInQueue']));
156 - }
157 - }
158 - /**
159 - * Runs until all outstanding connections have completed.
160 - */
161 - public function execute() : void
162 - {
163 - $queue = \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue();
164 - while ($this->handles || !$queue->isEmpty()) {
165 - // If there are no transfers, then sleep for the next delay
166 - if (!$this->active && $this->delays) {
167 - \usleep($this->timeToNext());
168 - }
169 - $this->tick();
170 - }
171 - }
172 - private function addRequest(array $entry) : void
173 - {
174 - $easy = $entry['easy'];
175 - $id = (int) $easy->handle;
176 - $this->handles[$id] = $entry;
177 - if (empty($easy->options['delay'])) {
178 - \curl_multi_add_handle($this->_mh, $easy->handle);
179 - } else {
180 - $this->delays[$id] = \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime() + $easy->options['delay'] / 1000;
181 - }
182 - }
183 - /**
184 - * Cancels a handle from sending and removes references to it.
185 - *
186 - * @param int $id Handle ID to cancel and remove.
187 - *
188 - * @return bool True on success, false on failure.
189 - */
190 - private function cancel($id) : bool
191 - {
192 - if (!\is_int($id)) {
193 - 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__);
194 - }
195 - // Cannot cancel if it has been processed.
196 - if (!isset($this->handles[$id])) {
197 - return \false;
198 - }
199 - $handle = $this->handles[$id]['easy']->handle;
200 - unset($this->delays[$id], $this->handles[$id]);
201 - \curl_multi_remove_handle($this->_mh, $handle);
202 - if (\PHP_VERSION_ID < 80000) {
203 - \curl_close($handle);
204 - }
205 - return \true;
206 - }
207 - private function processMessages() : void
208 - {
209 - while ($done = \curl_multi_info_read($this->_mh)) {
210 - if ($done['msg'] !== \CURLMSG_DONE) {
211 - // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216
212 - continue;
213 - }
214 - $id = (int) $done['handle'];
215 - \curl_multi_remove_handle($this->_mh, $done['handle']);
216 - if (!isset($this->handles[$id])) {
217 - // Probably was cancelled.
218 - continue;
219 - }
220 - $entry = $this->handles[$id];
221 - unset($this->handles[$id], $this->delays[$id]);
222 - $entry['easy']->errno = $done['result'];
223 - $entry['deferred']->resolve(\YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactory::finish($this, $entry['easy'], $this->factory));
224 - }
225 - }
226 - private function timeToNext() : int
227 - {
228 - $currentTime = \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime();
229 - $nextTime = \PHP_INT_MAX;
230 - foreach ($this->delays as $time) {
231 - if ($time < $nextTime) {
232 - $nextTime = $time;
233 - }
234 - }
235 - return (int) \max(0, $nextTime - $currentTime) * 1000000;
236 - }
237 -}