Completed
Push — master ( 7afde3...af9122 )
by Ramesh
04:08 queued 01:51
created

Client::listFiles()   C

Complexity

Conditions 13
Paths 224

Size

Total Lines 55
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 13.2076

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 55
ccs 25
cts 28
cp 0.8929
rs 5.4833
c 0
b 0
f 0
cc 13
nc 224
nop 1
crap 13.2076

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace BackblazeB2;
4
5
use BackblazeB2\Exceptions\NotFoundException;
6
use BackblazeB2\Exceptions\ValidationException;
7
use BackblazeB2\Http\Client as HttpClient;
8
use Carbon\Carbon;
9
10
class Client
11
{
12
    protected $accountId;
13
    protected $applicationKey;
14
15
    protected $authToken;
16
    protected $apiUrl;
17
    protected $downloadUrl;
18
19
    protected $client;
20
21
    protected $reauthTime;
22
    protected $authTimeoutSeconds;
23
24
    /**
25
     * Client constructor. Accepts the account ID, application key and an optional array of options.
26
     *
27
     * @param $accountId
28
     * @param $applicationKey
29
     * @param array $options
30
     */
31 28
    public function __construct($accountId, $applicationKey, array $options = [])
32
    {
33 28
        $this->accountId = $accountId;
34 28
        $this->applicationKey = $applicationKey;
35
36 28
        if (isset($options['auth_timeout_seconds'])) {
37 1
            $this->authTimeoutSeconds = $options['auth_timeout_seconds'];
38
        } else {
39 27
            $this->authTimeoutSeconds = 12 * 60 * 60;  // 12 hour default
40
        }
41
42
        // set reauthorize time to force an authentication to take place
43 28
        $this->reauthTime = Carbon::now('UTC')->subSeconds($this->authTimeoutSeconds * 2);
44
45 28
        if (isset($options['client'])) {
46 28
            $this->client = $options['client'];
47
        } else {
48
            $this->client = new HttpClient(['exceptions' => false]);
49
        }
50
51 28
        $this->authorizeAccount();
52 28
    }
53
54
    /**
55
     * Create a bucket with the given name and type.
56
     *
57
     * @param array $options
58
     *
59
     * @throws ValidationException
60
     *
61
     * @return Bucket
62
     */
63 5
    public function createBucket(array $options)
64
    {
65 5
        if (!in_array($options['BucketType'], [Bucket::TYPE_PUBLIC, Bucket::TYPE_PRIVATE])) {
66 1
            throw new ValidationException(
67 1
                sprintf('Bucket type must be %s or %s', Bucket::TYPE_PRIVATE, Bucket::TYPE_PUBLIC)
68
            );
69
        }
70
71 4
        $this->authorizeAccount();
72
73 4
        $response = $this->client->request('POST', $this->apiUrl.'/b2_create_bucket', [
74
            'headers' => [
75 4
                'Authorization' => $this->authToken,
76
            ],
77
            'json' => [
78 4
                'accountId'  => $this->accountId,
79 4
                'bucketName' => $options['BucketName'],
80 4
                'bucketType' => $options['BucketType'],
81
            ],
82
        ]);
83
84 3
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
85
    }
86
87
    /**
88
     * Updates the type attribute of a bucket by the given ID.
89
     *
90
     * @param array $options
91
     *
92
     * @throws ValidationException
93
     *
94
     * @return Bucket
95
     */
96 2
    public function updateBucket(array $options)
97
    {
98 2
        if (!in_array($options['BucketType'], [Bucket::TYPE_PUBLIC, Bucket::TYPE_PRIVATE])) {
99
            throw new ValidationException(
100
                sprintf('Bucket type must be %s or %s', Bucket::TYPE_PRIVATE, Bucket::TYPE_PUBLIC)
101
            );
102
        }
103
104 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
105
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
106
        }
107
108 2
        $this->authorizeAccount();
109
110 2
        $response = $this->client->request('POST', $this->apiUrl.'/b2_update_bucket', [
111
            'headers' => [
112 2
                'Authorization' => $this->authToken,
113
            ],
114
            'json' => [
115 2
                'accountId'  => $this->accountId,
116 2
                'bucketId'   => $options['BucketId'],
117 2
                'bucketType' => $options['BucketType'],
118
            ],
119
        ]);
120
121 2
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
122
    }
