Completed
Pull Request — master (#249)
by Carlos
02:58
created

AbstractAPI::accessTokenMiddleware()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
rs 9.4285
cc 2
eloc 9
nc 1
nop 0
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 reqeust 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
    public function __construct(AccessToken $accessToken)
61
    {
62
        $this->setAccessToken($accessToken);
63
64
        $this->registerHttpMiddleware();
65
    }
66
67
    /**
68
     * Return the http instance.
69
     *
70
     * @return \EasyWeChat\Core\Http
71
     */
72
    public function getHttp()
73
    {
74
        return $this->http ?: $this->http = new Http();
75
    }
76
77
    /**
78
     * Set the http instance.
79
     *
80
     * @param \EasyWeChat\Core\Http $http
81
     *
82
     * @return $this
83
     */
84
    public function setHttp(Http $http)
85
    {
86
        $this->http = $http;
87
88
        return $this;
89
    }
90
91
    /**
92
     * Return the current accessToken.
93
     *
94
     * @return \EasyWeChat\Core\AccessToken
95
     */
96
    public function getAccessToken()
97
    {
98
        return $this->accessToken;
99
    }
100
101
    /**
102
     * Set the request token.
103
     *
104
     * @param \EasyWeChat\Core\AccessToken $accessToken
105
     *
106
     * @return $this
107
     */
108
    public function setAccessToken(AccessToken $accessToken)
109
    {
110
        $this->accessToken = $accessToken;
111
112
        return $this;
113
    }
114
115
    /**
116
     * Parse JSON from response and check error.
117
     *
118
     * @param string $method
119
     * @param array  $args
120
     * @param bool   $throws
121
     *
122
     * @return \EasyWeChat\Support\Collection
123
     */
124
    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...
125
    {
126
        $http = $this->getHttp();
127
128
        $args = (array) $args;
129
130
        $contents = $http->parseJSON(call_user_func_array([$http, $method], $args));
131
132
        $this->checkAndThrow($contents);
133
134
        return new Collection($contents);
135
    }
136
137
    /**
138
     * Set request access_token query.
139
     */
140
    protected function registerHttpMiddleware()
141
    {
142
        // access token
143
        $this->getHttp()->addMiddleware($this->accessTokenMiddleware());
144
        // log
145
        $this->getHttp()->addMiddleware($this->logMiddleware());
146
        // retry
147
        $this->getHttp()->addMiddleware($this->retryMiddleware());
148
    }
149
150
    /**
151
     * Attache access token to request query.
152
     *
153
     * @return Closure
154
     */
155
    public function accessTokenMiddleware()
156
    {
157
        return function (callable $handler) {
158
            return function (RequestInterface $request, array $options) use ($handler) {
159
                if (!$this->accessToken) {
160
                    return $handler($request, $options);
161
                }
162
163
                $field = $this->accessToken->getQueryName();
164
                $token = $this->accessToken->getToken();
165
166
                $request = $request->withUri(Uri::withQueryValue($request->getUri(), $field, $token));
167
168
                return $handler($request, $options);
169
            };
170
        };
171
    }
172
173
    /**
174
     * Log the request.
175
     *
176
     * @return \GuzzleHttp\Middleware
177
     */
178
    public function logMiddleware()
179
    {
180
        return Middleware::tap(function (RequestInterface $request) {
181
            Log::debug("Request: {$request->getMethod()} {$request->getUri()}");
182
            Log::debug("Request Body: {$request->getBody()}");
183
        });
184
    }
185
186
    /**
187
     * Return retry middleware.
188
     *
189
     * @return \GuzzleHttp\RetryMiddleware
190
     */
191
    protected function retryMiddleware()
192
    {
193
        return Middleware::retry(function (
194
                                          $retries,
195
                                          RequestInterface $request,
196
                                          ResponseInterface $response = null
197
                                       ) {
198
            // Limit the number of retries to 2
199
            if ($retries <= 2 && $response && $body = $response->getBody()) {
200
                // Retry on server errors
201
                if (stripos($body, 'errcode') && (stripos($body, '40001') || stripos($body, '42001'))) {
202
                    $field = $this->accessToken->getQueryName();
203
                    $token = $this->accessToken->getToken();
204
205
                    $request = $request->withUri($newUri = Uri::withQueryValue($request->getUri(), $field, $token));
206
207
                    Log::debug("Retry with Request Token: {$token}");
208
                    Log::debug("Retry with Request Uri: {$newUri}");
209
210
                    return true;
211
                }
212
            }
213
214
            return false;
215
        });
216
    }
217
218
    /**
219
     * Check the array data erros, and Throw expcetion when the contents cotnains error.
220
     *
221
     * @param array $contents
222
     *
223
     * @throws \EasyWeChat\Core\Exceptions\HttpException
224
     */
225
    protected function checkAndThrow(array $contents)
226
    {
227
        if (isset($contents['errcode']) && 0 !== $contents['errcode']) {
228
            if (empty($contents['errmsg'])) {
229
                $contents['errmsg'] = 'Unknown';
230
            }
231
232
            throw new HttpException($contents['errmsg'], $contents['errcode']);
233
        }
234
    }
235
}
236