scratch – Blame information for rev 87
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
87 | office | 1 | <?php |
2 | |||
3 | namespace GuzzleHttp; |
||
4 | |||
5 | use GuzzleHttp\Adapter\AdapterInterface; |
||
6 | use GuzzleHttp\Adapter\Curl\CurlAdapter; |
||
7 | use GuzzleHttp\Adapter\Curl\MultiAdapter; |
||
8 | use GuzzleHttp\Adapter\FakeParallelAdapter; |
||
9 | use GuzzleHttp\Adapter\ParallelAdapterInterface; |
||
10 | use GuzzleHttp\Adapter\StreamAdapter; |
||
11 | use GuzzleHttp\Adapter\StreamingProxyAdapter; |
||
12 | use GuzzleHttp\Adapter\Transaction; |
||
13 | use GuzzleHttp\Adapter\TransactionIterator; |
||
14 | use GuzzleHttp\Event\HasEmitterTrait; |
||
15 | use GuzzleHttp\Exception\RequestException; |
||
16 | use GuzzleHttp\Message\MessageFactory; |
||
17 | use GuzzleHttp\Message\MessageFactoryInterface; |
||
18 | use GuzzleHttp\Message\RequestInterface; |
||
19 | |||
20 | /** |
||
21 | * HTTP client |
||
22 | */ |
||
23 | class Client implements ClientInterface |
||
24 | { |
||
25 | use HasEmitterTrait; |
||
26 | |||
27 | const DEFAULT_CONCURRENCY = 25; |
||
28 | |||
29 | /** @var MessageFactoryInterface Request factory used by the client */ |
||
30 | private $messageFactory; |
||
31 | |||
32 | /** @var AdapterInterface */ |
||
33 | private $adapter; |
||
34 | |||
35 | /** @var ParallelAdapterInterface */ |
||
36 | private $parallelAdapter; |
||
37 | |||
38 | /** @var Url Base URL of the client */ |
||
39 | private $baseUrl; |
||
40 | |||
41 | /** @var array Default request options */ |
||
42 | private $defaults; |
||
43 | |||
44 | /** |
||
45 | * Clients accept an array of constructor parameters. |
||
46 | * |
||
47 | * Here's an example of creating a client using an URI template for the |
||
48 | * client's base_url and an array of default request options to apply |
||
49 | * to each request: |
||
50 | * |
||
51 | * $client = new Client([ |
||
52 | * 'base_url' => [ |
||
53 | * 'http://www.foo.com/{version}/', |
||
54 | * ['version' => '123'] |
||
55 | * ], |
||
56 | * 'defaults' => [ |
||
57 | * 'timeout' => 10, |
||
58 | * 'allow_redirects' => false, |
||
59 | * 'proxy' => '192.168.16.1:10' |
||
60 | * ] |
||
61 | * ]); |
||
62 | * |
||
63 | * @param array $config Client configuration settings |
||
64 | * - base_url: Base URL of the client that is merged into relative URLs. |
||
65 | * Can be a string or an array that contains a URI template followed |
||
66 | * by an associative array of expansion variables to inject into the |
||
67 | * URI template. |
||
68 | * - adapter: Adapter used to transfer requests |
||
69 | * - parallel_adapter: Adapter used to transfer requests in parallel |
||
70 | * - message_factory: Factory used to create request and response object |
||
71 | * - defaults: Default request options to apply to each request |
||
72 | * - emitter: Event emitter used for request events |
||
73 | */ |
||
74 | public function __construct(array $config = []) |
||
75 | { |
||
76 | $this->configureBaseUrl($config); |
||
77 | $this->configureDefaults($config); |
||
78 | $this->configureAdapter($config); |
||
79 | if (isset($config['emitter'])) { |
||
80 | $this->emitter = $config['emitter']; |
||
81 | } |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Get the default User-Agent string to use with Guzzle |
||
86 | * |
||
87 | * @return string |
||
88 | */ |
||
89 | public static function getDefaultUserAgent() |
||
90 | { |
||
91 | static $defaultAgent = ''; |
||
92 | if (!$defaultAgent) { |
||
93 | $defaultAgent = 'Guzzle/' . self::VERSION; |
||
94 | if (extension_loaded('curl')) { |
||
95 | $defaultAgent .= ' curl/' . curl_version()['version']; |
||
96 | } |
||
97 | $defaultAgent .= ' PHP/' . PHP_VERSION; |
||
98 | } |
||
99 | |||
100 | return $defaultAgent; |
||
101 | } |
||
102 | |||
103 | public function __call($name, $arguments) |
||
104 | { |
||
105 | return \GuzzleHttp\deprecation_proxy( |
||
106 | $this, |
||
107 | $name, |
||
108 | $arguments, |
||
109 | ['getEventDispatcher' => 'getEmitter'] |
||
110 | ); |
||
111 | } |
||
112 | |||
113 | public function getDefaultOption($keyOrPath = null) |
||
114 | { |
||
115 | return $keyOrPath === null |
||
116 | ? $this->defaults |
||
117 | : \GuzzleHttp\get_path($this->defaults, $keyOrPath); |
||
118 | } |
||
119 | |||
120 | public function setDefaultOption($keyOrPath, $value) |
||
121 | { |
||
122 | \GuzzleHttp\set_path($this->defaults, $keyOrPath, $value); |
||
123 | } |
||
124 | |||
125 | public function getBaseUrl() |
||
126 | { |
||
127 | return (string) $this->baseUrl; |
||
128 | } |
||
129 | |||
130 | public function createRequest($method, $url = null, array $options = []) |
||
131 | { |
||
132 | $headers = $this->mergeDefaults($options); |
||
133 | // Use a clone of the client's emitter |
||
134 | $options['config']['emitter'] = clone $this->getEmitter(); |
||
135 | |||
136 | $request = $this->messageFactory->createRequest( |
||
137 | $method, |
||
138 | $url ? (string) $this->buildUrl($url) : (string) $this->baseUrl, |
||
139 | $options |
||
140 | ); |
||
141 | |||
142 | // Merge in default headers |
||
143 | if ($headers) { |
||
144 | foreach ($headers as $key => $value) { |
||
145 | if (!$request->hasHeader($key)) { |
||
146 | $request->setHeader($key, $value); |
||
147 | } |
||
148 | } |
||
149 | } |
||
150 | |||
151 | return $request; |
||
152 | } |
||
153 | |||
154 | public function get($url = null, $options = []) |
||
155 | { |
||
156 | return $this->send($this->createRequest('GET', $url, $options)); |
||
157 | } |
||
158 | |||
159 | public function head($url = null, array $options = []) |
||
160 | { |
||
161 | return $this->send($this->createRequest('HEAD', $url, $options)); |
||
162 | } |
||
163 | |||
164 | public function delete($url = null, array $options = []) |
||
165 | { |
||
166 | return $this->send($this->createRequest('DELETE', $url, $options)); |
||
167 | } |
||
168 | |||
169 | public function put($url = null, array $options = []) |
||
170 | { |
||
171 | return $this->send($this->createRequest('PUT', $url, $options)); |
||
172 | } |
||
173 | |||
174 | public function patch($url = null, array $options = []) |
||
175 | { |
||
176 | return $this->send($this->createRequest('PATCH', $url, $options)); |
||
177 | } |
||
178 | |||
179 | public function post($url = null, array $options = []) |
||
180 | { |
||
181 | return $this->send($this->createRequest('POST', $url, $options)); |
||
182 | } |
||
183 | |||
184 | public function options($url = null, array $options = []) |
||
185 | { |
||
186 | return $this->send($this->createRequest('OPTIONS', $url, $options)); |
||
187 | } |
||
188 | |||
189 | public function send(RequestInterface $request) |
||
190 | { |
||
191 | $transaction = new Transaction($this, $request); |
||
192 | try { |
||
193 | if ($response = $this->adapter->send($transaction)) { |
||
194 | return $response; |
||
195 | } |
||
196 | throw new \LogicException('No response was associated with the transaction'); |
||
197 | } catch (RequestException $e) { |
||
198 | throw $e; |
||
199 | } catch (\Exception $e) { |
||
200 | // Wrap exceptions in a RequestException to adhere to the interface |
||
201 | throw new RequestException($e->getMessage(), $request, null, $e); |
||
202 | } |
||
203 | } |
||
204 | |||
205 | public function sendAll($requests, array $options = []) |
||
206 | { |
||
207 | if (!($requests instanceof TransactionIterator)) { |
||
208 | $requests = new TransactionIterator($requests, $this, $options); |
||
209 | } |
||
210 | |||
211 | $this->parallelAdapter->sendAll( |
||
212 | $requests, |
||
213 | isset($options['parallel']) |
||
214 | ? $options['parallel'] |
||
215 | : self::DEFAULT_CONCURRENCY |
||
216 | ); |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * Get an array of default options to apply to the client |
||
221 | * |
||
222 | * @return array |
||
223 | */ |
||
224 | protected function getDefaultOptions() |
||
225 | { |
||
226 | $settings = [ |
||
227 | 'allow_redirects' => true, |
||
228 | 'exceptions' => true, |
||
229 | 'decode_content' => true, |
||
230 | 'verify' => __DIR__ . '/cacert.pem' |
||
231 | ]; |
||
232 | |||
233 | // Use the bundled cacert if it is a regular file, or set to true if |
||
234 | // using a phar file (because curL and the stream wrapper can't read |
||
235 | // cacerts from the phar stream wrapper). Favor the ini setting over |
||
236 | // the system's cacert. |
||
237 | if (substr(__FILE__, 0, 7) == 'phar://') { |
||
238 | $settings['verify'] = ini_get('openssl.cafile') ?: true; |
||
239 | } |
||
240 | |||
241 | // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. |
||
242 | // We can only trust the HTTP_PROXY environment variable in a CLI |
||
243 | // process due to the fact that PHP has no reliable mechanism to |
||
244 | // get environment variables that start with "HTTP_". |
||
245 | if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) { |
||
246 | $settings['proxy']['http'] = getenv('HTTP_PROXY'); |
||
247 | } |
||
248 | |||
249 | if ($proxy = getenv('HTTPS_PROXY')) { |
||
250 | $settings['proxy']['https'] = $proxy; |
||
251 | } |
||
252 | |||
253 | return $settings; |
||
254 | } |
||
255 | |||
256 | /** |
||
257 | * Expand a URI template and inherit from the base URL if it's relative |
||
258 | * |
||
259 | * @param string|array $url URL or URI template to expand |
||
260 | * |
||
261 | * @return string |
||
262 | */ |
||
263 | private function buildUrl($url) |
||
264 | { |
||
265 | if (!is_array($url)) { |
||
266 | if (strpos($url, '://')) { |
||
267 | return (string) $url; |
||
268 | } |
||
269 | return (string) $this->baseUrl->combine($url); |
||
270 | } elseif (strpos($url[0], '://')) { |
||
271 | return \GuzzleHttp\uri_template($url[0], $url[1]); |
||
272 | } |
||
273 | |||
274 | return (string) $this->baseUrl->combine( |
||
275 | \GuzzleHttp\uri_template($url[0], $url[1]) |
||
276 | ); |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * Get a default parallel adapter to use based on the environment |
||
281 | * |
||
282 | * @return ParallelAdapterInterface |
||
283 | */ |
||
284 | private function getDefaultParallelAdapter() |
||
285 | { |
||
286 | return extension_loaded('curl') |
||
287 | ? new MultiAdapter($this->messageFactory) |
||
288 | : new FakeParallelAdapter($this->adapter); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Create a default adapter to use based on the environment |
||
293 | * @throws \RuntimeException |
||
294 | */ |
||
295 | private function getDefaultAdapter() |
||
296 | { |
||
297 | if (extension_loaded('curl')) { |
||
298 | $this->parallelAdapter = new MultiAdapter($this->messageFactory); |
||
299 | $this->adapter = function_exists('curl_reset') |
||
300 | ? new CurlAdapter($this->messageFactory) |
||
301 | : $this->parallelAdapter; |
||
302 | if (ini_get('allow_url_fopen')) { |
||
303 | $this->adapter = new StreamingProxyAdapter( |
||
304 | $this->adapter, |
||
305 | new StreamAdapter($this->messageFactory) |
||
306 | ); |
||
307 | } |
||
308 | } elseif (ini_get('allow_url_fopen')) { |
||
309 | $this->adapter = new StreamAdapter($this->messageFactory); |
||
310 | } else { |
||
311 | throw new \RuntimeException('Guzzle requires cURL, the ' |
||
312 | . 'allow_url_fopen ini setting, or a custom HTTP adapter.'); |
||
313 | } |
||
314 | } |
||
315 | |||
316 | private function configureBaseUrl(&$config) |
||
317 | { |
||
318 | if (!isset($config['base_url'])) { |
||
319 | $this->baseUrl = new Url('', ''); |
||
320 | } elseif (is_array($config['base_url'])) { |
||
321 | $this->baseUrl = Url::fromString( |
||
322 | \GuzzleHttp\uri_template( |
||
323 | $config['base_url'][0], |
||
324 | $config['base_url'][1] |
||
325 | ) |
||
326 | ); |
||
327 | $config['base_url'] = (string) $this->baseUrl; |
||
328 | } else { |
||
329 | $this->baseUrl = Url::fromString($config['base_url']); |
||
330 | } |
||
331 | } |
||
332 | |||
333 | private function configureDefaults($config) |
||
334 | { |
||
335 | if (!isset($config['defaults'])) { |
||
336 | $this->defaults = $this->getDefaultOptions(); |
||
337 | } else { |
||
338 | $this->defaults = array_replace( |
||
339 | $this->getDefaultOptions(), |
||
340 | $config['defaults'] |
||
341 | ); |
||
342 | } |
||
343 | |||
344 | // Add the default user-agent header |
||
345 | if (!isset($this->defaults['headers'])) { |
||
346 | $this->defaults['headers'] = [ |
||
347 | 'User-Agent' => static::getDefaultUserAgent() |
||
348 | ]; |
||
349 | } elseif (!isset(array_change_key_case($this->defaults['headers'])['user-agent'])) { |
||
350 | // Add the User-Agent header if one was not already set |
||
351 | $this->defaults['headers']['User-Agent'] = static::getDefaultUserAgent(); |
||
352 | } |
||
353 | } |
||
354 | |||
355 | private function configureAdapter(&$config) |
||
356 | { |
||
357 | if (isset($config['message_factory'])) { |
||
358 | $this->messageFactory = $config['message_factory']; |
||
359 | } else { |
||
360 | $this->messageFactory = new MessageFactory(); |
||
361 | } |
||
362 | if (isset($config['adapter'])) { |
||
363 | $this->adapter = $config['adapter']; |
||
364 | } else { |
||
365 | $this->getDefaultAdapter(); |
||
366 | } |
||
367 | // If no parallel adapter was explicitly provided and one was not |
||
368 | // defaulted when creating the default adapter, then create one now. |
||
369 | if (isset($config['parallel_adapter'])) { |
||
370 | $this->parallelAdapter = $config['parallel_adapter']; |
||
371 | } elseif (!$this->parallelAdapter) { |
||
372 | $this->parallelAdapter = $this->getDefaultParallelAdapter(); |
||
373 | } |
||
374 | } |
||
375 | |||
376 | /** |
||
377 | * Merges default options into the array passed by reference and returns |
||
378 | * an array of headers that need to be merged in after the request is |
||
379 | * created. |
||
380 | * |
||
381 | * @param array $options Options to modify by reference |
||
382 | * |
||
383 | * @return array|null |
||
384 | */ |
||
385 | private function mergeDefaults(&$options) |
||
386 | { |
||
387 | // Merging optimization for when no headers are present |
||
388 | if (!isset($options['headers']) |
||
389 | || !isset($this->defaults['headers'])) { |
||
390 | $options = array_replace_recursive($this->defaults, $options); |
||
391 | return null; |
||
392 | } |
||
393 | |||
394 | $defaults = $this->defaults; |
||
395 | unset($defaults['headers']); |
||
396 | $options = array_replace_recursive($defaults, $options); |
||
397 | |||
398 | return $this->defaults['headers']; |
||
399 | } |
||
400 | } |