Passed
Push — master ( b0125c...7afde3 )
by Ramesh
04:20
created

Client::__construct()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3.0067

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 21
ccs 10
cts 11
cp 0.9091
rs 9.8666
c 0
b 0
f 0
cc 3
nc 4
nop 3
crap 3.0067
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 2
        $files = [];
309
310 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
311
            $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...
312
        }
313
314 2
        if ($fileName) {
315
            $nextFileName = $fileName;
316
            $maxFileCount = 1;
317
        }
318
319 2
        $this->authorizeAccount();
320
321
        // B2 returns, at most, 1000 files per "page". Loop through the pages and compile an array of File objects.
322 2
        while (true) {
323 2
            $response = $this->client->request('POST', $this->apiUrl.'/b2_list_file_names', [
324
                'headers' => [
325 2
                    'Authorization' => $this->authToken,
326
                ],
327
                'json' => [
328 2
                    'bucketId'      => $options['BucketId'],
329 2
                    'startFileName' => $nextFileName,
330 2
                    'maxFileCount'  => $maxFileCount,
331
                ],
332
            ]);
333
334 2
            foreach ($response['files'] as $file) {
335
                // if we have a file name set, only retrieve information if the file name matches
336 1
                if (!$fileName || ($fileName === $file['fileName'])) {
337 1
                    $files[] = new File($file['fileId'], $file['fileName'], null, $file['size']);
338
                }
339
            }
340
341 2
            if ($fileName || $response['nextFileName'] === null) {
342
                // We've got all the files - break out of loop.
343 2
                break;
344
            }
345
346 1
            $nextFileName = $response['nextFileName'];
347
        }
348
349 2
        return $files;
350
    }
351
352
    /**
353
     * Test whether a file exists in B2 for the given bucket.
354
     *
355
     * @param array $options
356
     *
357
     * @return bool
358
     */
359
    public function fileExists(array $options)
360
    {
361
        $files = $this->listFiles($options);
362
363
        return !empty($files);
364
    }
365
366
    /**
367
     * Returns a single File object representing a file stored on B2.
368
     *
369
     * @param array $options
370
     *
371
     * @throws NotFoundException If no file id was provided and BucketName + FileName does not resolve to a file, a NotFoundException is thrown.
372
     *
373
     * @return File
374
     */
375 4
    public function getFile(array $options)
376
    {
377 4
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
378
            $options['FileId'] = $this->getFileIdFromBucketAndFileName($options['BucketName'], $options['FileName']);
379
380
            if (!$options['FileId']) {
381
                throw new NotFoundException();
382
            }
383
        }
384
385 4
        $this->authorizeAccount();
386
387 4
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_file_info', [
388
            'headers' => [
389 4
                'Authorization' => $this->authToken,
390
            ],
391
            'json' => [
392 4
                'fileId' => $options['FileId'],
393
            ],
394
        ]);
395
396 3
        return new File(
397 3
            $response['fileId'],
398 3
            $response['fileName'],
399 3
            $response['contentSha1'],
400 3
            $response['contentLength'],
401 3
            $response['contentType'],
402 3
            $response['fileInfo'],
403 3
            $response['bucketId'],
404 3
            $response['action'],
405 3
            $response['uploadTimestamp']
406
        );
407
    }
408
409
    /**
410
     * Deletes the file identified by ID from Backblaze B2.
411
     *
412
     * @param array $options
413
     *
414
     * @return bool
415
     */
416 3
    public function deleteFile(array $options)
417
    {
418 3
        if (!isset($options['FileName'])) {
419 2
            $file = $this->getFile($options);
420
421 2
            $options['FileName'] = $file->getName();
422
        }
423
424 3
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
425
            $file = $this->getFile($options);
426
427
            $options['FileId'] = $file->getId();
428
        }
429
430 3
        $this->authorizeAccount();
431
432 3
        $this->client->request('POST', $this->apiUrl.'/b2_delete_file_version', [
433
            'headers' => [
434 3
                'Authorization' => $this->authToken,
435
            ],
436
            'json' => [
437 3
                'fileName' => $options['FileName'],
438 3
                'fileId'   => $options['FileId'],
439
            ],
440
        ]);
441
442 2
        return true;
443
    }
444
445
    /**
446
     * Authorize the B2 account in order to get an auth token and API/download URLs.
447
     *
448
     * @throws \Exception
449
     */
450 28
    protected function authorizeAccount()
451
    {
452 28
        if (Carbon::now('UTC')->timestamp > $this->reauthTime->timestamp) {
453 28
            $response = $this->client->request('GET', 'https://api.backblazeb2.com/b2api/v1/b2_authorize_account', [
454 28
                'auth' => [$this->accountId, $this->applicationKey],
455
            ]);
456
457 28
            $this->authToken = $response['authorizationToken'];
458 28
            $this->apiUrl = $response['apiUrl'].'/b2api/v1';
459 28
            $this->downloadUrl = $response['downloadUrl'];
460 28
            $this->reauthTime = Carbon::now('UTC');
461 28
            $this->reauthTime->addSeconds($this->authTimeoutSeconds);
462
        }
463 28
    }
464
465
    /**
466
     * Maps the provided bucket name to the appropriate bucket ID.
467
     *
468
     * @param $name
469
     *
470
     * @return null
471
     */
472
    protected function getBucketIdFromName($name)
473
    {
474
        $buckets = $this->listBuckets();
475
476
        foreach ($buckets as $bucket) {
477
            if ($bucket->getName() === $name) {
478
                return $bucket->getId();
479
            }
480
        }
481
    }
482
483
    /**
484
     * Maps the provided bucket ID to the appropriate bucket name.
485
     *
486
     * @param $id
487
     *
488
     * @return null
489
     */