123
124
    /**
125
     * Returns a list of bucket objects representing the buckets on the account.
126
     *
127
     * @return array
128
     */
129 2
    public function listBuckets()
130
    {
131 2
        $buckets = [];
132
133 2
        $this->authorizeAccount();
134
135 2
        $response = $this->client->request('POST', $this->apiUrl.'/b2_list_buckets', [
136
            'headers' => [
137 2
                'Authorization' => $this->authToken,
138
            ],
139
            'json' => [
140 2
                'accountId' => $this->accountId,
141
            ],
142
        ]);
143
144 2
        foreach ($response['buckets'] as $bucket) {
145 1
            $buckets[] = new Bucket($bucket['bucketId'], $bucket['bucketName'], $bucket['bucketType']);
146
        }
147
148 2
        return $buckets;
149
    }
150
151
    /**
152
     * Deletes the bucket identified by its ID.
153
     *
154
     * @param array $options
155
     *
156
     * @return bool
157
     */
158 3
    public function deleteBucket(array $options)
159
    {
160 3
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
161
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
162
        }
163
164 3
        $this->authorizeAccount();
165
166 3
        $this->client->request('POST', $this->apiUrl.'/b2_delete_bucket', [
167
            'headers' => [
168 3
                'Authorization' => $this->authToken,
169
            ],
170
            'json' => [
171 3
                'accountId' => $this->accountId,
172 3
                'bucketId'  => $options['BucketId'],
173
            ],
174
        ]);
175
176 1
        return true;
177
    }
178
179
    /**
180
     * Uploads a file to a bucket and returns a File object.
181
     *
182
     * @param array $options
183
     *
184
     * @return File
185
     */
186 3
    public function upload(array $options)
187
    {
188
        // Clean the path if it starts with /.
189 3
        if (substr($options['FileName'], 0, 1) === '/') {
190
            $options['FileName'] = ltrim($options['FileName'], '/');
191
        }
192
193 3
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
194
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
195
        }
196
197 3
        $this->authorizeAccount();
198
199
        // Retrieve the URL that we should be uploading to.
200 3
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_upload_url', [
201
            'headers' => [
202 3
                'Authorization' => $this->authToken,
203
            ],
204
            'json' => [
205 3
                'bucketId' => $options['BucketId'],
206
            ],
207
        ]);
208
209 3
        $uploadEndpoint = $response['uploadUrl'];
210 3
        $uploadAuthToken = $response['authorizationToken'];
211
212 3
        if (is_resource($options['Body'])) {
213
            // We need to calculate the file's hash incrementally from the stream.
214 1
            $context = hash_init('sha1');
215 1
            hash_update_stream($context, $options['Body']);
216 1
            $hash = hash_final($context);
217
218
            // Similarly, we have to use fstat to get the size of the stream.
219 1
            $size = fstat($options['Body'])['size'];
220
221
            // Rewind the stream before passing it to the HTTP client.
222 1
            rewind($options['Body']);
223
        } else {
224
            // We've been given a simple string body, it's super simple to calculate the hash and size.
225 2
            $hash = sha1($options['Body']);
226 2
            $size = strlen($options['Body']);
227
        }
228
229 3
        if (!isset($options['FileLastModified'])) {
230 2
            $options['FileLastModified'] = round(microtime(true) * 1000);
231
        }
232
233 3
        if (!isset($options['FileContentType'])) {
234 2
            $options['FileContentType'] = 'b2/x-auto';
235
        }
