Completed
Push — master ( a679b5...bc4430 )
by Carlos
03:34
created

AbstractAPI::retryMiddleware()   C

Complexity

Conditions 7
Paths 1

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 13.125

Importance

Changes 0
Metric Value
cc 7
eloc 14
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 26
ccs 6
cts 12
cp 0.5
crap 13.125
rs 6.7272
1
<?php
2
3
/*
4
 * This file is part of the overtrue/wechat.
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
/**
13
 * AbstractAPI.php.
14
 *
15
 * This file is part of the wechat-components.
16
 *
17
 * (c) overtrue <[email protected]>
18
 *
19
 * This source file is subject to the MIT license that is bundled
20
 * with this source code in the file LICENSE.
21
 */
22
namespace EasyWeChat\Core;
23
24
use EasyWeChat\Core\Exceptions\HttpException;
25
use EasyWeChat\Support\Collection;
26
use EasyWeChat\Support\Log;
27
use GuzzleHttp\Middleware;
28
use GuzzleHttp\Psr7\Uri;
29
use Psr\Http\Message\RequestInterface;
30
use Psr\Http\Message\ResponseInterface;
31
32
/**
33
 * Class AbstractAPI.
34
 */
35
abstract class AbstractAPI
36
{
37
    /**
38
     * Http instance.
39
     *
40
     * @var \EasyWeChat\Core\Http
41
     */
42
    protected $http;
43
44
    /**
45
     * The request token.
46
     *
47
     * @var \EasyWeChat\Core\AccessToken
48
     */
49
    protected $accessToken;
50
51
    const GET = 'get';
52
    const POST = 'post';
53
    const JSON = 'json';
54
55
    /**
56
     * Constructor.
57
     *
58
     * @param \EasyWeChat\Core\AccessToken $accessToken
59
     */
60 132
    public function __construct(AccessToken $accessToken)
61
    {
62 132
        $this->setAccessToken($accessToken);
63 132
    }
64
65
    /**
66
     * Return the http instance.
67
     *
68
     * @return \EasyWeChat\Core\Http
69
     */
70 10
    public function getHttp()
71
    {
72 10
        if (is_null($this->http)) {
73 2
            $this->http = new Http();
74 2
        }
75
76 10
        if (count($this->http->getMiddlewares()) === 0) {
77 9
            $this->registerHttpMiddlewares();
78 9
        }
79
80 10
        return $this->http;
81
    }
82
83
    /**
84
     * Set the http instance.
85
     *
86
     * @param \EasyWeChat\Core\Http $http
87
     *
88
     * @return $this
89
     */
90 13
    public function setHttp(Http $http)
91
    {
92 13
        $this->http = $http;
93
94 13
        return $this;
95
    }
96
97
    /**
98
     * Return the current accessToken.
99
     *
100
     * @return \EasyWeChat\Core\AccessToken
101
     */
102 8
    public function getAccessToken()
103
    {
104 8
        return $this->accessToken;
105
    }
106
107
    /**
108
     * Set the request token.
109
     *
110
     * @param \EasyWeChat\Core\AccessToken $accessToken
111
     *
112
     * @return $this
113
     */
114 132
    public function setAccessToken(AccessToken $accessToken)
115
    {
116 132
        $this->accessToken = $accessToken;
117
118 132
        return $this;
119
    }
120
121
    /**
122
     * Parse JSON from response and check error.
123
     *
124
     * @param string $method
125
     * @param array  $args
126
     *
127
     * @return \EasyWeChat\Support\Collection
128
     */
129 7
    public function parseJSON($method, array $args)
130
    {
131 7
        $http = $this->getHttp();
132
133 7
        $contents = $http->parseJSON(call_user_func_array([$http, $method], $args));
134
135 7
        $this->checkAndThrow($contents);
136
137 7
        return new Collection($contents);
138
    }
139
140
    /**
141
     * Register Guzzle middlewares.
142
     */
143 9
    protected function registerHttpMiddlewares()
144
    {
145
        // log
146 9
        $this->http->addMiddleware($this->logMiddleware());
147
        // retry
148 9
        $this->http->addMiddleware($this->retryMiddleware());
149
        // access token
150 9
        $this->http->addMiddleware($this->accessTokenMiddleware());
151 9
    }
152
153
    /**
154
     * Attache access token to request query.
155
     *
156
     * @return \Closure
157
     */
158 9
    protected function accessTokenMiddleware()
159
    {
160
        return function (callable $handler) {
161
            return function (RequestInterface $request, array $options) use ($handler) {
162 1
                if (!$this->accessToken) {
163
                    return $handler($request, $options);
164
                }
165
166 1
                $field = $this->accessToken->getQueryName();
167 1
                $token = $this->accessToken->getToken();
168
169 1
                $request = $request->withUri(Uri::withQueryValue($request->getUri(), $field, $token));
170
171 1
                return $handler($request, $options);
172 1
            };
173 9
        };
174
    }
175
176
    /**
177
     * Log the request.
178
     *
179
     * @return \Closure
180
     */
181 9
    protected function logMiddleware()
182
    {
183
        return Middleware::tap(function (RequestInterface $request, $options) {
184 1
            Log::debug("Request: {$request->getMethod()} {$request->getUri()} ".json_encode($options));
185 1
            Log::debug('Request headers:'.json_encode($request->getHeaders()));
186 9
        });
187
    }
188
189
    /**
190
     * Return retry middleware.
191
     *
192
     * @return \Closure
193
     */
194
    protected function retryMiddleware()
195
    {
196 9
        return Middleware::retry(function (
197
                                          $retries,
198
                                          RequestInterface $request,
199
                                          ResponseInterface $response = null
200
                                       ) {
201
            // Limit the number of retries to 2
202 1
            if ($retries <= 2 && $response && $body = $response->getBody()) {
203
                // Retry on server errors
204 1
                if (stripos($body, 'errcode') && (stripos($body, '40001') || stripos($body, '42001'))) {
205
                    $field = $this->accessToken->getQueryName();
206
                    $token = $this->accessToken->getToken(true);
207
208
                    $request = $request->withUri($newUri = Uri::withQueryValue($request->getUri(), $field, $token));
209
210
                    Log::debug("Retry with Request Token: {$token}");
211
                    Log::debug("Retry with Request Uri: {$newUri}");
212
213
                    return true;
214
                }
215 1
            }
216
217 1
            return false;
218 9
        });
219
    }
220
221
    /**
222
     * Check the array data errors, and Throw exception when the contents contains error.
223
     *
224
     * @param array $contents
225
     *
226
     * @throws \EasyWeChat\Core\Exceptions\HttpException
227
     */
228 8
    protected function checkAndThrow(array $contents)
229
    {
230 8
        if (isset($contents['errcode']) && 0 !== $contents['errcode']) {
231 1
            if (empty($contents['errmsg'])) {
232 1
                $contents['errmsg'] = 'Unknown';
233 1
            }
234
235 1
            throw new HttpException($contents['errmsg'], $contents['errcode']);
236
        }
237 8
    }
238
}
239