Completed
Push — scrutinizer_segfault ( 3cb465...950f93 )
by Jens
07:28
created

Client::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1
Metric Value
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 3
crap 1
1
<?php
2
/**
3
 * @author @jayS-de <[email protected]>
4
 * @created 19.01.15, 14:29
5
 */
6
7
namespace Commercetools\Core;
8
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Log\LoggerInterface;
12
use Commercetools\Core\Client\Adapter\AdapterInterface;
13
use Commercetools\Core\Error\InvalidTokenException;
14
use Commercetools\Core\Error\Message;
15
use Commercetools\Core\Error\ApiException;
16
use Commercetools\Core\Model\Common\ContextAwareInterface;
17
use Commercetools\Core\Response\ApiResponseInterface;
18
use Commercetools\Core\Request\ClientRequestInterface;
19
use Commercetools\Core\Client\OAuth\Manager;
20
21
/**
22
 * The client for communicating with the commercetools platform
23
 *
24
 * @description
25
 * ## Instantiation
26
 *
27
 * ```php
28
 * $config = Config::fromArray(
29
 *  ['client_id' => '<client_id>', 'client_secret' => '<client_secret>', 'project' => '<project>']
30
 * );
31
 * $client = Client::ofConfig($config);
32
 * ```
33
 *
34
 * ## Execution
35
 *
36
 * ### Synchronous
37
 * There are two main approaches for retrieving result objects
38
 *
39
 * Client centric:
40
 *
41
 * ```php
42
 * $response = $client->execute(ProductProjectionSearchRequest::of());
43
 * $products = $response->toObject();
44
 * ```
45
 *
46
 * Request centric:
47
 *
48
 * ```php
49
 * $request = ProductProjectionSearchRequest::of();
50
 * $response = $request->executeWithClient($client);
51
 * $products = $request->mapResponse($response);
52
 * ```
53
 *
54
 * By using the request centric approach the IDE is capable of resolving the correct classes and give
55
 * a maximum of support with available methods.
56
 *
57
 * ### Asynchronous
58
 * The asynchronous execution will return a promise to fulfill the request.
59
 *
60
 * ```php
61
 * $response = $client->executeAsync(ProductProjectionSearchRequest::of());
62
 * ```
63
 *
64
 * ### Batch
65
 * By filling the batch queue and starting the execution all requests will be executed in parallel.
66
 *
67
 * ```php
68
 * $client->addBatchRequest(ProductProjectionSearchRequest::of())
69
 *     ->addBatchRequest(CartByIdGetRequest::ofId($cartId));
70
 * $responses = $client->executeBatch();
71
 * ```
72
 *
73
 * ## Instantiation options
74
 *
75
 * ### Using a logger
76
 *
77
 * The client uses the PSR-3 logger interface for logging requests and deprecation notices. To enable
78
 * logging provide a PSR-3 compliant logger (e.g. Monolog).
79
 *
80
 * ```php
81
 * $logger = new \Monolog\Logger('name');
82
 * $logger->pushHandler(new StreamHandler('./requests.log'));
83
 * $client = Client::ofConfigAndLogger($config, $logger);
84
 * ```
85
 *
86
 * ### Using a cache adapter ###
87
 *
88
 * The client will automatically request an OAuth token and store the token in the APCu cache, which should be the
89
 * fastest option as there is no network or socket interface used.
90
 *
91
 * It's also possible to use a different cache adapter. The SDK provides a Doctrine, a Redis and an APCu cache adapter.
92
 * By default the SDK tries to instantiate the APCu cache adapter if there is no cache given. E.g. Redis:
93
 *
94
 * ```php
95
 * $redis = new \Redis();
96
 * $redis->connect('localhost');
97
 * $client = Client::ofConfigAndCache($config, $redis);
98
 * ```
99
 *
100
 * #### Using cache and logger ####
101
 *
102
 * ```php
103
 * $client = Client::ofConfigCacheAndLogger($config, $cache, $logger);
104
 * ```
105
 *
106
 * #### Using a custom cache adapter ####
107
 *
108
 * ```php
109
 * class <CacheClass>Adapter implements \Commercetools\Core\Cache\CacheAdapterInterface {
110
 *     protected $cache;
111
 *     public function __construct(<CacheClass> $cache) {
112
 *         $this->cache = $cache;
113
 *     }
114
 * }
115
 *
116
 * $client->getAdapterFactory()->registerCallback(function ($cache) {
117
 *     if ($cache instanceof <CacheClass>) {
118
 *         return new <CacheClass>Adapter($cache);
119
 *     }
120
 *     return null;
121
 * });
122
 * ```
123
 *
124
 * @package Commercetools\Core
125
 */