236
237 3
        $response = $this->client->request('POST', $uploadEndpoint, [
238
            'headers' => [
239 3
                'Authorization'                      => $uploadAuthToken,
240 3
                'Content-Type'                       => $options['FileContentType'],
241 3
                'Content-Length'                     => $size,
242 3
                'X-Bz-File-Name'                     => $options['FileName'],
243 3
                'X-Bz-Content-Sha1'                  => $hash,
244 3
                'X-Bz-Info-src_last_modified_millis' => $options['FileLastModified'],
245
            ],
246 3
            'body' => $options['Body'],
247
        ]);
248
249 3
        return new File(
250 3
            $response['fileId'],
251 3
            $response['fileName'],
252 3
            $response['contentSha1'],
253 3
            $response['contentLength'],
254 3
            $response['contentType'],
255 3
            $response['fileInfo']
256
        );
257
    }
258
259
    /**
260
     * Download a file from a B2 bucket.
261
     *
262
     * @param array $options
263
     *
264
     * @return bool|mixed|string
265
     */
266 6
    public function download(array $options)
267
    {
268 6
        $requestUrl = null;
269
        $requestOptions = [
270 6
            'headers' => [
271 6
                'Authorization' => $this->authToken,
272
            ],
273 6
            'sink' => isset($options['SaveAs']) ? $options['SaveAs'] : null,
274
        ];
275
276 6
        if (isset($options['FileId'])) {
277 3
            $requestOptions['query'] = ['fileId' => $options['FileId']];
278 3
            $requestUrl = $this->downloadUrl.'/b2api/v1/b2_download_file_by_id';
279
        } else {
280 3
            if (!isset($options['BucketName']) && isset($options['BucketId'])) {
281
                $options['BucketName'] = $this->getBucketNameFromId($options['BucketId']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketName'] is correct as $this->getBucketNameFromId($options['BucketId']) targeting BackblazeB2\Client::getBucketNameFromId() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
282
            }
283
284 3
            $requestUrl = sprintf('%s/file/%s/%s', $this->downloadUrl, $options['BucketName'], $options['FileName']);
285
        }
286
287 6
        $this->authorizeAccount();
288
289 6
        $response = $this->client->request('GET', $requestUrl, $requestOptions, false);
290
291 4
        return isset($options['SaveAs']) ? true : $response;
292
    }
293
294
    /**
295
     * Retrieve a collection of File objects representing the files stored inside a bucket.
296
     *
297
     * @param array $options
298
     *
299
     * @return array
300
     */
301 2
    public function listFiles(array $options)
302
    {
303
        // if FileName is set, we only attempt to retrieve information about that single file.
304 2
        $fileName = !empty($options['FileName']) ? $options['FileName'] : null;
305
306 2
        $nextFileName = null;
307 2
        $maxFileCount = 1000;
308
309 2
        $prefix = isset($options['Prefix']) ? $options['Prefix'] : '';
310 2
        $delimiter = isset($options['Delimiter']) ? $options['Delimiter'] : null;
311
312 2
        $files = [];
313
314 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
315
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
316
        }
317
318 2
        if ($fileName) {
319
            $nextFileName = $fileName;
320
            $maxFileCount = 1;
321
        }
322
323 2
        $this->authorizeAccount();
324
325
        // B2 returns, at most, 1000 files per "page". Loop through the pages and compile an array of File objects.
326 2
        while (true) {
327 2
            $response = $this->client->request('POST', $this->apiUrl.'/b2_list_file_names', [
328
                'headers' => [
329 2
                    'Authorization' => $this->authToken,
330
                ],
331
                'json' => [
332 2
                    'bucketId'      => $options['BucketId'],
333 2
                    'startFileName' => $nextFileName,
334 2
                    'maxFileCount'  => $maxFileCount,
335 2
                    'prefix'        => $prefix,
336 2
                    'delimiter'     => $delimiter,
337
                ],
338
            ]);
339
340 2
            foreach ($response['files'] as $file) {
341
                // if we have a file name set, only retrieve information if the file name matches
342 1
                if (!$fileName || ($fileName === $file['fileName'])) {
343 1
                    $files[] = new File($file['fileId'], $file['fileName'], null, $file['size']);
344
                }
345
            }
346
347 2
            if ($fileName || $response['nextFileName'] === null) {
348
                // We've got all the files - break out of loop.
349 2
                break;
350
            }
351
352 1
            $nextFileName = $response['nextFileName'];
353
        }
354
355 2
        return $files;
356
    }
