Issues (1)

src/Request.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace Sulao\BaiduBos;
4
5
use GuzzleHttp;
6
use GuzzleHttp\Exception\BadResponseException;
7
use Psr\Http\Message\ResponseInterface;
8
9
/**
10
 * Class Client
11
 *
12
 * @package Sulao\BaiduBos
13
 */
14
abstract class Request
15
{
16
    const BOS_HOST = 'bcebos.com';
17
18
    /**
19
     * @var array
20
     */
21
    protected $config;
22
23
    /**
24
     * @var array Guzzle request options
25
     */
26
    protected $options = ['connect_timeout' => 10];
27
28
    /**
29
     * @var Authorizer
30
     */
31
    protected $authorizer;
32
33
    /**
34
     * Client constructor.
35
     *
36
     * @param array $config
37
     *
38
     * @throws Exception
39
     */
40 2
    public function __construct(array $config)
41
    {
42 2
        $keys = array_diff(
43 2
            ['access_key', 'secret_key', 'bucket', 'region'],
44 2
            array_keys($config)
45 2
        );
46 2
        if (!empty($keys)) {
47 1
            throw new Exception(
48 1
                'Invalid config, missing: ' . implode(',', $keys)
49 1
            );
50
        }
51
52 1
        if (isset($config['options']) && is_array($config['options'])) {
53 1
            $this->options = $config['options'] + $this->options;
54
        }
55
56 1
        $this->authorizer = new Authorizer(
57 1
            $config['access_key'],
58 1
            $config['secret_key']
59 1
        );
60
61 1
        $this->config = $config;
62
    }
63
64
    /**
65
     * Request BOS api
66
     *
67
     * @param string $method
68
     * @param string $path
69
     * @param array  $options
70
     *
71
     * @return array|string|mixed
72
     * @throws Exception
73
     */
74 2
    protected function request($method, $path, array $options = [])
75
    {
76 2
        list($endpoint, $requestOptions) = $this
77 2
            ->buildRequestOptions($method, $path, $options);
78
79 2
        $httpClient = new GuzzleHttp\Client($this->options);
80
81
        try {
82 2
            $response = $httpClient
83 2
                ->request($method, $endpoint, $requestOptions);
84 1
        } catch (\Exception $exception) {
85 1
            throw $this->handleRquestException($exception);
86
        }
87
88 2
        return $this->processReturn(
89 2
            $response,
90 2
            isset($options['return']) ? $options['return'] : null
91 2
        );
92
    }
93
94
    /**
95
     * Handle http request exception
96
     *
97
     * @param \Exception $exception
98
     *
99
     * @return Exception
100
     */
101 1
    protected function handleRquestException(\Exception $exception)
102
    {
103 1
        if ($exception instanceof BadResponseException) {
104 1
            $content = $exception->getResponse()->getBody()->getContents();
105 1
            $response = json_decode($content, true);
106 1
            if (isset($response['code']) && isset($response['message'])) {
107 1
                $newException = new Exception(
108 1
                    $response['message'],
109 1
                    $exception->getCode(),
110 1
                    $exception
111 1
                );
112
113 1
                $newException->bosCode = $response['code'];
114
115 1
                if (isset($response['requestId'])) {
116 1
                    $newException->requestId = $response['requestId'];
117
                }
118
119 1
                return $newException;
120
            }
121
        }
122
123 1
        return new Exception(
124 1
            $exception->getMessage(),
125 1
            $exception->getCode(),
126 1
            $exception
127 1
        );
128
    }
129
130
    /**
131
     * Build endpoint and request options for guzzle to request
132
     *
133
     * @param string $method
134
     * @param string $path
135
     * @param array  $options
136
     *
137
     * @return array
138
     */
139 2
    protected function buildRequestOptions($method, $path, array $options = [])
140
    {
141 2
        $path = '/' . ltrim($path, '/');
142
143 2
        $options += [
144 2
            'query' => [], 'headers' => [], 'body' => null,
145 2
            'request' => [], 'authorize' => [],
146 2
        ];
147 2
        $requestOptions = $options['request'];
148
149 2
        $headers = $this->buildHeaders(
150 2
            $method,
151 2
            $path,
152 2
            $options['query'],
153 2
            $options['headers'],
154 2
            $options['body'],
155 2
            $options['authorize']
156 2
        );
157 2
        $requestOptions['headers'] = $headers;
158
159 2
        if (!is_null($options['body'])) {
160 1
            $requestOptions['body'] = $options['body'];
161
        }
162
163 2
        $endpoint = 'https://' . $headers['Host'] . $path;
164 2
        if (!empty($options['query'])) {
165 2
            $endpoint .= '?' . $this->buildQuery($options['query']);
166
        }
167
168 2
        return [$endpoint, $requestOptions];
169
    }
170
171
    /**
172
     * Build BOS api request header
173
     *
174
     * @param string      $method
175
     * @param string      $path
176
     * @param array       $query
177
     * @param array       $headers
178
     * @param string|null $body
179
     * @param array       $options
180
     *
181
     * @return array
182
     */
183 2
    protected function buildHeaders(
184
        $method,
185
        $path,
186
        array $query = [],
187
        array $headers = [],
188
        $body = null,
189
        array $options = []
190
    ) {
191 2
        $keys = array_map('strtolower', array_keys($headers));
192
193 2
        $headers['Host'] = $this->config['bucket'] . '.'
194 2
            . $this->config['region'] . '.' . self::BOS_HOST;
195
196 2
        if (!array_key_exists('date', $keys)) {
197 2
            $headers['Date'] = gmdate('D, d M Y H:i:s e');
198
        }
199
200 2
        $contentLength = strlen((string) $body);
201 2
        if (!array_key_exists('content-length', $keys)) {
202 2
            $headers['Content-Length'] = $contentLength ;
203
        }
204
205 2
        if (!array_key_exists('content-md5', $keys) && $contentLength) {
206 1
            $headers['Content-MD5'] = base64_encode(md5($body, true));
0 ignored issues
show
It seems like $body can also be of type null; however, parameter $string of md5() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

206
            $headers['Content-MD5'] = base64_encode(md5(/** @scrutinizer ignore-type */ $body, true));
Loading history...
207
        }
208
209 2
        if (!array_key_exists('authorization', $keys)) {
210 2
            $headers['Authorization'] = $this->authorizer
211 2
                ->getAuthorization($method, $path, $query, $headers, $options);
212
        }
213
214 2
        return $headers;
215
    }
216
217
    /**
218
     *  Build query string
219
     *
220
     * @param array $query
221
     *
222
     * @return string
223
     */
224 2
    protected function buildQuery(array $query)
225
    {
226 2
        $arr = [];
227 2
        foreach ($query as $key => $value) {
228 2
            $arr[] = rawurlencode($key)
229 2
                . (isset($value) ? '=' . rawurlencode($value) : '');
230
        }
231
232 2
        return implode('&', $arr);
233
    }
234
235
    /**
236
     * Process the return base on format
237
     *
238
     * @param ResponseInterface $response
239
     * @param string|null       $format
240
     *
241
     * @return array|string|mixed
242
     */
243 2
    protected function processReturn(
244
        ResponseInterface $response,
245
        $format = null
246
    ) {
247 2
        if ($format === 'body-json') {
248 2
            $return = json_decode($response->getBody()->getContents(), true);
249 1
        } elseif ($format === 'headers') {
250 1
            $return = $this->parseHeaders($response->getHeaders());
251 1
        } elseif ($format === 'both') {
252 1
            $return = [
253 1
                'headers' => $this->parseHeaders($response->getHeaders()),
254 1
                'body' => $response->getBody()->getContents(),
255 1
            ];
256
        } else {
257 1
            $return = $response->getBody()->getContents();
258
        }
259
260 2
        return $return;
261
    }
262
263
    /**
264
     * Parse response headers
265
     *
266
     * @param array $responseHeaders
267
     *
268
     * @return array
269
     */
270 1
    protected function parseHeaders(array $responseHeaders)
271
    {
272 1
        $headers = array();
273 1
        foreach ($responseHeaders as $name => $values) {
274 1
            $headers[$name] = count($values) == 1 ? reset($values) : $values;
275
        }
276
277 1
        return $headers;
278
    }
279
}
280