GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 573435...aa8877 )
by Freek
01:14
created

Client   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 564
Duplicated Lines 8.16 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 6
dl 46
loc 564
rs 8.5454
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A copy() 9 9 1
A createFolder() 0 12 1
A createSharedLinkWithSettings() 9 9 1
A listSharedLinks() 0 12 2
A delete() 0 8 1
A download() 0 10 1
A getMetadata() 0 8 1
A getTemporaryLink() 10 10 1
A getThumbnail() 0 12 1
A listFolder() 9 9 1
A listFolderContinue() 0 4 1
A move() 9 9 1
A getMaxChunkSize() 0 4 1
A shouldUploadChunked() 0 14 4
A isPipe() 0 4 2
A upload() 0 19 2
B uploadChunked() 0 29 6
A uploadSessionStart() 0 11 1
A uploadSessionAppend() 0 10 1
A uploadSessionFinish() 0 13 2
A readChunk() 0 14 4
A getAccountInfo() 0 4 1
A revokeToken() 0 4 1
A normalizePath() 0 6 2
A contentEndpointRequest() 0 19 3
A rpcEndpointRequest() 0 18 3
A determineException() 0 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Client often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Client, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Spatie\Dropbox;
4
5
use Exception;
6
use GuzzleHttp\Psr7\StreamWrapper;
7
use GuzzleHttp\Client as GuzzleClient;
8
use Psr\Http\Message\ResponseInterface;
9
use GuzzleHttp\Exception\ClientException;
10
use Spatie\Dropbox\Exceptions\BadRequest;
11
12
class Client
13
{
14
    const THUMBNAIL_FORMAT_JPEG = 'jpeg';
15
    const THUMBNAIL_FORMAT_PNG = 'png';
16
17
    const THUMBNAIL_SIZE_XS = 'w32h32';
18
    const THUMBNAIL_SIZE_S = 'w64h64';
19
    const THUMBNAIL_SIZE_M = 'w128h128';
20
    const THUMBNAIL_SIZE_L = 'w640h480';
21
    const THUMBNAIL_SIZE_XL = 'w1024h768';
22
23
    const MAX_CHUNK_SIZE = 150 * 1024 * 1024;
24
25
    /** @var string */
26
    protected $accessToken;
27
28
    /** @var \GuzzleHttp\Client */
29
    protected $client;
30
31
    public function __construct(string $accessToken, GuzzleClient $client = null)
32
    {
33
        $this->accessToken = $accessToken;
34
35
        $this->client = $client ?? new GuzzleClient([
36
                'headers' => [
37
                    'Authorization' => "Bearer {$this->accessToken}",
38
                ],
39
            ]);
40
    }
41
42
    /**
43
     * Copy a file or folder to a different location in the user's Dropbox.
44
     *
45
     * If the source path is a folder all its contents will be copied.
46
     *
47
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy
48
     */
49 View Code Duplication
    public function copy(string $fromPath, string $toPath): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
50
    {
51
        $parameters = [
52
            'from_path' => $this->normalizePath($fromPath),
53
            'to_path' => $this->normalizePath($toPath),
54
        ];
55
56
        return $this->rpcEndpointRequest('files/copy', $parameters);
57
    }
58
59
    /**
60
     * Create a folder at a given path.
61
     *
62
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder
63
     */
64
    public function createFolder(string $path): array
65
    {
66
        $parameters = [
67
            'path' => $this->normalizePath($path),
68
        ];
69
70
        $object = $this->rpcEndpointRequest('files/create_folder', $parameters);
71
72
        $object['.tag'] = 'folder';
73
74
        return $object;
75
    }
76
77
    /**
78
     * Create a shared link with custom settings.
79
     *
80
     * If no settings are given then the default visibility is RequestedVisibility.public.
81
     * The resolved visibility, though, may depend on other aspects such as team and
82
     * shared folder settings). Only for paid users.
83
     *
84
     * @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-create_shared_link_with_settings
85
     */
86 View Code Duplication
    public function createSharedLinkWithSettings(string $path, array $settings = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
    {
88
        $parameters = [
89
            'path' => $this->normalizePath($path),
90
            'settings' => $settings,
91
        ];
92
93
        return $this->rpcEndpointRequest('sharing/create_shared_link_with_settings', $parameters);
94
    }
95
96
    /**
97
     * List shared links.
98
     *
99
     * For empty path returns a list of all shared links. For non-empty path
100
     * returns a list of all shared links with access to the given path.
101
     *
102
     * If direct_only is set true, only direct links to the path will be returned, otherwise
103
     * it may return link to the path itself and parent folders as described on docs.
104
     *
105
     * @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links
106
     */
107
    public function listSharedLinks(string $path = null, bool $direct_only = false, string $cursor = null): array
108
    {
109
        $parameters = [
110
            'path' => $path ? $this->normalizePath($path) : null,
111
            'cursor' => $cursor,
112
            'direct_only' => $direct_only,
113
        ];
114
115
        $body = $this->rpcEndpointRequest('sharing/list_shared_links', $parameters);
116
117
        return $body['links'];
118
    }
119
120
    /**
121
     * Delete the file or folder at a given path.
122
     *
123
     * If the path is a folder, all its contents will be deleted too.
124
     * A successful response indicates that the file or folder was deleted.
125
     *
126
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete
127
     */
128
    public function delete(string $path): array
129
    {
130
        $parameters = [
131
            'path' => $this->normalizePath($path),
132
        ];
133
134
        return $this->rpcEndpointRequest('files/delete', $parameters);
135
    }
136
137
    /**
138
     * Download a file from a user's Dropbox.
139
     *
140
     * @param string $path
141
     *
142
     * @return resource
143
     *
144
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-download
145
     */
146
    public function download(string $path)
147
    {
148
        $arguments = [
149
            'path' => $this->normalizePath($path),
150
        ];
151
152
        $response = $this->contentEndpointRequest('files/download', $arguments);
153
154
        return StreamWrapper::getResource($response->getBody());
155
    }
156
157
    /**
158
     * Returns the metadata for a file or folder.
159
     *
160
     * Note: Metadata for the root folder is unsupported.
161
     *
162
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata
163
     */
164
    public function getMetadata(string $path): array
165
    {
166
        $parameters = [
167
            'path' => $this->normalizePath($path),
168
        ];
169
170
        return $this->rpcEndpointRequest('files/get_metadata', $parameters);
171
    }
172
173
    /**
174
     * Get a temporary link to stream content of a file.
175
     *
176
     * This link will expire in four hours and afterwards you will get 410 Gone.
177
     * Content-Type of the link is determined automatically by the file's mime type.
178
     *
179
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link
180
     */
181 View Code Duplication
    public function getTemporaryLink(string $path): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
    {
183
        $parameters = [
184
            'path' => $this->normalizePath($path),
185
        ];
186
187
        $body = $this->rpcEndpointRequest('files/get_temporary_link', $parameters);
188
189
        return $body['link'];
190
    }
191
192
    /**
193
     * Get a thumbnail for an image.
194
     *
195
     * This method currently supports files with the following file extensions:
196
     * jpg, jpeg, png, tiff, tif, gif and bmp.
197
     *
198
     * Photos that are larger than 20MB in size won't be converted to a thumbnail.
199
     *
200
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_thumbnail
201
     */
202
    public function getThumbnail(string $path, string $format = 'jpeg', string $size = 'w64h64'): string
203
    {
204
        $arguments = [
205
            'path' => $this->normalizePath($path),
206
            'format' => $format,
207
            'size' => $size,
208
        ];
209
210
        $response = $this->contentEndpointRequest('files/get_thumbnail', $arguments);
211
212
        return (string) $response->getBody();
213
    }
214
215
    /**
216
     * Starts returning the contents of a folder.
217
     *
218
     * If the result's ListFolderResult.has_more field is true, call
219
     * list_folder/continue with the returned ListFolderResult.cursor to retrieve more entries.
220
     *
221
     * Note: auth.RateLimitError may be returned if multiple list_folder or list_folder/continue calls
222
     * with same parameters are made simultaneously by same API app for same user. If your app implements
223
     * retry logic, please hold off the retry until the previous request finishes.
224
     *
225
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder
226
     */
227 View Code Duplication
    public function listFolder(string $path = '', bool $recursive = false): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
228
    {
229
        $parameters = [
230
            'path' => $this->normalizePath($path),
231
            'recursive' => $recursive,
232
        ];
233
234
        return $this->rpcEndpointRequest('files/list_folder', $parameters);
235
    }
236
237
    /**
238
     * Once a cursor has been retrieved from list_folder, use this to paginate through all files and
239
     * retrieve updates to the folder, following the same rules as documented for list_folder.
240
     *
241
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue
242
     */
243
    public function listFolderContinue(string $cursor = ''): array
244
    {
245
        return $this->rpcEndpointRequest('files/list_folder/continue', compact('cursor'));
246
    }
247
248
    /**
249
     * Move a file or folder to a different location in the user's Dropbox.
250
     *
251
     * If the source path is a folder all its contents will be moved.
252
     *
253
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-move_v2
254
     */
255 View Code Duplication
    public function move(string $fromPath, string $toPath): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
256
    {
257
        $parameters = [
258
            'from_path' => $this->normalizePath($fromPath),
259
            'to_path' => $this->normalizePath($toPath),
260
        ];
261
262
        return $this->rpcEndpointRequest('files/move_v2', $parameters);
263
    }
264
265
    /**
266
     * Get max chunk size that can be sent to dropbox api.
267
     *
268
     * @return int
269
     */
270
    public function getMaxChunkSize(): int
271
    {
272
        return static::MAX_CHUNK_SIZE;
273
    }
274
275
    /**
276
     * The file should be uploaded in chunks if it size exceeds the 150 MB threshold
277
     * or if the resource size could not be determined (eg. a popen() stream).
278
     *
279
     * @param string|resource $contents
280
     *
281
     * @return bool
282
     */
283
    protected function shouldUploadChunked($contents): bool
284
    {
285
        $size = is_string($contents) ? strlen($contents) : fstat($contents)['size'];
286
287
        if ($this->isPipe($contents)) {
288
            return true;
289
        }
290
291
        if ($size === null) {
292
            return true;
293
        }
294
295
        return $size > $this->getMaxChunkSize();
296
    }
297
298
    /**
299
     * Check if the contents is a pipe stream (not seekable, no size defined).
300
     *
301
     * @param string|resource $contents
302
     *
303
     * @return bool
304
     */
305
    protected function isPipe($contents): bool
306
    {
307
        return is_resource($contents) ? (fstat($contents)['mode'] & 010000) != 0 : false;
308
    }
309
310
    /**
311
     * Create a new file with the contents provided in the request.
312
     *
313
     * Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start.
314
     *
315
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload
316
     *
317
     * @param string $path
318
     * @param string|resource $contents
319
     * @param string|array $mode
320
     *
321
     * @return array
322
     */
323
    public function upload(string $path, $contents, $mode = 'add'): array
324
    {
325
        if ($this->shouldUploadChunked($contents)) {
326
            return $this->uploadChunked($path, $contents, $mode);
0 ignored issues
show
Bug introduced by
It seems like $mode defined by parameter $mode on line 323 can also be of type array; however, Spatie\Dropbox\Client::uploadChunked() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
327
        }
328
329
        $arguments = [
330
            'path' => $this->normalizePath($path),
331
            'mode' => $mode,
332
        ];
333
334
        $response = $this->contentEndpointRequest('files/upload', $arguments, $contents);
335
336
        $metadata = json_decode($response->getBody(), true);
337
338
        $metadata['.tag'] = 'file';
339
340
        return $metadata;
341
    }
342
343
    /**
344
     * Upload file split in chunks. This allows uploading large files, since
345
     * Dropbox API v2 limits the content size to 150MB.
346
     *
347
     * The chunk size will affect directly the memory usage, so be careful.
348
     * Large chunks tends to speed up the upload, while smaller optimizes memory usage.
349
     *
350
     * @param string          $path
351
     * @param string|resource $contents
352
     * @param string          $mode
353
     * @param int             $chunkSize
354
     *
355
     * @return array
356
     */
357
    public function uploadChunked(string $path, $contents, $mode = 'add', $chunkSize = null): array
358
    {
359
        $chunkSize = $chunkSize ?? $this->getMaxChunkSize();
360
        $stream = $contents;
361
362
        // This method relies on resources, so we need to convert strings to resource
363
        if (is_string($contents)) {
364
            $stream = fopen('php://memory', 'r+');
365
            fwrite($stream, $contents);
366
            rewind($stream);
367
        }
368
369
        $data = self::readChunk($stream, $chunkSize);
0 ignored issues
show
Bug introduced by
It seems like $stream defined by $contents on line 360 can also be of type string; however, Spatie\Dropbox\Client::readChunk() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
370
        $cursor = null;
371
372
        while (! ((strlen($data) < $chunkSize) || feof($stream))) {
373
            // Start upload session on first iteration, then just append on subsequent iterations
374
            $cursor = isset($cursor) ? $this->uploadSessionAppend($data, $cursor) : $this->uploadSessionStart($data);
375
            $data = self::readChunk($stream, $chunkSize);
0 ignored issues
show
Bug introduced by
It seems like $stream defined by $contents on line 360 can also be of type string; however, Spatie\Dropbox\Client::readChunk() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
376
        }
377
378
        // If there's no cursor here, our stream is small enough to a single request
379
        if (! isset($cursor)) {
380
            $cursor = $this->uploadSessionStart($data);
381
            $data = '';
382
        }
383
384
        return $this->uploadSessionFinish($data, $cursor, $path, $mode);
385
    }
386
387
    /**
388
     * Upload sessions allow you to upload a single file in one or more requests,
389
     * for example where the size of the file is greater than 150 MB.
390
     * This call starts a new upload session with the given data.
391
     *
392
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start
393
     *
394
     * @param string $contents
395
     * @param bool   $close
396
     *
397
     * @return UploadSessionCursor
398
     */
399
    public function uploadSessionStart($contents, bool $close = false): UploadSessionCursor
400
    {
401
        $arguments = compact('close');
402
403
        $response = json_decode(
404
            $this->contentEndpointRequest('files/upload_session/start', $arguments, $contents)->getBody(),
405
            true
406
        );
407
408
        return new UploadSessionCursor($response['session_id'], strlen($contents));
409
    }
410
411
    /**
412
     * Append more data to an upload session.
413
     * When the parameter close is set, this call will close the session.
414
     * A single request should not upload more than 150 MB.
415
     *
416
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2
417
     *
418
     * @param string              $contents
419
     * @param UploadSessionCursor $cursor
420
     * @param bool                $close
421
     *
422
     * @return \Spatie\Dropbox\UploadSessionCursor
423
     */
424
    public function uploadSessionAppend($contents, UploadSessionCursor $cursor, bool $close = false): UploadSessionCursor
425
    {
426
        $arguments = compact('cursor', 'close');
427
428
        $this->contentEndpointRequest('files/upload_session/append_v2', $arguments, $contents);
429
430
        $cursor->offset += strlen($contents);
431
432
        return $cursor;
433
    }
434
435
    /**
436
     * Finish an upload session and save the uploaded data to the given file path.
437
     * A single request should not upload more than 150 MB.
438
     *
439
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
440
     *
441
     * @param string                              $contents
442
     * @param \Spatie\Dropbox\UploadSessionCursor $cursor
443
     * @param string                              $path
444
     * @param string|array                        $mode
445
     * @param bool                                $autorename
446
     * @param bool                                $mute
447
     *
448
     * @return array
449
     */
450
    public function uploadSessionFinish($contents, UploadSessionCursor $cursor, string $path, $mode = 'add', $autorename = false, $mute = false): array
451
    {
452
        $arguments = compact('cursor');
453
        $arguments['commit'] = compact('path', 'mode', 'autorename', 'mute');
454
455
        $response = $this->contentEndpointRequest(
456
            'files/upload_session/finish',
457
            $arguments,
458
            ($contents == '') ? null : $contents
459
        );
460
461
        return json_decode($response->getBody(), true);
462
    }
463
464
    /**
465
     * Sometimes fread() returns less than the request number of bytes (for example, when reading
466
     * from network streams).  This function repeatedly calls fread until the requested number of
467
     * bytes have been read or we've reached EOF.
468
     *
469
     * @param resource $stream
470
     * @param int      $chunkSize
471
     *
472
     * @throws Exception
473
     * @return string
474
     */
475
    protected static function readChunk($stream, int $chunkSize)
476
    {
477
        $chunk = '';
478
        while (! feof($stream) && $chunkSize > 0) {
479
            $part = fread($stream, $chunkSize);
480
            if ($part === false) {
481
                throw new Exception('Error reading from $stream.');
482
            }
483
            $chunk .= $part;
484
            $chunkSize -= strlen($part);
485
        }
486
487
        return $chunk;
488
    }
489
490
    /**
491
     * Get Account Info for current authenticated user.
492
     *
493
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
494
     *
495
     * @return array
496
     */
497
    public function getAccountInfo(): array
498
    {
499
        return $this->rpcEndpointRequest('users/get_current_account');
500
    }
501
502
    /**
503
     * Revoke current access token.
504
     *
505
     * @link https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke
506
     */
507
    public function revokeToken()
508
    {
509
        $this->rpcEndpointRequest('auth/token/revoke');
510
    }
511
512
    protected function normalizePath(string $path): string
513
    {
514
        $path = trim($path, '/');
515
516
        return ($path === '') ? '' : '/'.$path;
517
    }
518
519
    /**
520
     * @param string $endpoint
521
     * @param array $arguments
522
     * @param string|resource $body
523
     *
524
     * @return \Psr\Http\Message\ResponseInterface
525
     *
526
     * @throws \Exception
527
     */
528
    public function contentEndpointRequest(string $endpoint, array $arguments, $body = ''): ResponseInterface
529
    {
530
        $headers['Dropbox-API-Arg'] = json_encode($arguments);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$headers was never initialized. Although not strictly required by PHP, it is generally a good practice to add $headers = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
531
532
        if ($body !== '') {
533
            $headers['Content-Type'] = 'application/octet-stream';
534
        }
535
536
        try {
537
            $response = $this->client->post("https://content.dropboxapi.com/2/{$endpoint}", [
538
                'headers' => $headers,
539
                'body' => $body,
540
            ]);
541
        } catch (ClientException $exception) {
542
            throw $this->determineException($exception);
543
        }
544
545
        return $response;
546
    }
547
548
    public function rpcEndpointRequest(string $endpoint, array $parameters = null): array
549
    {
550
        try {
551
            $options = [];
552
553
            if ($parameters) {
554
                $options['json'] = $parameters;
555
            }
556
557
            $response = $this->client->post("https://api.dropboxapi.com/2/{$endpoint}", $options);
558
        } catch (ClientException $exception) {
559
            throw $this->determineException($exception);
560
        }
561
562
        $response = json_decode($response->getBody(), true);
563
564
        return $response ?? [];
565
    }
566
567
    protected function determineException(ClientException $exception): Exception
568
    {
569
        if (in_array($exception->getResponse()->getStatusCode(), [400, 409])) {
570
            return new BadRequest($exception->getResponse());
0 ignored issues
show
Bug introduced by
It seems like $exception->getResponse() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
571
        }
572
573
        return $exception;
574
    }
575
}
576