357
358
    /**
359
     * Test whether a file exists in B2 for the given bucket.
360
     *
361
     * @param array $options
362
     *
363
     * @return bool
364
     */
365
    public function fileExists(array $options)
366
    {
367
        $files = $this->listFiles($options);
368
369
        return !empty($files);
370
    }
371
372
    /**
373
     * Returns a single File object representing a file stored on B2.
374
     *
375
     * @param array $options
376
     *
377
     * @throws NotFoundException If no file id was provided and BucketName + FileName does not resolve to a file, a NotFoundException is thrown.
378
     *
379
     * @return File
380
     */
381 4
    public function getFile(array $options)
382
    {
383 4
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
384
            $options['FileId'] = $this->getFileIdFromBucketAndFileName($options['BucketName'], $options['FileName']);
385
386
            if (!$options['FileId']) {
387
                throw new NotFoundException();
388
            }
389
        }
390
391 4
        $this->authorizeAccount();
392
393 4
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_file_info', [
394
            'headers' => [
395 4
                'Authorization' => $this->authToken,
396
            ],
397
            'json' => [
398 4
                'fileId' => $options['FileId'],
399
            ],
400
        ]);
401
402 3
        return new File(
403 3
            $response['fileId'],
404 3
            $response['fileName'],
405 3
            $response['contentSha1'],
406 3
            $response['contentLength'],
407 3
            $response['contentType'],
408 3
            $response['fileInfo'],
409 3
            $response['bucketId'],
410 3
            $response['action'],
411 3
            $response['uploadTimestamp']
412
        );
413
    }
414
415
    /**
416
     * Deletes the file identified by ID from Backblaze B2.
417
     *
418
     * @param array $options
419
     *
420
     * @return bool
421
     */
422 3
    public function deleteFile(array $options)
423
    {
424 3
        if (!isset($options['FileName'])) {
425 2
            $file = $this->getFile($options);
426
427 2
            $options['FileName'] = $file->getName();
428
        }
429
430 3
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
431
            $file = $this->getFile($options);
432
433
            $options['FileId'] = $file->getId();
434
        }
435
436 3
        $this->authorizeAccount();
437
438 3
        $this->client->request('POST', $this->apiUrl.'/b2_delete_file_version', [
439
            'headers' => [
440 3
                'Authorization' => $this->authToken,
441
            ],
442
            'json' => [
443 3
                'fileName' => $options['FileName'],
444 3
                'fileId'   => $options['FileId'],
445
            ],
446
        ]);
447
448 2
        return true;
449
    }
450
451
    /**
452
     * Authorize the B2 account in order to get an auth token and API/download URLs.
453
     *
454
     * @throws \Exception
455
     */
456 28
    protected function authorizeAccount()
457
    {
458 28
        if (Carbon::now('UTC')->timestamp > $this->reauthTime->timestamp) {
459 28
            $response = $this->client->request('GET', 'https://api.backblazeb2.com/b2api/v1/b2_authorize_account', [
460 28
                'auth' => [$this->accountId, $this->applicationKey],
461
            ]);
462
463 28
            $this->authToken = $response['authorizationToken'];
464 28
            $this->apiUrl = $response['apiUrl'].'/b2api/v1';
465 28
            $this->downloadUrl = $response['downloadUrl'];
466 28
            $this->reauthTime = Carbon::now('UTC');
467 28
            $this->reauthTime->addSeconds($this->authTimeoutSeconds);
468
        }
469 28
    }
