Completed
Branch develop (966c07)
by Carlos
02:52
created

AbstractAPI::checkAndThrow()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.2
cc 4
eloc 5
nc 3
nop 1
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\Exception\RequestException;
28
use GuzzleHttp\Middleware;
29
use GuzzleHttp\Psr7\Uri;
30
use Psr\Http\Message\RequestInterface;
31
use Psr\Http\Message\ResponseInterface;
32
33
/**
34
 * Class AbstractAPI.
35
 */
36
abstract class AbstractAPI
37
{
38
    /**
39
     * Http instance.
40
     *
41
     * @var \EasyWeChat\Core\Http
42
     */
43
    protected $http;
44
45
    /**
46
     * The reqeust token.
47
     *
48
     * @var \EasyWeChat\Core\AccessToken
49
     */
50
    protected $accessToken;
51
52
    const GET = 'get';
53
    const POST = 'post';
54
    const JSON = 'json';
55
56
    /**
57
     * Constructor.
58
     *
59
     * @param \EasyWeChat\Core\AccessToken $accessToken
60
     */
61
    public function __construct(AccessToken $accessToken)
62
    {
63
        $this->setAccessToken($accessToken);
64
65
        $this->registerHttpMiddleware();
66
    }
67
68
    /**
69
     * Return the http instance.
70
     *
71
     * @return \EasyWeChat\Core\Http
72
     */
73
    public function getHttp()
74
    {
75
        return $this->http ?: $this->http = new Http();
76
    }
77
78
    /**
79
     * Set the http instance.
80
     *
81
     * @param \EasyWeChat\Core\Http $http
82
     *
83
     * @return $this
84
     */
85
    public function setHttp(Http $http)
86
    {
87
        $this->http = $http;
88
89
        return $this;
90
    }
91
92
    /**
93
     * Return the current accessToken.
94
     *
95
     * @return \EasyWeChat\Core\AccessToken
96
     */
97
    public function getAccessToken()
98
    {
99
        return $this->accessToken;
100
    }
101
102
    /**
103
     * Set the request token.
104
     *
105
     * @param \EasyWeChat\Core\AccessToken $accessToken
106
     *
107
     * @return $this
108
     */
109
    public function setAccessToken(AccessToken $accessToken)
110
    {
111
        $this->accessToken = $accessToken;
112
113
        return $this;
114
    }
115
116
    /**
117
     * Parse JSON from response and check error.
118
     *
119
     * @param string $method
120
     * @param array  $args
121
     * @param bool   $throws
122
     *
123
     * @return \EasyWeChat\Support\Collection
124
     */
125
    public function parseJSON($method, $args, $throws = true)
0 ignored issues
show
Unused Code introduced by
The parameter $throws is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
126
    {
127
        $http = $this->getHttp();
128
129
        $args = (array) $args;
130
131
        $contents = $http->parseJSON(call_user_func_array([$http, $method], $args));
132
133
        $this->checkAndThrow($contents);
134
135
        return new Collection($contents);
136
    }
137
138
    /**
139
     * Set request access_token query.
140
     */
141
    protected function registerHttpMiddleware()
142
    {
143
        // access token
144
        $this->getHttp()->addMiddleware($this->accessTokenMiddleware());
145
        // log
146
        $this->getHttp()->addMiddleware($this->logMiddleware());
147
        // retry
148
        $this->getHttp()->addMiddleware($this->retryMiddleware());
149
    }
150
151
    /**
152
     * Attache access token to request query.
153
     *
154
     * @return Closure
155
     */
156
    public function accessTokenMiddleware()
157
    {
158
        return function (callable $handler) {
159
            return function (RequestInterface $request, array $options) use ($handler) {
160
                if (!$this->accessToken) {
161
                    return $handler($request, $options);
162
                }
163
164
                $field = $this->accessToken->getQueryName();
165
                $token = $this->accessToken->getToken();
166
167
                $request = $request->withUri(Uri::withQueryValue($request->getUri(), $field, $token));
168
169
                return $handler($request, $options);
170
            };
171
        };
172
    }
173
174
    /**
175
     * Log the request.
176
     *
177
     * @return \GuzzleHttp\Middleware
178
     */
179
    public function logMiddleware()
180
    {
181
        return Middleware::tap(function (RequestInterface $request) {
182
            Log::debug("Request: {$request->getMethod()} {$request->getUri()}");
183
            Log::debug("Request Body: {$request->getBody()}");
184
        });
185
    }
186
187
    /**
188
     * Return retry middleware.
189
     *
190
     * @return \GuzzleHttp\RetryMiddleware
191
     */
192
    protected function retryMiddleware()
193
    {
194
        return Middleware::retry(function (
195
                                          $retries,
196
                                          RequestInterface $request,
197
                                          ResponseInterface $response = null
198
                                       ) {
199
            // Limit the number of retries to 2
200
            if ($retries <= 2 && $response && $body = $response->getBody()) {
201
                // Retry on server errors
202
                if (stripos($body, 'errcode') && (stripos($body, '40001') || stripos($body, '42001'))) {
203
                    $field = $this->accessToken->getQueryName();
204
                    $token = $this->accessToken->getToken();
205
206
                    $request = $request->withUri($newUri = Uri::withQueryValue($request->getUri(), $field, $token));
207
208
                    Log::debug("Retry with Request Token: {$token}");
209
                    Log::debug("Retry with Request Uri: {$newUri}");
210
211
                    return true;
212
                }
213
            }
214
215
            return false;
216
        });
217
    }
218
219
    /**
220
     * Check the array data erros, and Throw expcetion when the contents cotnains error.
221
     *
222
     * @param array $contents
223
     *
224
     * @throws \EasyWeChat\Core\Exceptions\HttpException
225
     */
226
    protected function checkAndThrow(array $contents)
227
    {
228
        if (isset($contents['errcode']) && 0 !== $contents['errcode']) {
229
            if (empty($contents['errmsg'])) {
230
                $contents['errmsg'] = 'Unknown';
231
            }
232
233
            throw new HttpException($contents['errmsg'], $contents['errcode']);
234
        }
235
    }
236
}
237