490
    protected function getBucketNameFromId($id)
491
    {
492
        $buckets = $this->listBuckets();
493
494
        foreach ($buckets as $bucket) {
495
            if ($bucket->getId() === $id) {
496
                return $bucket->getName();
497
            }
498
        }
499
    }
500
501
    protected function getFileIdFromBucketAndFileName($bucketName, $fileName)
502
    {
503
        $files = $this->listFiles([
504
            'BucketName' => $bucketName,
505
            'FileName'   => $fileName,
506
        ]);
507
508
        foreach ($files as $file) {
509
            if ($file->getName() === $fileName) {
510
                return $file->getId();
511
            }
512
        }
513
    }
514
515
    /**
516
     * Uploads a large file using b2 large file proceedure.
517
     *
518
     * @param array $options
519
     *
520
     * @return \BackblazeB2\File
521
     */
522
    public function uploadLargeFile(array $options)
523
    {
524
        if (substr($options['FileName'], 0, 1) === '/') {
525
            $options['FileName'] = ltrim($options['FileName'], '/');
526
        }
527
528
        //if last char of path is not a "/" then add a "/"
529
        if (substr($options['FilePath'], -1) != '/') {
530
            $options['FilePath'] = $options['FilePath'].'/';
531
        }
532
533
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
534
            $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...
535
        }
536
537
        if (!isset($options['FileContentType'])) {
538
            $options['FileContentType'] = 'b2/x-auto';
539
        }
540
541
        $this->authorizeAccount();
542
543
        // 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...
544
        $start = $this->startLargeFile($options['FileName'], $options['FileContentType'], $options['BucketId']);
545
546
        // 2) b2_get_upload_part_url for each thread uploading (takes fileId)
547
        $url = $this->getUploadPartUrl($start['fileId']);
548
549
        // 3) b2_upload_part for each part of the file
550
        $parts = $this->uploadParts($options['FilePath'].$options['FileName'], $url['uploadUrl'], $url['authorizationToken']);
551
552
        $sha1s = [];
553
554
        foreach ($parts as $part) {
555
            $sha1s[] = $part['contentSha1'];
556
        }
557
558
        // 4) b2_finish_large_file.
559
        return $this->finishLargeFile($start['fileId'], $sha1s);
560
    }
561
562
    /**
563
     * starts the large file upload process.
564
     *
565
     * @param $fileName
566
     * @param $contentType
567
     * @param $bucketId
568
     *
569
     * @return array
570
     */
571
    protected function startLargeFile($fileName, $contentType, $bucketId)
572
    {
573
        $response = $this->client->request('POST', $this->apiUrl.'/b2_start_large_file', [
574
            'headers' => [
575
                'Authorization' => $this->authToken,
576
            ],
577
            'json' => [
578
                'fileName'      => $fileName,
579
                'contentType'   => $contentType,
580
                'bucketId'      => $bucketId,
581
            ],
582
        ]);
583
584
        return $response;
585
    }
586
587
    /**
588
     * gets the url for the next large file part upload.
589
     *
590
     * @param $fileId
591
     *
592
     * @return array
593
     */
594
    protected function getUploadPartUrl($fileId)
595
    {
596
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_upload_part_url', [
597
            'headers' => [
598
                'Authorization' => $this->authToken,
599
            ],
600
            'json' => [
601
                'fileId' => $fileId,
602
            ],
603
        ]);
604
605
        return $response;
606
    }
607
608
    /**
609
     * uploads the file as "parts" of 100MB each.
610
     *
611
     * @param $filePath
612
     * @param $uploadUrl
613
     * @param $largeFileAuthToken
614
     *
615
     * @return array
616
     */
617
    protected function uploadParts($filePath, $uploadUrl, $largeFileAuthToken)
618
    {
619
        $return = [];
620
621
        $minimum_part_size = 100 * (1000 * 1000);
622
623
        $local_file_size = filesize($filePath);
624
        $total_bytes_sent = 0;
625
        $bytes_sent_for_part = $minimum_part_size;
626
        $sha1_of_parts = [];
627
        $part_no = 1;
628
        $file_handle = fopen($filePath, 'r');
629
630
        while ($total_bytes_sent < $local_file_size) {
631
632
            // Determine the number of bytes to send based on the minimum part size
633
            if (($local_file_size - $total_bytes_sent) < $minimum_part_size) {
634
                $bytes_sent_for_part = ($local_file_size - $total_bytes_sent);
635
            }
636
637
            // Get a sha1 of the part we are going to send
638
            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

638
            fseek(/** @scrutinizer ignore-type */ $file_handle, $total_bytes_sent);
Loading history...
639
            $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

639
            $data_part = fread(/** @scrutinizer ignore-type */ $file_handle, $bytes_sent_for_part);
Loading history...
640
            array_push($sha1_of_parts, sha1($data_part));
641
            fseek($file_handle, $total_bytes_sent);
642
643
            $response = $this->client->request('POST', $uploadUrl, [
644
                'headers' => [
645
                    'Authorization'                      => $largeFileAuthToken,
646
                    'Content-Length'                     => $bytes_sent_for_part,
647
                    'X-Bz-Part-Number'                   => $part_no,
648
                    'X-Bz-Content-Sha1'                  => $sha1_of_parts[$part_no - 1],
649
                ],
650
                'body' => $data_part,
651
            ]);
652
653
            $return[] = $response;
654
655
            // Prepare for the next iteration of the loop
656
            $part_no++;
657
            $total_bytes_sent = $bytes_sent_for_part + $total_bytes_sent;
658
        }
659
660
        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

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