• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jwilsson / spotify-web-api-php / 4152585362

pending completion
4152585362

push

github

GitHub
Add return self on setters (#259)

9 of 9 new or added lines in 3 files covered. (100.0%)

744 of 755 relevant lines covered (98.54%)

5.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.32
/src/Request.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace SpotifyWebAPI;
6

7
class Request
8
{
9
    public const ACCOUNT_URL = 'https://accounts.spotify.com';
10
    public const API_URL = 'https://api.spotify.com';
11

12
    protected $lastResponse = [];
13
    protected $options = [
14
        'curl_options' => [],
15
        'return_assoc' => false,
16
    ];
17

18
    /**
19
     * Constructor
20
     * Set options.
21
     *
22
     * @param array|object $options Optional. Options to set.
23
     */
24
    public function __construct($options = [])
25
    {
26
        $this->setOptions($options);
31✔
27
    }
28

29
    /**
30
     * Handle response errors.
31
     *
32
     * @param string $body The raw, unparsed response body.
33
     * @param int $status The HTTP status code, passed along to any exceptions thrown.
34
     *
35
     * @throws SpotifyWebAPIException
36
     * @throws SpotifyWebAPIAuthException
37
     *
38
     * @return void
39
     */
40
    protected function handleResponseError($body, $status)
41
    {
42
        $parsedBody = json_decode($body);
4✔
43
        $error = $parsedBody->error ?? null;
4✔
44

45
        if (isset($error->message) && isset($error->status)) {
4✔
46
            // It's an API call error
47
            $exception = new SpotifyWebAPIException($error->message, $error->status);
1✔
48

49
            if (isset($error->reason)) {
1✔
50
                $exception->setReason($error->reason);
×
51
            }
52

53
            throw $exception;
1✔
54
        } elseif (isset($parsedBody->error_description)) {
3✔
55
            // It's an auth call error
56
            throw new SpotifyWebAPIAuthException($parsedBody->error_description, $status);
1✔
57
        } elseif ($body) {
2✔
58
            // Something else went wrong, try to give at least some info
59
            throw new SpotifyWebAPIException($body, $status);
1✔
60
        } else {
61
            // Something went really wrong, we don't know what
62
            throw new SpotifyWebAPIException('An unknown error occurred.', $status);
1✔
63
        }
64
    }
65

66
    /**
67
     * Parse HTTP response headers and normalize names.
68
     *
69
     * @param string $headers The raw, unparsed response headers.
70
     *
71
     * @return array Headers as key–value pairs.
72
     */
73
    protected function parseHeaders($headers)
74
    {
75
        $headers = str_replace("\r\n", "\n", $headers);
15✔
76
        $headers = explode("\n", $headers);
15✔
77

78
        array_shift($headers);
15✔
79

80
        $parsedHeaders = [];
15✔
81
        foreach ($headers as $header) {
15✔
82
            [$key, $value] = explode(':', $header, 2);
15✔
83

84
            $key = strtolower($key);
15✔
85
            $parsedHeaders[$key] = trim($value);
15✔
86
        }
87

88
        return $parsedHeaders;
15✔
89
    }
90

91
    /**
92
     * Make a request to the "account" endpoint.
93
     *
94
     * @param string $method The HTTP method to use.
95
     * @param string $uri The URI to request.
96
     * @param array $parameters Optional. Query string parameters or HTTP body, depending on $method.
97
     * @param array $headers Optional. HTTP headers.
98
     *
99
     * @throws SpotifyWebAPIException
100
     * @throws SpotifyWebAPIAuthException
101
     *
102
     * @return array Response data.
103
     * - array|object body The response body. Type is controlled by the `return_assoc` option.
104
     * - array headers Response headers.
105
     * - int status HTTP status code.
106
     * - string url The requested URL.
107
     */
108
    public function account($method, $uri, $parameters = [], $headers = [])
109
    {
110
        return $this->send($method, self::ACCOUNT_URL . $uri, $parameters, $headers);
2✔
111
    }
112

113
    /**
114
     * Make a request to the "api" endpoint.
115
     *
116
     * @param string $method The HTTP method to use.
117
     * @param string $uri The URI to request.
118
     * @param array $parameters Optional. Query string parameters or HTTP body, depending on $method.
119
     * @param array $headers Optional. HTTP headers.
120
     *
121
     * @throws SpotifyWebAPIException
122
     * @throws SpotifyWebAPIAuthException
123
     *
124
     * @return array Response data.
125
     * - array|object body The response body. Type is controlled by the `return_assoc` option.
126
     * - array headers Response headers.
127
     * - int status HTTP status code.
128
     * - string url The requested URL.
129
     */
130
    public function api($method, $uri, $parameters = [], $headers = [])
131
    {
132
        return $this->send($method, self::API_URL . $uri, $parameters, $headers);
4✔
133
    }
134

135
    /**
136
     * Get the latest full response from the Spotify API.
137
     *
138
     * @return array Response data.
139
     * - array|object body The response body. Type is controlled by the `return_assoc` option.
140
     * - array headers Response headers.
141
     * - int status HTTP status code.
142
     * - string url The requested URL.
143
     */
144
    public function getLastResponse()
145
    {
146
        return $this->lastResponse;
1✔
147
    }
148

149
    /**
150
     * Make a request to Spotify.
151
     * You'll probably want to use one of the convenience methods instead.
152
     *
153
     * @param string $method The HTTP method to use.
154
     * @param string $url The URL to request.
155
     * @param array $parameters Optional. Query string parameters or HTTP body, depending on $method.
156
     * @param array $headers Optional. HTTP headers.
157
     *
158
     * @throws SpotifyWebAPIException
159
     * @throws SpotifyWebAPIAuthException
160
     *
161
     * @return array Response data.
162
     * - array|object body The response body. Type is controlled by the `return_assoc` option.
163
     * - array headers Response headers.
164
     * - int status HTTP status code.
165
     * - string url The requested URL.
166
     */
167
    public function send($method, $url, $parameters = [], $headers = [])
168
    {
169
        // Reset any old responses
170
        $this->lastResponse = [];
16✔
171

172
        // Sometimes a stringified JSON object is passed
173
        if (is_array($parameters) || is_object($parameters)) {
16✔
174
            $parameters = http_build_query($parameters, '', '&');
16✔
175
        }
176

177
        $options = [
16✔
178
            CURLOPT_CAINFO => __DIR__ . '/cacert.pem',
16✔
179
            CURLOPT_ENCODING => '',
16✔
180
            CURLOPT_HEADER => true,
16✔
181
            CURLOPT_HTTPHEADER => [],
16✔
182
            CURLOPT_RETURNTRANSFER => true,
16✔
183
            CURLOPT_URL => rtrim($url, '/'),
16✔
184
        ];
16✔
185

186
        foreach ($headers as $key => $val) {
16✔
187
            $options[CURLOPT_HTTPHEADER][] = "$key: $val";
1✔
188
        }
189

190
        $method = strtoupper($method);
16✔
191

192
        switch ($method) {
193
            case 'DELETE': // No break
16✔
194
            case 'PUT':
15✔
195
                $options[CURLOPT_CUSTOMREQUEST] = $method;
2✔
196
                $options[CURLOPT_POSTFIELDS] = $parameters;
2✔
197

198
                break;
2✔
199
            case 'POST':
14✔
200
                $options[CURLOPT_POST] = true;
2✔
201
                $options[CURLOPT_POSTFIELDS] = $parameters;
2✔
202

203
                break;
2✔
204
            default:
205
                $options[CURLOPT_CUSTOMREQUEST] = $method;
12✔
206

207
                if ($parameters) {
12✔
208
                    $options[CURLOPT_URL] .= '/?' . $parameters;
1✔
209
                }
210

211
                break;
12✔
212
        }
213

214
        $ch = curl_init();
16✔
215

216
        curl_setopt_array($ch, array_replace($options, $this->options['curl_options']));
16✔
217

218
        $response = curl_exec($ch);
16✔
219

220
        if (curl_error($ch)) {
16✔
221
            $error = curl_error($ch);
1✔
222
            $errno = curl_errno($ch);
1✔
223
            curl_close($ch);
1✔
224

225
            throw new SpotifyWebAPIException('cURL transport error: ' . $errno . ' ' . $error);
1✔
226
        }
227

228
        [$headers, $body] = $this->splitResponse($response);
15✔
229

230
        $parsedBody = json_decode($body, $this->options['return_assoc']);
15✔
231
        $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
15✔
232
        $parsedHeaders = $this->parseHeaders($headers);
15✔
233

234
        $this->lastResponse = [
15✔
235
            'body' => $parsedBody,
15✔
236
            'headers' => $parsedHeaders,
15✔
237
            'status' => $status,
15✔
238
            'url' => $url,
15✔
239
        ];
15✔
240

241
        curl_close($ch);
15✔
242

243
        if ($status >= 400) {
15✔
244
            $this->handleResponseError($body, $status);
4✔
245
        }
246

247
        return $this->lastResponse;
11✔
248
    }
249

250
    /**
251
     * Set options
252
     *
253
     * @param array|object $options Options to set.
254
     *
255
     * @return self
256
     */
257
    public function setOptions($options)
258
    {
259
        $this->options = array_merge($this->options, (array) $options);
121✔
260

261
        return $this;
121✔
262
    }
263

264
    /**
265
     * Split response into headers and body, taking proxy response headers etc. into account.
266
     *
267
     * @param string $response The complete response.
268
     *
269
     * @return array An array consisting of two elements, headers and body.
270
     */
271
    protected function splitResponse($response)
272
    {
273
        $parts = explode("\r\n\r\n", $response, 3);
15✔
274

275
        // Skip first set of headers for proxied requests etc.
276
        if (
277
            preg_match('/^HTTP\/1.\d 100 Continue/', $parts[0]) ||
15✔
278
            preg_match('/^HTTP\/1.\d 200 Connection established/', $parts[0]) ||
15✔
279
            preg_match('/^HTTP\/1.\d 200 Tunnel established/', $parts[0])
15✔
280
        ) {
281
            return [
×
282
                $parts[1],
×
283
                $parts[2],
×
284
            ];
×
285
        }
286

287
        return [
15✔
288
            $parts[0],
15✔
289
            $parts[1],
15✔
290
        ];
15✔
291
    }
292
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc