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
Pull Request — master (#65)
by
unknown
01:15
created

Client::getHeadersForBearerToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Spatie\Dropbox;
4
5
use Exception;
6
use GrahamCampbell\GuzzleFactory\GuzzleFactory;
7
use GuzzleHttp\Client as GuzzleClient;
8
use GuzzleHttp\Exception\ClientException;
9
use GuzzleHttp\Exception\RequestException;
10
use GuzzleHttp\Psr7;
11
use GuzzleHttp\Psr7\PumpStream;
12
use GuzzleHttp\Psr7\StreamWrapper;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\StreamInterface;
15
use Spatie\Dropbox\Exceptions\BadRequest;
16
17
class Client
18
{
19
    const THUMBNAIL_FORMAT_JPEG = 'jpeg';
20
    const THUMBNAIL_FORMAT_PNG = 'png';
21
22
    const THUMBNAIL_SIZE_XS = 'w32h32';
23
    const THUMBNAIL_SIZE_S = 'w64h64';
24
    const THUMBNAIL_SIZE_M = 'w128h128';
25
    const THUMBNAIL_SIZE_L = 'w640h480';
26
    const THUMBNAIL_SIZE_XL = 'w1024h768';
27
28
    const MAX_CHUNK_SIZE = 1024 * 1024 * 150;
29
30
    const UPLOAD_SESSION_START = 0;
31
    const UPLOAD_SESSION_APPEND = 1;
32
33
    /** @var string */
34
    protected $accessToken;
35
36
    /** @var string */
37
    protected $appKey;
38
39
    /** @var string */
40
    protected $appSecret;
41
42
    /** @var \GuzzleHttp\Client */
43
    protected $client;
44
45
    /** @var int */
46
    protected $maxChunkSize;
47
48
    /** @var int */
49
    protected $maxUploadChunkRetries;
50
51
    /**
52
     * @param string|array|null $accessTokenOrAppCredentials
53
     * @param GuzzleClient|null $client
54
     * @param int $maxChunkSize Set max chunk size per request (determines when to switch from "one shot upload" to upload session and defines chunk size for uploads via session).
55
     * @param int $maxUploadChunkRetries How many times to retry an upload session start or append after RequestException.
56
     */
57
    public function __construct($accessTokenOrAppCredentials = null, GuzzleClient $client = null, int $maxChunkSize = self::MAX_CHUNK_SIZE, int $maxUploadChunkRetries = 0)
58
    {
59
        if (is_array($accessTokenOrAppCredentials)) {
60
            [$this->appKey, $this->appSecret] = $accessTokenOrAppCredentials;
61
        }
62
        if (is_string($accessTokenOrAppCredentials)) {
63
            $this->accessToken = $accessTokenOrAppCredentials;
64
        }
65
66
        $this->client = $client ?? new GuzzleClient(['handler' => GuzzleFactory::handler()]);
67
68
        $this->maxChunkSize = ($maxChunkSize < self::MAX_CHUNK_SIZE ? ($maxChunkSize > 1 ? $maxChunkSize : 1) : self::MAX_CHUNK_SIZE);
69
        $this->maxUploadChunkRetries = $maxUploadChunkRetries;
70
    }
71
72
    /**
73
     * Copy a file or folder to a different location in the user's Dropbox.
74
     *
75
     * If the source path is a folder all its contents will be copied.
76
     *
77
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy_v2
78
     */
79
    public function copy(string $fromPath, string $toPath): array
80
    {
81
        $parameters = [
82
            'from_path' => $this->normalizePath($fromPath),
83
            'to_path' => $this->normalizePath($toPath),
84
        ];
85
86
        return $this->rpcEndpointRequest('files/copy_v2', $parameters);
87
    }
88
89
    /**
90
     * Create a folder at a given path.
91
     *
92
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder
93
     */
94
    public function createFolder(string $path): array
95
    {
96
        $parameters = [
97
            'path' => $this->normalizePath($path),
98
        ];
99
100
        $object = $this->rpcEndpointRequest('files/create_folder', $parameters);
101
102
        $object['.tag'] = 'folder';
103
104
        return $object;
105
    }
106
107
    /**
108
     * Create a shared link with custom settings.
109
     *
110
     * If no settings are given then the default visibility is RequestedVisibility.public.
111
     * The resolved visibility, though, may depend on other aspects such as team and
112
     * shared folder settings). Only for paid users.
113
     *
114
     * @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-create_shared_link_with_settings
115
     */
116
    public function createSharedLinkWithSettings(string $path, array $settings = []): array
117
    {
118
        $parameters = [
119
            'path' => $this->normalizePath($path),
120
        ];
121
122
        if (count($settings)) {
123
            $parameters = array_merge(compact('settings'), $parameters);
124
        }
125
126
        return $this->rpcEndpointRequest('sharing/create_shared_link_with_settings', $parameters);
127
    }
128
129
    /**
130
     * Search a file or folder in the user's Dropbox.
131
     *
132
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-search
133
     */
134
    public function search(string $query, bool $includeHighlights = false): array
135
    {
136
        $parameters = [
137
            'query' => $query,
138
            'include_highlights' => $includeHighlights,
139
        ];
140
141
        return $this->rpcEndpointRequest('files/search_v2', $parameters);
142
    }
143
144
    /**
145
     * List shared links.
146
     *
147
     * For empty path returns a list of all shared links. For non-empty path
148
     * returns a list of all shared links with access to the given path.
149
     *
150
     * If direct_only is set true, only direct links to the path will be returned, otherwise
151
     * it may return link to the path itself and parent folders as described on docs.
152
     *
153
     * @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links
154
     */
155
    public function listSharedLinks(string $path = null, bool $direct_only = false, string $cursor = null): array
156
    {
157
        $parameters = [
158
            'path' => $path ? $this->normalizePath($path) : null,
159
            'cursor' => $cursor,
160
            'direct_only' => $direct_only,
161
        ];
162
163
        $body = $this->rpcEndpointRequest('sharing/list_shared_links', $parameters);
164
165
        return $body['links'];
166
    }
167
168
    /**
169
     * Delete the file or folder at a given path.
170
     *
171
     * If the path is a folder, all its contents will be deleted too.
172
     * A successful response indicates that the file or folder was deleted.
173
     *
174
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete
175
     */
176
    public function delete(string $path): array
177
    {
178
        $parameters = [
179
            'path' => $this->normalizePath($path),
180
        ];
181
182
        return $this->rpcEndpointRequest('files/delete', $parameters);
183
    }
184
185
    /**
186
     * Download a file from a user's Dropbox.
187
     *
188
     * @param string $path
189
     *
190
     * @return resource
191
     *
192
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-download
193
     */
194
    public function download(string $path)
195
    {
196
        $arguments = [
197
            'path' => $this->normalizePath($path),
198
        ];
199
200
        $response = $this->contentEndpointRequest('files/download', $arguments);
201
202
        return StreamWrapper::getResource($response->getBody());
203
    }
204
205
    /**
206
     * Download a folder from the user's Dropbox, as a zip file.
207
     * The folder must be less than 20 GB in size and have fewer than 10,000 total files.
208
     * The input cannot be a single file. Any single file must be less than 4GB in size.
209
     *
210
     * @param string $path
211
     *
212
     * @return resource
213
     *
214
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-download_zip
215
     */
216
    public function downloadZip(string $path)
217
    {
218
        $arguments = [
219
            'path' => $this->normalizePath($path),
220
        ];
221
222
        $response = $this->contentEndpointRequest('files/download_zip', $arguments);
223
224
        return StreamWrapper::getResource($response->getBody());
225
    }
226
227
    /**
228
     * Returns the metadata for a file or folder.
229
     *
230
     * Note: Metadata for the root folder is unsupported.
231
     *
232
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata
233
     */
234
    public function getMetadata(string $path): array
235
    {
236
        $parameters = [
237
            'path' => $this->normalizePath($path),
238
        ];
239
240
        return $this->rpcEndpointRequest('files/get_metadata', $parameters);
241
    }
242
243
    /**
244
     * Get a temporary link to stream content of a file.
245
     *
246
     * This link will expire in four hours and afterwards you will get 410 Gone.
247
     * Content-Type of the link is determined automatically by the file's mime type.
248
     *
249
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link
250
     */
251
    public function getTemporaryLink(string $path): string
252
    {
253
        $parameters = [
254
            'path' => $this->normalizePath($path),
255
        ];
256
257
        $body = $this->rpcEndpointRequest('files/get_temporary_link', $parameters);
258
259
        return $body['link'];
260
    }
261
262
    /**
263
     * Get a thumbnail for an image.
264
     *
265
     * This method currently supports files with the following file extensions:
266
     * jpg, jpeg, png, tiff, tif, gif and bmp.
267
     *
268
     * Photos that are larger than 20MB in size won't be converted to a thumbnail.
269
     *
270
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_thumbnail
271
     */
272
    public function getThumbnail(string $path, string $format = 'jpeg', string $size = 'w64h64'): string
273
    {
274
        $arguments = [
275
            'path' => $this->normalizePath($path),
276
            'format' => $format,
277
            'size' => $size,
278
        ];
279
280
        $response = $this->contentEndpointRequest('files/get_thumbnail', $arguments);
281
282
        return (string) $response->getBody();
283
    }
284
285
    /**
286
     * Starts returning the contents of a folder.
287
     *
288
     * If the result's ListFolderResult.has_more field is true, call
289
     * list_folder/continue with the returned ListFolderResult.cursor to retrieve more entries.
290
     *
291
     * Note: auth.RateLimitError may be returned if multiple list_folder or list_folder/continue calls
292
     * with same parameters are made simultaneously by same API app for same user. If your app implements
293
     * retry logic, please hold off the retry until the previous request finishes.
294
     *
295
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder
296
     */
297
    public function listFolder(string $path = '', bool $recursive = false): array
298
    {
299
        $parameters = [
300
            'path' => $this->normalizePath($path),
301
            'recursive' => $recursive,
302
        ];
303
304
        return $this->rpcEndpointRequest('files/list_folder', $parameters);
305
    }
306
307
    /**
308
     * Once a cursor has been retrieved from list_folder, use this to paginate through all files and
309
     * retrieve updates to the folder, following the same rules as documented for list_folder.
310
     *
311
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue
312
     */
313
    public function listFolderContinue(string $cursor = ''): array
314
    {
315
        return $this->rpcEndpointRequest('files/list_folder/continue', compact('cursor'));
316
    }
317
318
    /**
319
     * Move a file or folder to a different location in the user's Dropbox.
320
     *
321
     * If the source path is a folder all its contents will be moved.
322
     *
323
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-move_v2
324
     */
325
    public function move(string $fromPath, string $toPath): array
326
    {
327
        $parameters = [
328
            'from_path' => $this->normalizePath($fromPath),
329
            'to_path' => $this->normalizePath($toPath),
330
        ];
331
332
        return $this->rpcEndpointRequest('files/move_v2', $parameters);
333
    }
334
335
    /**
336
     * The file should be uploaded in chunks if it size exceeds the 150 MB threshold
337
     * or if the resource size could not be determined (eg. a popen() stream).
338
     *
339
     * @param string|resource $contents
340
     *
341
     * @return bool
342
     */
343
    protected function shouldUploadChunked($contents): bool
344
    {
345
        $size = is_string($contents) ? strlen($contents) : fstat($contents)['size'];
346
347
        if ($this->isPipe($contents)) {
348
            return true;
349
        }
350
351
        if ($size === null) {
352
            return true;
353
        }
354
355
        return $size > $this->maxChunkSize;
356
    }
357
358
    /**
359
     * Check if the contents is a pipe stream (not seekable, no size defined).
360
     *
361
     * @param string|resource $contents
362
     *
363
     * @return bool
364
     */
365
    protected function isPipe($contents): bool
366
    {
367
        return is_resource($contents) ? (fstat($contents)['mode'] & 010000) != 0 : false;
368
    }
369
370
    /**
371
     * Create a new file with the contents provided in the request.
372
     *
373
     * Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start.
374
     *
375
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload
376
     *
377
     * @param string $path
378
     * @param string|resource $contents
379
     * @param string $mode
380
     *
381
     * @return array
382
     */
383
    public function upload(string $path, $contents, $mode = 'add'): array
384
    {
385
        if ($this->shouldUploadChunked($contents)) {
386
            return $this->uploadChunked($path, $contents, $mode);
387
        }
388
389
        $arguments = [
390
            'path' => $this->normalizePath($path),
391
            'mode' => $mode,
392
        ];
393
394
        $response = $this->contentEndpointRequest('files/upload', $arguments, $contents);
395
396
        $metadata = json_decode($response->getBody(), true);
397
398
        $metadata['.tag'] = 'file';
399
400
        return $metadata;
401
    }
402
403
    /**
404
     * Upload file split in chunks. This allows uploading large files, since
405
     * Dropbox API v2 limits the content size to 150MB.
406
     *
407
     * The chunk size will affect directly the memory usage, so be careful.
408
     * Large chunks tends to speed up the upload, while smaller optimizes memory usage.
409
     *
410
     * @param string $path
411
     * @param string|resource $contents
412
     * @param string $mode
413
     * @param int|null $chunkSize
414
     *
415
     * @return array
416
     */
417
    public function uploadChunked(string $path, $contents, $mode = 'add', $chunkSize = null): array
418
    {
419
        if ($chunkSize === null || $chunkSize > $this->maxChunkSize) {
420
            $chunkSize = $this->maxChunkSize;
421
        }
422
423
        $stream = $this->getStream($contents);
424
425
        $cursor = $this->uploadChunk(self::UPLOAD_SESSION_START, $stream, $chunkSize, null);
0 ignored issues
show
Compatibility introduced by
$stream of type object<Psr\Http\Message\StreamInterface> is not a sub-type of object<GuzzleHttp\Psr7\Stream>. It seems like you assume a concrete implementation of the interface Psr\Http\Message\StreamInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
426
427
        while (! $stream->eof()) {
428
            $cursor = $this->uploadChunk(self::UPLOAD_SESSION_APPEND, $stream, $chunkSize, $cursor);
429
        }
430
431
        return $this->uploadSessionFinish('', $cursor, $path, $mode);
432
    }
433
434
    /**
435
     * @param int $type
436
     * @param Psr7\Stream $stream
437
     * @param int $chunkSize
438
     * @param UploadSessionCursor|null $cursor
439
     * @return UploadSessionCursor
440
     * @throws Exception
441
     */
442
    protected function uploadChunk($type, &$stream, $chunkSize, $cursor = null): UploadSessionCursor
443
    {
444
        $maximumTries = $stream->isSeekable() ? $this->maxUploadChunkRetries : 0;
445
        $pos = $stream->tell();
446
447
        $tries = 0;
448
449
        tryUpload:
450
        try {
451
            $tries++;
452
453
            $chunkStream = new Psr7\LimitStream($stream, $chunkSize, $stream->tell());
454
455
            if ($type === self::UPLOAD_SESSION_START) {
456
                return $this->uploadSessionStart($chunkStream);
457
            }
458
459
            if ($type === self::UPLOAD_SESSION_APPEND && $cursor !== null) {
460
                return $this->uploadSessionAppend($chunkStream, $cursor);
461
            }
462
463
            throw new Exception('Invalid type');
464
        } catch (RequestException $exception) {
465
            if ($tries < $maximumTries) {
466
                // rewind
467
                $stream->seek($pos, SEEK_SET);
468
                goto tryUpload;
469
            }
470
            throw $exception;
471
        }
472
    }
473
474
    /**
475
     * Upload sessions allow you to upload a single file in one or more requests,
476
     * for example where the size of the file is greater than 150 MB.
477
     * This call starts a new upload session with the given data.
478
     *
479
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start
480
     *
481
     * @param string|StreamInterface $contents
482
     * @param bool $close
483
     *
484
     * @return UploadSessionCursor
485
     */
486
    public function uploadSessionStart($contents, bool $close = false): UploadSessionCursor
487
    {
488
        $arguments = compact('close');
489
490
        $response = json_decode(
491
            $this->contentEndpointRequest('files/upload_session/start', $arguments, $contents)->getBody(),
492
            true
493
        );
494
495
        return new UploadSessionCursor($response['session_id'], ($contents instanceof StreamInterface ? $contents->tell() : strlen($contents)));
496
    }
497
498
    /**
499
     * Append more data to an upload session.
500
     * When the parameter close is set, this call will close the session.
501
     * A single request should not upload more than 150 MB.
502
     *
503
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2
504
     *
505
     * @param string|StreamInterface $contents
506
     * @param UploadSessionCursor $cursor
507
     * @param bool $close
508
     *
509
     * @return UploadSessionCursor
510
     */
511
    public function uploadSessionAppend($contents, UploadSessionCursor $cursor, bool $close = false): UploadSessionCursor
512
    {
513
        $arguments = compact('cursor', 'close');
514
515
        $pos = $contents instanceof StreamInterface ? $contents->tell() : 0;
516
        $this->contentEndpointRequest('files/upload_session/append_v2', $arguments, $contents);
517
518
        $cursor->offset += $contents instanceof StreamInterface ? ($contents->tell() - $pos) : strlen($contents);
519
520
        return $cursor;
521
    }
522
523
    /**
524
     * Finish an upload session and save the uploaded data to the given file path.
525
     * A single request should not upload more than 150 MB.
526
     *
527
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
528
     *
529
     * @param string|StreamInterface $contents
530
     * @param UploadSessionCursor $cursor
531
     * @param string $path
532
     * @param string|array $mode
533
     * @param bool $autorename
534
     * @param bool $mute
535
     *
536
     * @return array
537
     */
538
    public function uploadSessionFinish($contents, UploadSessionCursor $cursor, string $path, $mode = 'add', $autorename = false, $mute = false): array
539
    {
540
        $arguments = compact('cursor');
541
        $arguments['commit'] = compact('path', 'mode', 'autorename', 'mute');
542
543
        $response = $this->contentEndpointRequest(
544
            'files/upload_session/finish',
545
            $arguments,
546
            ($contents == '') ? null : $contents
547
        );
548
549
        $metadata = json_decode($response->getBody(), true);
550
551
        $metadata['.tag'] = 'file';
552
553
        return $metadata;
554
    }
555
556
    /**
557
     * Get Account Info for current authenticated user.
558
     *
559
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
560
     *
561
     * @return array
562
     */
563
    public function getAccountInfo(): array
564
    {
565
        return $this->rpcEndpointRequest('users/get_current_account');
566
    }
567
568
    /**
569
     * Revoke current access token.
570
     *
571
     * @link https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke
572
     *
573
     * @throws Exception
574
     */
575
    public function revokeToken(): void
576
    {
577
        $this->rpcEndpointRequest('auth/token/revoke');
578
    }
579
580
    protected function normalizePath(string $path): string
581
    {
582
        if (preg_match("/^id:.*|^rev:.*|^(ns:[0-9]+(\/.*)?)/", $path) === 1) {
583
            return $path;
584
        }
585
586
        $path = trim($path, '/');
587
588
        return ($path === '') ? '' : '/'.$path;
589
    }
590
591
    protected function getEndpointUrl(string $subdomain, string $endpoint): string
592
    {
593
        if (count($parts = explode('::', $endpoint)) === 2) {
594
            [$subdomain, $endpoint] = $parts;
595
        }
596
597
        return "https://{$subdomain}.dropboxapi.com/2/{$endpoint}";
598
    }
599
600
    /**
601
     * @param string $endpoint
602
     * @param array $arguments
603
     * @param string|resource|StreamInterface $body
604
     *
605
     * @return \Psr\Http\Message\ResponseInterface
606
     *
607
     * @throws \Exception
608
     */
609
    public function contentEndpointRequest(string $endpoint, array $arguments, $body = ''): ResponseInterface
610
    {
611
        $headers = ['Dropbox-API-Arg' => json_encode($arguments)];
612
613
        if ($body !== '') {
614
            $headers['Content-Type'] = 'application/octet-stream';
615
        }
616
617
        try {
618
            $response = $this->client->post($this->getEndpointUrl('content', $endpoint), [
619
                'headers' => $this->getHeaders($headers),
620
                'body' => $body,
621
            ]);
622
        } catch (ClientException $exception) {
623
            throw $this->determineException($exception);
624
        }
625
626
        return $response;
627
    }
628
629
    public function rpcEndpointRequest(string $endpoint, array $parameters = null): array
630
    {
631
        try {
632
            $options = ['headers' => $this->getHeaders()];
633
634
            if ($parameters) {
635
                $options['json'] = $parameters;
636
            }
637
638
            $response = $this->client->post($this->getEndpointUrl('api', $endpoint), $options);
639
        } catch (ClientException $exception) {
640
            throw $this->determineException($exception);
641
        }
642
643
        $response = json_decode($response->getBody(), true);
644
645
        return $response ?? [];
646
    }
647
648
    protected function determineException(ClientException $exception): Exception
649
    {
650
        if (in_array($exception->getResponse()->getStatusCode(), [400, 409])) {
651
            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...
652
        }
653
654
        return $exception;
655
    }
656
657
    /**
658
     * @param $contents
659
     *
660
     * @return PumpStream|StreamInterface
661
     */
662
    protected function getStream($contents)
663
    {
664
        if ($this->isPipe($contents)) {
665
            /* @var resource $contents */
666
            return new PumpStream(function ($length) use ($contents) {
667
                $data = fread($contents, $length);
668
                if (strlen($data) === 0) {
669
                    return false;
670
                }
671
672
                return $data;
673
            });
674
        }
675
676
        return Psr7\stream_for($contents);
677
    }
678
679
    /**
680
     * Get the access token.
681
     */
682
    public function getAccessToken(): string
683
    {
684
        return $this->accessToken;
685
    }
686
687
    /**
688
     * Set the access token.
689
     */
690
    public function setAccessToken(string $accessToken): self
691
    {
692
        $this->accessToken = $accessToken;
693
694
        return $this;
695
    }
696
697
    /**
698
     * Get the HTTP headers.
699
     */
700
    protected function getHeaders(array $headers = []): array
701
    {
702
        $auth = [];
703
        if ($this->accessToken || ($this->appKey && $this->appSecret)) {
704
            $auth = $this->accessToken ? $this->getHeadersForBearerToken() : $this->getHeadersForCredentials();
705
        }
706
707
        return array_merge($auth, $headers);
708
    }
709
710
    /**
711
     * @return array
712
     */
713
    protected function getHeadersForBearerToken()
714
    {
715
        return [
716
            'Authorization' => "Bearer {$this->accessToken}",
717
        ];
718
    }
719
720
    /**
721
     * @return array
722
     */
723
    protected function getHeadersForCredentials()
724
    {
725
        return [
726
            'Authorization' => 'Basic '.base64_encode("{$this->appKey}:{$this->appSecret}"),
727
        ];
728
    }
729
}
730