126
class Client extends AbstractHttpClient
127
{
128
    const DEPRECATION_HEADER = 'X-DEPRECATION-NOTICE';
129
130
    /**
131
     * @sideeffect Test123
132
     * @var LoggerInterface
133
     */
134
    protected $logger;
135
136
    /**
137
     * @var Manager
138
     */
139
    protected $oauthManager;
140
141
    /**
142
     * @var ClientRequestInterface[]
143
     */
144
    protected $batchRequests = [];
145
146
    protected $tokenRefreshed = false;
147
148
    /**
149
     * @param array|Config $config
150
     * @param $cache
151
     * @param LoggerInterface $logger
152
     */
153 113
    public function __construct($config, $cache = null, LoggerInterface $logger = null)
154
    {
155 113
        parent::__construct($config);
156
157 113
        $manager = new Manager($config, $cache);
158 113
        $this->setOauthManager($manager);
159 113
        $this->setLogger($logger);
160 113
    }
161
162
    /**
163
     * @return Manager
164
     */
165 92
    public function getOauthManager()
166
    {
167 92
        return $this->oauthManager;
168
    }
169
170
    /**
171
     * @param Manager $oauthManager
172
     * @return $this
173
     */
174 113
    protected function setOauthManager(Manager $oauthManager)
175
    {
176 113
        $this->oauthManager = $oauthManager;
177 113
        return $this;
178
    }
179
180
    /**
181
     * @param LoggerInterface $logger
182
     * @return $this
183
     */
184 113
    protected function setLogger(LoggerInterface $logger = null)
185
    {
186 113
        if ($logger instanceof LoggerInterface) {
187 4
            $this->logger = $logger;
188 4
        }
189 113
        return $this;
190
    }
191
192
    /**
193
     * @param array $options
194
     * @return AdapterInterface
195
     */
196 111
    public function getHttpClient($options = [])
197
    {
198 111
        if (is_null($this->httpClient)) {
199 111
            $client = parent::getHttpClient($options);
200 111
            if ($this->logger instanceof LoggerInterface) {
201 4
                $client->setLogger($this->logger);
202 4
            }
203 111
        }
204
205 111
        return $this->httpClient;
206
    }
207
208
209
    /**
210
     * @return string
211
     */
212 112
    protected function getBaseUrl()
213
    {
214 112
        return $this->getConfig()->getApiUrl() . '/' . $this->getConfig()->getProject() . '/';
215
    }
216
217
    /**
218
     * Executes an API request synchronously
219
     *
220
     * @param ClientRequestInterface $request
221
     * @return ApiResponseInterface
222
     * @throws InvalidTokenException
223
     * @throws ApiException
224
     * @throws \Exception
225
     */
226 108
    public function execute(ClientRequestInterface $request)
227
    {
228 108
        if ($request instanceof ContextAwareInterface) {
229 108
            $request->setContextIfNull($this->getConfig()->getContext());
230 108
        }
231 108
        $httpRequest = $this->createHttpRequest($request);
232
233
        try {
234 108
            $response = $this->getHttpClient()->execute($httpRequest);
235 108
        } catch (ApiException $exception) {
236 12
            if ($exception instanceof InvalidTokenException && !$this->tokenRefreshed) {
237 1
                $this->tokenRefreshed = true;
238 1
                $this->getOauthManager()->refreshToken();
239 1
                return $this->execute($request);
240
            }
241 12
            if ($this->getConfig()->getThrowExceptions() || !$exception->getResponse() instanceof ResponseInterface) {
242 10
                throw $exception;
243
            }
244 2
            $response = $exception->getResponse();
245
        }
246 98
        $this->logDeprecatedRequest($response, $httpRequest);
247
248 98
        $response = $request->buildResponse($response);
249
250 98
        return $response;
251
    }
252
253
    /**
254
     * Executes an API request asynchronously
255
     * @param ClientRequestInterface $request
256
     * @return ApiResponseInterface
257
     */
258 2
    public function executeAsync(ClientRequestInterface $request)
259
    {
260 2
        if ($request instanceof ContextAwareInterface) {
261 2
            $request->setContextIfNull($this->getConfig()->getContext());
262 2
        }
263 2
        $httpRequest = $this->createHttpRequest($request);
264 2
        $response = $request->buildResponse($this->getHttpClient()->executeAsync($httpRequest));
265
266 2
        $response = $response->then(
267
            function ($httpResponse) use ($httpRequest) {
268 2
                $this->logDeprecatedRequest($httpResponse, $httpRequest);
269 2
                return $httpResponse;
270
            }
271 2
        );
272
273 2
        return $response;
274
    }
275
276
    /**
277
     * @param ClientRequestInterface $request
278
     * @return RequestInterface
279
     */
280 111
    protected function createHttpRequest(ClientRequestInterface $request)
281
    {
282 111
        $token = $this->getOAuthManager()->getToken();
283
284 111
        $httpRequest = $request->httpRequest();
285
        $httpRequest = $httpRequest
286 111
            ->withHeader('Authorization', 'Bearer ' . $token->getToken())
287 111
        ;
288 111
        return $httpRequest;
289
    }
290
291
    /**
292
     * Executes API requests in batch
293
     * @return Response\ApiResponseInterface[]
294
     * @throws ApiException
295
     */
296 69
    public function executeBatch()
297
    {
298 69
        $requests = $this->getBatchHttpRequests();
299 69
        $httpResponses = $this->getHttpClient()->executeBatch($requests);
300
301 69
        $responses = [];
302 69
        foreach ($httpResponses as $key => $httpResponse) {
303 69
            $request = $this->batchRequests[$key];
304 69
            $httpRequest = $requests[$key];
305 69
            if ($httpResponse instanceof ApiException) {
306
                if ($this->getConfig()->getThrowExceptions() ||
307
                    !$httpResponse->getResponse() instanceof ResponseInterface
308
                ) {
309
                    throw $httpResponse;
310
                }
311
                $httpResponse = $httpResponse->getResponse();
312
            }
313 69
            $responses[$request->getIdentifier()] = $request->buildResponse($httpResponse);
314 69
            $this->logDeprecatedRequest($httpResponse, $httpRequest);
315 69
        }
316 69
        unset($this->batchRequests);
317 69
        $this->batchRequests = [];
318
319 69
        return $responses;
320
    }
321
322
    /**
323
     * @param ResponseInterface $response
324
     * @param RequestInterface $request
325
     * @return $this
326
     */
327 101
    protected function logDeprecatedRequest(ResponseInterface $response, RequestInterface $request)
328
    {
329 101
        if (is_null($this->logger)) {
330 97
            return $this;
331
        }
332
333 4
        if ($response->hasHeader(static::DEPRECATION_HEADER)) {
334 2
            $message = sprintf(
335 2
                Message::DEPRECATED_METHOD,
336 2
                $request->getUri(),
337 2
                $request->getMethod(),
338 2
                $response->getHeaderLine(static::DEPRECATION_HEADER)
339 2
            );
340 2
            $this->logger->warning($message);
341 2
        }
342 4
        return $this;
343
    }
344
345
    /**
346
     * @param RequestInterface $request
347
     * @param ResponseInterface $response
348
     * @return string
349
     */
350
    protected function format(RequestInterface $request, ResponseInterface $response)
351
    {
352
        $entries = [
353
            $request->getMethod(),
354
            (string)$request->getUri(),
355
            $response->getStatusCode()
356
        ];
357
        return implode(', ', $entries);
358
    }
359
360
    /**
361
     * @return array
362
     */
363 69
    protected function getBatchHttpRequests()
364
    {
365 69
        $requests = array_map(
366 69
            function ($request) {
367 69
                return $this->createHttpRequest($request);
368 69
            },
369 69
            $this->batchRequests
370 69
        );
371
372 69
        return $requests;
373
    }
374
375
    /**
376
     * Adds a request to the batch execution queue
377
     * @param ClientRequestInterface $request
378
     * @return $this
379
     */
380 69
    public function addBatchRequest(ClientRequestInterface $request)
381
    {
382 69
        if ($request instanceof ContextAwareInterface) {
383 69
            $request->setContextIfNull($this->getConfig()->getContext());
384 69
        }
385 69
        $this->batchRequests[] = $request;
386 69
        return $this;
387
    }
388
389
    /**
390
     * Instantiates a client with the given config
391
     * @param Config $config
392
     * @return static
393
     */
394
    public static function ofConfig(Config $config)
395
    {
396
        return new static($config);
397
    }
398
399
    /**
400
     * Instantiates a client with the given config and cache adapter
401
     * @param Config $config
402
     * @param $cache
403
     * @return static
404
     */
405
    public static function ofConfigAndCache(Config $config, $cache)
406
    {
407
        return new static($config, $cache);
408
    }
409
410
    /**
411
     * Instantiates a client with the given config and a PSR-3 compliant logger
412
     * @param Config $config
413
     * @param LoggerInterface $logger
414
     * @return static
415
     */
416
    public static function ofConfigAndLogger(Config $config, LoggerInterface $logger)
417
    {
418
        return new static($config, null, $logger);
419
    }
420
421
    /**
422
     * Instantiates a client with the given config, a cache adapter and a PSR-3 compliant logger
423
     * @param Config $config
424
     * @param $cache
425
     * @param LoggerInterface $logger
426
     * @return static
427
     */
428
    public static function ofConfigCacheAndLogger(Config $config, $cache, LoggerInterface $logger)
429
    {
430
        return new static($config, $cache, $logger);
431
    }
432
}
433