Issues (120)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Core/Client/ClientFactory.php (7 issues)

1
<?php
2
3
namespace Commercetools\Core\Client;
4
5
use Commercetools\Core\Cache\CacheAdapterFactory;
6
use Commercetools\Core\Client\OAuth\CacheTokenProvider;
7
use Commercetools\Core\Client\OAuth\ClientCredentials;
8
use Commercetools\Core\Client\OAuth\CredentialTokenProvider;
9
use Commercetools\Core\Client\OAuth\OAuth2Handler;
10
use Commercetools\Core\Client\OAuth\TokenProvider;
11
use Commercetools\Core\Config;
12
use Commercetools\Core\Error\ApiException;
13
use Commercetools\Core\Error\DeprecatedException;
14
use Commercetools\Core\Error\InvalidTokenException;
15
use Commercetools\Core\Error\Message;
16
use Commercetools\Core\Helper\CorrelationIdProvider;
17
use Commercetools\Core\Error\InvalidArgumentException;
18
use Commercetools\Core\Model\Common\Context;
19
use Commercetools\Core\Model\Common\ContextAwareInterface;
20
use Commercetools\Core\Response\AbstractApiResponse;
21
use GuzzleHttp\Client;
22
use GuzzleHttp\Client as HttpClient;
23
use GuzzleHttp\Exception\RequestException;
24
use GuzzleHttp\HandlerStack;
25
use GuzzleHttp\MessageFormatter;
26
use GuzzleHttp\Middleware;
27
use Psr\Cache\CacheItemPoolInterface;
28
use Psr\Http\Message\RequestInterface;
29
use Psr\Http\Message\ResponseInterface;
30
use Psr\Log\LoggerInterface;
31
use Psr\Log\LogLevel;
32
use Psr\SimpleCache\CacheInterface;
33
34
/**
35
 * The factory to create a Client for communicating with the commercetools platform
36
 *
37
 * @description
38
 * This factory will create a Guzzle HTTP Client preconfigured for talking to the commercetools platform
39
 *
40
 * ## Instantiation
41
 *
42
 * ```php
43
 * $config = Config::fromArray(
44
 *  ['client_id' => '<client_id>', 'client_secret' => '<client_secret>', 'project' => '<project>']
45
 * );
46
 * $client = ClientFactory::of()->createClient($config);
47
 * ```
48
 *
49
 * ## Execution
50
 *
51
 * ### Synchronous
52
 *
53
 * ```php
54
 * $request = ProductProjectionSearchRequest::of();
55
 * $response = $client->execute($request);
56
 * $products = $request->mapFromResponse($response);
57
 * ```
58
 *
59
 * ### Asynchronous
60
 * The asynchronous execution will return a promise to fulfill the request.
61
 *
62
 * ```php
63
 * $request = ProductProjectionSearchRequest::of();
64
 * $response = $client->executeAsync($request);
65
 * $products = $request->mapFromResponse($response->wait());
66
 * ```
67
 *
68
 * ### Batch
69
 * By filling the batch queue and starting the execution all requests will be executed in parallel.
70
 *
71
 * ```php
72
 * $responses = Pool::batch(
73
 *     $client,
74
 *     [ProductProjectionSearchRequest::of()->httpRequest(), CartByIdGetRequest::ofId($cartId)->httpRequest()]
75
 * );
76
 * ```
77
 *
78
 * ## Instantiation options
79
 *
80
 * ### Using a logger
81
 *
82
 * The client uses the PSR-3 logger interface for logging requests and deprecation notices. To enable
83
 * logging provide a PSR-3 compliant logger (e.g. Monolog).
84
 *
85
 * ```php
86
 * $logger = new \Monolog\Logger('name');
87
 * $logger->pushHandler(new StreamHandler('./requests.log'));
88
 * $client = ClientFactory::of()->createClient($config, $logger);
89
 * ```
90
 *
91
 * ### Using a cache adapter ###
92
 *
93
 * The client will automatically request an OAuth token and store the token in the provided cache.
94
 *
95
 * It's also possible to use a different cache adapter. The SDK provides a Doctrine, a Redis and an APCu cache adapter.
96
 * By default the SDK tries to instantiate the APCu or a PSR-6 filesystem cache adapter if there is no cache given.
97
 * E.g. Redis:
98
 *
99
 * ```php
100
 * $redis = new \Redis();
101
 * $redis->connect('localhost');
102
 * $client = ClientFactory::of()->createClient($config, null, $cache);
103
 * ```
104
 *
105
 * #### Using cache and logger ####
106
 *
107
 * ```php
108
 * $client = ClientFactory::of()->createClient($config, $logger, $cache);
109
 * ```
110
 *
111
 * #### Using a custom cache adapter ####
112
 *
113
 * ```php
114
 * class <CacheClass>Adapter implements \Psr\Cache\CacheItemPoolInterface {
115
 *     protected $cache;
116
 *     public function __construct(<CacheClass> $cache) {
117
 *         $this->cache = $cache;
118
 *     }
119
 * }
120
 *
121
 * $client->getAdapterFactory()->registerCallback(function ($cache) {
122
 *     if ($cache instanceof <CacheClass>) {
123
 *         return new <CacheClass>Adapter($cache);
124
 *     }
125
 *     return null;
126
 * });
127
 * ```
128
 *
129
 * ### Using a custom client class
130
 *
131
 * If some additional configuration is needed or the client should have custom logic you could provide a class name
132
 * to be used for the client instance. This class has to be an extended Guzzle client.
133
 *
134
 * ```php
135
 * $client = ClientFactory::of()->createCustomClient(MyCustomClient::class, $config);
136
 * ```
137
 *
138
 * ## Middlewares
139
 *
140
 * Adding middlewares to the clients for platform as well for the authentication can be done using the config
141
 * by setting client options.
142
 *
143
 * ### Using a HandlerStack
144
 *
145
 * ```php
146
 * $handler = HandlerStack::create();
147
 * $handler->push(Middleware::mapRequest(function (RequestInterface $request) {
148
 *     ...
149
 *     return $request; })
150
 * );
151
 * $config = Config::of()->setClientOptions(['handler' => $handler])
152
 * ```
153
 *
154
 * ### Using a middleware array
155
 *
156
 * ```php
157
 * $middlewares = [
158
 *     Middleware::mapRequest(function (RequestInterface $request) {
159
 *     ...
160
 *     return $request; }),
161
 *     ...
162
 * ]
163
 * $config = Config::of()->setClientOptions(['middlewares' => $middlewares])
164
 * ```
165
 *
166
 * ### Timeouts
167
 *
168
 * The clients are configured to timeout by default after 60 seconds. This can be changed by setting the client options in the Config instance
169
 *
170
 * ```php
171
 * $config = Config::of()->setClientOptions([
172
 *     'defaults' => [
173
 *         'timeout' => 10
174
 *     ]
175
 * ])
176
 * ```
177
 *
178
 * Another option is to specify the timeout per request
179 28
 *
180
 * ```php
181 28
 * $request = ProductProjectionSearchRequest::of();
182
 * $response = $client->execute($request, null, ['timeout' => 10]);
183
 * ```
184 28
 *
185
 * ### Retrying
186
 *
187
 * As a request can error in multiple ways it's possible to add a retry middleware to the client config. E.g.: Retrying in case of service unavailable errors
188
 *
189
 * ```php
190
 * $config = Config::of()->setClientOptions([
191
 *     'defaults' => [
192
 *         'timeout' => 10
193
 *     ]
194
 * ])
195
 * $maxRetries = 3;
196 27
 * $clientOptions = [
197
 *     'middlewares' => [
198
 *         'retry' => Middleware::retry(
199
 *             function ($retries, RequestInterface $request, ResponseInterface $response = null, $error = null) use ($maxRetries) {
200
 *                 if ($response instanceof ResponseInterface && $response->getStatusCode() < 500) {
201
 *                     return false;
202
 *                 }
203
 *                 if ($retries > $maxRetries) {
204
 *                     return false;
205 27
 *                 }
206
 *                 if ($error instanceof ServiceUnavailableException) {
207 27
 *                     return true;
208 27
 *                 }
209
 *                 if ($error instanceof ServerException && $error->getCode() == 503) {
210 27
 *                     return true;
211 27
 *                 }
212 27
 *                 if ($response instanceof ResponseInterface && $response->getStatusCode() == 503) {
213 27
 *                     return true;
214
 *                 }
215
 *                 return false;
216 27
 *             },
217 27
 *             [RetryMiddleware::class, 'exponentialDelay']
218
 *         )
219
 *     ]
220
 * ];
221 27
 * $config->setClientOptions($clientOptions);
222 27
 * ```
223 27
 * @package Commercetools\Core\Client
224 27
 */