470
471
    /**
472
     * Maps the provided bucket name to the appropriate bucket ID.
473
     *
474
     * @param $name
475
     *
476
     * @return null
477
     */
478
    protected function getBucketIdFromName($name)
479
    {
480
        $buckets = $this->listBuckets();
481
482
        foreach ($buckets as $bucket) {
483
            if ($bucket->getName() === $name) {
484
                return $bucket->getId();
485
            }
486
        }
487
    }
488
489
    /**
490
     * Maps the provided bucket ID to the appropriate bucket name.
491
     *
492
     * @param $id
493
     *
494
     * @return null
495
     */
496
    protected function getBucketNameFromId($id)
497
    {
498
        $buckets = $this->listBuckets();
499
500
        foreach ($buckets as $bucket) {
501
            if ($bucket->getId() === $id) {
502
                return $bucket->getName();
503
            }
504
        }
505
    }
506
507
    protected function getFileIdFromBucketAndFileName($bucketName, $fileName)
508
    {
509
        $files = $this->listFiles([
510
            'BucketName' => $bucketName,
511
            'FileName'   => $fileName,
512
        ]);
513
514
        foreach ($files as $file) {
515
            if ($file->getName() === $fileName) {
516
                return $file->getId();
517
            }
518
        }
519
    }
520
521
    /**
522
     * Uploads a large file using b2 large file proceedure.
523
     *
524
     * @param array $options
525
     *
526
     * @return \BackblazeB2\File
527
     */
528
    public function uploadLargeFile(array $options)
529
    {
530
        if (substr($options['FileName'], 0, 1) === '/') {
531
            $options['FileName'] = ltrim($options['FileName'], '/');
532
        }
533
534
        //if last char of path is not a "/" then add a "/"
535
        if (substr($options['FilePath'], -1) != '/') {
536
            $options['FilePath'] = $options['FilePath'].'/';
537
        }
538
539
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
540
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
541
        }
542
543
        if (!isset($options['FileContentType'])) {
544
            $options['FileContentType'] = 'b2/x-auto';
545
        }
546
547
        $this->authorizeAccount();
548
549
        // 1) b2_start_large_file, (returns fileId)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
550
        $start = $this->startLargeFile($options['FileName'], $options['FileContentType'], $options['BucketId']);
551
552
        // 2) b2_get_upload_part_url for each thread uploading (takes fileId)
553
        $url = $this->getUploadPartUrl($start['fileId']);
554
555
        // 3) b2_upload_part for each part of the file
556
        $parts = $this->uploadParts($options['FilePath'].$options['FileName'], $url['uploadUrl'], $url['authorizationToken']);
557
558
        $sha1s = [];
559
560
        foreach ($parts as $part) {
561
            $sha1s[] = $part['contentSha1'];
562
        }
563
564
        // 4) b2_finish_large_file.
565
        return $this->finishLargeFile($start['fileId'], $sha1s);
566
    }
567
568
    /**
569
     * starts the large file upload process.
570
     *
571
     * @param $fileName
572
     * @param $contentType
573
     * @param $bucketId
574
     *
575
     * @return array
576
     */
577
    protected function startLargeFile($fileName, $contentType, $bucketId)
578
    {
579
        $response = $this->client->request('POST', $this->apiUrl.'/b2_start_large_file', [
580
            'headers' => [
581
                'Authorization' => $this->authToken,
582
            ],
583
            'json' => [
584
                'fileName'      => $fileName,
585
                'contentType'   => $contentType,
586
                'bucketId'      => $bucketId,
587
            ],
588
        ]);
589
590
        return $response;
591
    }
592
593
    /**
594
     * gets the url for the next large file part upload.
595
     *
596
     * @param $fileId
597
     *
598
     * @return array
599
     */
600
    protected function getUploadPartUrl($fileId)
