Completed
Branch develop (02e6e4)
by Edwin
13:40
created

AbstractResource::getErrorsFromResponse()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 2
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
crap 12
1
<?php
2
3
namespace ShopifyClient\Resource;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\Exception\RequestException;
7
use ShopifyClient\Exception\ClientException;
8
9
abstract class AbstractResource
10
{
11
    const API_CALL_LIMIT_HEADER = 'http_x_shopify_shop_api_call_limit';
12
13
    /**
14
     * @var Client
15
     */
16
    protected $httpClient;
17
18
    /**
19
     * @var int
20
     */
21
    private $rateLimit;
22
23
    /**
24
     * @var int
25
     */
26
    private $callsMade;
27
28
    /**
29
     * @var float
30
     */
31
    private $callCycle = 0.5; // avg. 2 calls a second
32
33
    /**
34
     * @var float
35
     */
36
    private $rateLimitThreshold = 0.8;
37
38
    /**
39
     * @var bool
40
     */
41
    protected $countable = false;
42
43
    /**
44
     * AbstractResource constructor.
45
     * @param Client $client
46
     */
47
    public function __construct(Client $client)
48
    {
49
        $this->httpClient = $client;
50
    }
51
52
    /**
53
     * @param $method
54
     * @param $endpoint
55
     * @param array $params
56
     * @return array
57
     * @throws ClientException
58
     */
59
    public function request($method, $endpoint, $params = [])
60
    {
61
        if ($method !== 'GET') {
62
            $params['headers']['Content-Type'] = 'application/json';
63
        }
64
65
        if ($this->callsMade > 0 && $this->isRateLimitReached()) {
66
            // Prevent bucket overflow
67
            // https://help.shopify.com/api/getting-started/api-call-limit
68
            usleep(rand(3, 10) * 1000000);
69
        }
70
71
        try {
72
            $response = $this->httpClient->request($method, $endpoint, $params);
73
        } catch (RequestException $e) {
74
            $response = $e->getResponse();
75
            $content  = json_decode($response->getBody()->getContents(), true);
76
77
            throw new ClientException($this->getErrorsFromResponse($content), $response->getStatusCode());
78
        }
79
80
        $this->setRateLimit($response->getHeader(self::API_CALL_LIMIT_HEADER));
81
82
        return json_decode($response->getBody()->getContents(), true);
83
    }
84
85
    private function getErrorsFromResponse($content)
86
    {
87
        if (isset($content['error'])) {
88
            $errors = $content['error'];
89
        } elseif (isset($content['errors'])) {
90
            $errors = $content['errors'];
91
        }
92
93
        return $errors;
0 ignored issues
show
Bug introduced by
The variable $errors does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
94
    }
95
96
    /**
97
     * With a "leak rate" of 2 calls per second that continually empties the bucket.
98
     * If your app averages 2 calls per second, it will never trip a 429 error ("bucket overflow").
99
     *
100
     * @param callable $function
101
     * @return mixed
102
     */
103
    public function throttle(callable $function)
104
    {
105
        $start = time();
106
107
        $result = $function();
108
109
        $end = time();
110
111
        $duration = $end - $start;
112
        $waitTime = ceil($this->callCycle - $duration);
113
114
        if ($waitTime > 0) {
115
            sleep($waitTime);
116
        }
117
118
        return $result;
119
    }
120
121
    /**
122
     * @param array $header
123
     */
124
    private function setRateLimit(array $header)
125
    {
126
        $parts = explode('/', $header[0]);
127
128
        $this->rateLimit = $parts[1];
129
        $this->callsMade = $parts[0];
130
    }
131
132
    /**
133
     * @return bool
134
     */
135
    public function isRateLimitReached(): bool
136
    {
137
        return $this->getCallLimit() >= $this->rateLimitThreshold;
138
    }
139
140
    public function getCallLimit()
141
    {
142
        return $this->callsMade / $this->rateLimit;
143
    }
144
145
    /**
146
     * @return bool
147
     */
148
    public function isCountable(): bool
149
    {
150
        return $this->countable;
151
    }
152
}
153