225
class ClientFactory
226
{
227 27
    /**
228 27
     * @var bool
229
     */
230
    private static $isGuzzle6;
231 27
232
    /**
233 27
     * ClientFactory constructor.
234 27
     * @throws DeprecatedException
235
     */
236
    public function __construct()
237
    {
238 27
        if (!self::isGuzzle6()) {
239
            throw new DeprecatedException("ClientFactory doesn't support Guzzle version < 6");
240
        }
241 27
    }
242 26
243
    /**
244 27
     * @param string $clientClass
245
     * @param Config|array $config
246
     * @param LoggerInterface $logger
247
     * @param CacheItemPoolInterface|CacheInterface $cache
248
     * @param TokenProvider $provider
249
     * @param CacheAdapterFactory $cacheAdapterFactory
250
     * @param Context|null $context
251
     * @return Client
252
     */
253
    public function createCustomClient(
254
        $clientClass,
255
        $config,
256 25
        LoggerInterface $logger = null,
257
        $cache = null,
258
        TokenProvider $provider = null,
259
        CacheAdapterFactory $cacheAdapterFactory = null,
260
        Context $context = null
261
    ) {
262
        $config = $this->createConfig($config);
263
264 25
        if (is_null($context)) {
265 25
            $context = $config->getContext();
266
        }
267
        if (is_null($cacheAdapterFactory)) {
268
            $cacheDir = $config->getCacheDir();
269
            $cacheDir = !is_null($cacheDir) ? $cacheDir : realpath(__DIR__ . '/../../..');
0 ignored issues
show
The condition is_null($cacheDir) is always false.
Loading history...
270
            $cacheAdapterFactory = new CacheAdapterFactory($cacheDir);
271
        }
272
273
        $cache = $cacheAdapterFactory->get($cache);
274
        if (is_null($cache)) {
275 14
            throw new InvalidArgumentException(Message::INVALID_CACHE_ADAPTER);
276
        }
277
278
        $credentials = $config->getClientCredentials();
279 14
        $oauthHandler = $this->getHandler(
280
            $credentials,
281
            $config->getOauthUrl(),
282 14
            $cache,
283 14
            $provider,
284
            $config->getOAuthClientOptions(),
285
            $config->getCorrelationIdProvider()
286 14
        );
287 14
288
        $options = $this->getDefaultOptions($config);
289 14
290
        $client = $this->createGuzzle6Client(
291 14
            $clientClass,
292
            $options,
293 14
            $oauthHandler,
294
            $logger,
295
            $config->getCorrelationIdProvider()
296 27
        );
297
298 27
        if ($client instanceof ContextAwareInterface) {
299 27
            $client->setContext($context);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Client::__call() has been deprecated: Client::__call will be removed in guzzlehttp/guzzle:8.0. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

299
            /** @scrutinizer ignore-deprecated */ $client->setContext($context);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
300 27
        }
301
        return $client;
302 27
    }
303
304 27
    /**
305 27
     * @param Config|array $config
306
     * @param LoggerInterface $logger
307 27
     * @param CacheItemPoolInterface|CacheInterface $cache
308
     * @param TokenProvider $provider
309 27
     * @param CacheAdapterFactory $cacheAdapterFactory
310
     * @param Context|null $context
311
     * @return ApiClient
312
     */
313
    public function createClient(
314
        $config,
315
        LoggerInterface $logger = null,
316
        $cache = null,
317 27
        TokenProvider $provider = null,
318
        CacheAdapterFactory $cacheAdapterFactory = null,
319 27
        Context $context = null
320 27
    ) {
321
        return $this->createCustomClient(
322
            ApiClient::class,
323
            $config,
324
            $logger,
325
            $cache,
326
            $provider,
327
            $cacheAdapterFactory,
328
            $context
329
        );
330
    }
331
332
    public function createAuthClient(
333
        array $options = [],
334
        CorrelationIdProvider $correlationIdProvider = null
335
    ) {
336 27
        if (isset($options['handler']) && $options['handler'] instanceof HandlerStack) {
337
            $handler = $options['handler'];
338
        } else {
339
            $handler = HandlerStack::create();
340
            $options['handler'] = $handler;
341
        }
342
343 27
        $handler->remove("http_errors");
344
        $handler->unshift(self::httpErrors(), 'ctp_http_errors');
345
346 27
        $this->setCorrelationIdMiddleware($handler, $correlationIdProvider);
347 27
348
        $options = $this->addMiddlewares($handler, $options);
349
350 27
        return new Client($options);
351
    }
352 27
353
    private function getDefaultOptions(Config $config)
354
    {
355
        $options = $config->getClientOptions();
356
        $options['http_errors'] = $config->getThrowExceptions();
357
        $options['base_uri'] = $config->getApiUrl() . "/" . $config->getProject() . "/";
358
        $defaultHeaders = [
359
            'User-Agent' => (new UserAgentProvider())->getUserAgent()
360
        ];
361 27
        if (!is_null($config->getAcceptEncoding())) {
0 ignored issues
show
The condition is_null($config->getAcceptEncoding()) is always false.
Loading history...
362 23
            $defaultHeaders['Accept-Encoding'] = $config->getAcceptEncoding();
363
        }
364
        $options['headers'] = array_merge($defaultHeaders, (isset($options['headers']) ? $options['headers'] : []));
365 27
366 27
        return $options;
367
    }
368 27
369 27
    /**
370 27
     * @param Config|array $config
371
     * @return Config
372 27
     * @throws InvalidArgumentException
373 26
     */
374 26
    private function createConfig($config)
375 26
    {
376
        if ($config instanceof Config) {
377
            return $config;
378
        }
379 27
        if (is_array($config)) {
0 ignored issues
show
The condition is_array($config) is always true.
Loading history...
380 27
            return Config::fromArray($config);
381
        }
382 27
        throw new InvalidArgumentException();
383
    }
384 27
385
    /**
386
     * @param string $clientClass
387 23
     * @param array $options
388
     * @param OAuth2Handler $oauthHandler
389
     * @param LoggerInterface|null $logger
390
     * @param CorrelationIdProvider|null $correlationIdProvider
391
     * @return Client
392
     */
393 23
    private function createGuzzle6Client(
394 23
        $clientClass,
395
        array $options,
396 23
        OAuth2Handler $oauthHandler,
397 23
        LoggerInterface $logger = null,
398
        CorrelationIdProvider $correlationIdProvider = null
399
    ) {
400
        if (isset($options['handler']) && $options['handler'] instanceof HandlerStack) {
401
            $handler = $options['handler'];
402
        } else {
403 704
            $handler = HandlerStack::create();
404
            $options['handler'] = $handler;
405
        }
406
407 27
        $options = array_merge(
408 704
            [
409 702
                'allow_redirects' => false,
410 702
                'verify' => true,
411 702
                'timeout' => 60,
412
                'connect_timeout' => 10,
413 27
                'pool_size' => 25,
414
            ],
415 27
            $options
416
        );
417
418
        if (!is_null($logger)) {
419
            $this->setLogger($handler, $logger);
420
        }
421
422 27
        $handler->remove("http_errors");
423
        $handler->unshift(self::httpErrors(), 'ctp_http_errors');
424 27
425 4
        $handler->push(
426 4
            Middleware::mapRequest($oauthHandler),
427 4
            'oauth_2_0'
428 4
        );
429 4
        if ($oauthHandler->refreshable()) {
430
            $handler->push(
431
                self::reauthenticate($oauthHandler),
432
                'reauthenticate'
433
            );
434
        }
435
436 27
        $this->setCorrelationIdMiddleware($handler, $correlationIdProvider);
437
        $options = $this->addMiddlewares($handler, $options);
438
439
        $client = new $clientClass($options);
440
441
        return $client;
442
    }
443
444
    private function setLogger(
445
        HandlerStack $handler,
446 701
        LoggerInterface $logger,
447
        $logLevel = LogLevel::INFO,
448 26
        $formatter = null
449 701
    ) {
450 700
        if (is_null($formatter)) {
451 700
            $formatter = new MessageFormatter();
452 700
        }
453 700
        $handler->push(self::log($logger, $formatter, $logLevel), 'ctp_logger');
454 700
    }
455 700
456 700
    /**
457
     * @param CorrelationIdProvider $correlationIdProvider
458 700
     * @param HandlerStack $handler
459 1
     */
460 1
    private function setCorrelationIdMiddleware(
461
        HandlerStack $handler,
462 1
        CorrelationIdProvider $correlationIdProvider = null
463 1
    ) {
464 1
        if (!is_null($correlationIdProvider)) {
465 1
            $handler->push(Middleware::mapRequest(function (RequestInterface $request) use ($correlationIdProvider) {
466 1
                return $request->withAddedHeader(
467 1
                    AbstractApiResponse::X_CORRELATION_ID,
468 1
                    $correlationIdProvider->getCorrelationId()
469
                );
470 1
            }), 'ctp_correlation_id');
471
        }
472
    }
473 699
474 700
    /**
475
     * @param HandlerStack $handler
476 24
     * @param array $options
477 26
     * @return array
478
     */
479
    private function addMiddlewares(HandlerStack $handler, array $options)
480
    {
481
        if (isset($options['middlewares']) && is_array($options['middlewares'])) {
482
            foreach ($options['middlewares'] as $key => $middleware) {
483
                if (is_callable($middleware)) {
484
                    if (!is_numeric($key)) {
485
                        $handler->remove($key);
486 702
                        $handler->push($middleware, $key);
487
                    } else {
488 27
                        $handler->push($middleware);
489 702
                    }
490 702
                }
491 20
            }
492
        }
493 684
        return $options;
494 684
    }
495 684
496 684
    /**
497 674
     * Middleware that reauthenticates on invalid token error
498
     *
499 214
     * @param OAuth2Handler $oauthHandler
500 684
     * @param int $maxRetries
501
     * @return callable Returns a function that accepts the next handler.
502 25
     */
503 27
    public static function reauthenticate(OAuth2Handler $oauthHandler, $maxRetries = 1)
504
    {
505
        return function (callable $handler) use ($oauthHandler, $maxRetries) {
506
            return function (RequestInterface $request, array $options) use ($handler, $oauthHandler, $maxRetries) {
507
                return $handler($request, $options)->then(
508
                    function (ResponseInterface $response) use (
509
                        $request,
510
                        $handler,
511
                        $oauthHandler,
512
                        $options,
513
                        $maxRetries
514
                    ) {
515 27
                        if ($response->getStatusCode() == 401) {
516
                            if (!isset($options['reauth'])) {
517
                                $options['reauth'] = 0;
518
                            }
519
                            $exception = ApiException::create($request, $response);
520
                            if ($options['reauth'] < $maxRetries && $exception instanceof InvalidTokenException) {
521
                                $options['reauth']++;
522
                                $token = $oauthHandler->refreshToken();
523 27
                                $request = $request->withHeader(
524 14
                                    'Authorization',
525 14
                                    'Bearer ' . $token->getToken()
526
                                );
527
                                return $handler($request, $options);
528
                            }
529 14
                        }
530 14
                        return $response;
531
                    }
532 27
                );
533
            };
534
        };
535
    }
536
537
    /**
538
     * Middleware that throws exceptions for 4xx or 5xx responses when the
539
     * "http_error" request option is set to true.
540
     *
541
     * @return callable Returns a function that accepts the next handler.
542
     */
543
    public static function httpErrors()
544
    {
545 700
        return function (callable $handler) {
546
            return function ($request, array $options) use ($handler) {
547 23
                if (empty($options['http_errors'])) {
548 700
                    return $handler($request, $options);
549 700
                }
550 700
                return $handler($request, $options)->then(
551 700
                    function (ResponseInterface $response) use ($request, $handler) {
0 ignored issues
show
The import $handler is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
552
                        $code = $response->getStatusCode();
553 700
                        if ($code < 400) {
554 700
                            return $response;
555
                        }
556
                        throw ApiException::create($request, $response);
557 700
                    }
558 700
                );
559 700
            };
560 700
        };
561
    }
562
563
    /**
564
     * @param ClientCredentials $credentials
565
     * @param string $accessTokenUrl
566
     * @param CacheItemPoolInterface|CacheInterface $cache
567
     * @param TokenProvider $provider
568
     * @param array $authClientOptions
569
     * @param CorrelationIdProvider|null $correlationIdProvider
570
     * @return OAuth2Handler
571
     */
572
    private function getHandler(
573
        ClientCredentials $credentials,
574 700
        $accessTokenUrl,
575
        $cache,
576 23
        TokenProvider $provider = null,
577 23
        array $authClientOptions = [],
578
        CorrelationIdProvider $correlationIdProvider = null
579
    ) {
580
        if (is_null($provider)) {
581
            $provider = new CredentialTokenProvider(
582
                $this->createAuthClient($authClientOptions, $correlationIdProvider),
583 28
                $accessTokenUrl,
584
                $credentials
585 28
            );
586 1
            $cacheKey = sha1($credentials->getClientId() . $credentials->getScope());
587 1
            $provider = new CacheTokenProvider($provider, $cache, $cacheKey);
588
        }
589
        return new OAuth2Handler($provider);
590
    }
591 1
592 1
    /**
593
     * Middleware that logs requests, responses, and errors using a message
594
     * formatter.
595
     *
596
     * @param LoggerInterface  $logger Logs messages.
597 28
     * @param MessageFormatter $formatter Formatter used to create message strings.
598
     * @param string           $logLevel Level at which to log requests.
599
     *
600
     * @return callable Returns a function that accepts the next handler.
601
     */
602
    private static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
603 28
    {
604
        return function (callable $handler) use ($logger, $formatter, $logLevel) {
605 28
            return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
606
                return $handler($request, $options)->then(
607
                    function (ResponseInterface $response) use ($logger, $request, $formatter, $logLevel) {
608
                        $message = $formatter->format($request, $response);
609
                        $context = [
610
                            AbstractApiResponse::X_CORRELATION_ID => $response->getHeader(
611
                                AbstractApiResponse::X_CORRELATION_ID
612
                            )
613
                        ];
614
                        $logger->log($logLevel, $message, $context);
615
                        return $response;
616
                    },
617
                    function ($reason) use ($logger, $request, $formatter) {
618
                        $response = null;
619
                        $context = [];
620
                        if ($reason instanceof RequestException) {
621
                            $response = $reason->getResponse();
622
                            if (!is_null($response)) {
623
                                $context[AbstractApiResponse::X_CORRELATION_ID] = $response->getHeader(
624
                                    AbstractApiResponse::X_CORRELATION_ID
625
                                );
626
                            }
627
                        }
628
                        $message = $formatter->format($request, $response, $reason);
629
                        $logger->notice($message, $context);
630
                        return \GuzzleHttp\Promise\rejection_for($reason);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Promise\rejection_for() has been deprecated: rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

630
                        return /** @scrutinizer ignore-deprecated */ \GuzzleHttp\Promise\rejection_for($reason);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
631
                    }
632
                );
633
            };
634
        };
635
    }
636
637
    /**
638
     * @return bool
639
     */
640
    private static function isGuzzle6()
641
    {
642
        if (is_null(self::$isGuzzle6)) {
0 ignored issues
show
The condition is_null(self::isGuzzle6) is always false.
Loading history...
643
            if (defined('\GuzzleHttp\Client::MAJOR_VERSION')) {
644
                $clientVersion = (string) constant(HttpClient::class . '::MAJOR_VERSION');
645
            } else {
646
                $clientVersion = (string) constant(HttpClient::class . '::VERSION');
647
            }
648
            if (version_compare($clientVersion, '6.0.0', '>=')) {
649
                self::$isGuzzle6 = true;
650
            } else {
651
                self::$isGuzzle6 = false;
652
            }
653
        }
654
        return self::$isGuzzle6;
655
    }
656
657
    /**
658
     * @return ClientFactory
659
     */
660
    public static function of()
661
    {
662
        return new static();
663
    }
664
}
665