601
    {
602
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_upload_part_url', [
603
            'headers' => [
604
                'Authorization' => $this->authToken,
605
            ],
606
            'json' => [
607
                'fileId' => $fileId,
608
            ],
609
        ]);
610
611
        return $response;
612
    }
613
614
    /**
615
     * uploads the file as "parts" of 100MB each.
616
     *
617
     * @param $filePath
618
     * @param $uploadUrl
619
     * @param $largeFileAuthToken
620
     *
621
     * @return array
622
     */
623
    protected function uploadParts($filePath, $uploadUrl, $largeFileAuthToken)
624
    {
625
        $return = [];
626
627
        $minimum_part_size = 100 * (1000 * 1000);
628
629
        $local_file_size = filesize($filePath);
630
        $total_bytes_sent = 0;
631
        $bytes_sent_for_part = $minimum_part_size;
632
        $sha1_of_parts = [];
633
        $part_no = 1;
634
        $file_handle = fopen($filePath, 'r');
635
636
        while ($total_bytes_sent < $local_file_size) {
637
638
            // Determine the number of bytes to send based on the minimum part size
639
            if (($local_file_size - $total_bytes_sent) < $minimum_part_size) {
640
                $bytes_sent_for_part = ($local_file_size - $total_bytes_sent);
641
            }
642
643
            // Get a sha1 of the part we are going to send
644
            fseek($file_handle, $total_bytes_sent);
0 ignored issues
show
Bug introduced by
It seems like $file_handle can also be of type false; however, parameter $handle of fseek() does only seem to accept resource, 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

644
            fseek(/** @scrutinizer ignore-type */ $file_handle, $total_bytes_sent);
Loading history...
645
            $data_part = fread($file_handle, $bytes_sent_for_part);
0 ignored issues
show
Bug introduced by
It seems like $file_handle can also be of type false; however, parameter $handle of fread() does only seem to accept resource, 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

645
            $data_part = fread(/** @scrutinizer ignore-type */ $file_handle, $bytes_sent_for_part);
Loading history...
646
            array_push($sha1_of_parts, sha1($data_part));
647
            fseek($file_handle, $total_bytes_sent);
648
649
            $response = $this->client->request('POST', $uploadUrl, [
650
                'headers' => [
651
                    'Authorization'                      => $largeFileAuthToken,
652
                    'Content-Length'                     => $bytes_sent_for_part,
653
                    'X-Bz-Part-Number'                   => $part_no,
654
                    'X-Bz-Content-Sha1'                  => $sha1_of_parts[$part_no - 1],
655
                ],
656
                'body' => $data_part,
657
            ]);
658
659
            $return[] = $response;
660
661
            // Prepare for the next iteration of the loop
662
            $part_no++;
663
            $total_bytes_sent = $bytes_sent_for_part + $total_bytes_sent;
664
        }
665
666
        fclose($file_handle);
0 ignored issues
show
Bug introduced by
It seems like $file_handle can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, 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

666
        fclose(/** @scrutinizer ignore-type */ $file_handle);
Loading history...
667
668
        return $return;
669
    }
670
671
    /**
672
     * finishes the large file upload proceedure.
673
     *
674
     * @param $fileId
675
     * @param array $sha1s
676
     *
677
     * @return File
678
     */
679
    protected function finishLargeFile($fileId, array $sha1s)
680
    {
681
        $response = $this->client->request('POST', $this->apiUrl.'/b2_finish_large_file', [
682
            'headers' => [
683
                'Authorization' => $this->authToken,
684
            ],
685
            'json' => [
686
                'fileId'        => $fileId,
687
                'partSha1Array' => $sha1s,
688
            ],
689
        ]);
690
691
        return new File(
692
            $response['fileId'],
693
            $response['fileName'],
694
            $response['contentSha1'],
695
            $response['contentLength'],
696
            $response['contentType'],
697
            $response['fileInfo'],
698
            $response['bucketId'],
699
            $response['action'],
700
            $response['uploadTimestamp']
701
        );
702
    }
703
}
704