Test Failed
Push — master ( af9122...f5f924 )
by Ramesh
12:30 queued 12s
created

Client::generateAuthenticatedClient()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 13
ccs 0
cts 7
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
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
use GuzzleHttp\Exception\GuzzleException;
10
11
class Client
12
{
13
    protected $accountId;
14
    protected $applicationKey;
15
16
    protected $authToken;
17
    protected $apiUrl;
18
    protected $downloadUrl;
19
20
    protected $client;
21
22
    protected $reAuthTime;
23
    protected $authTimeoutSeconds;
24
25
    /**
26
     * Client constructor. Accepts the account ID, application key and an optional array of options.
27
     *
28
     * @param $accountId
29
     * @param $applicationKey
30
     * @param array $options
31 28
     * @throws \Exception
32
     */
33 28
    public function __construct($accountId, $applicationKey, array $options = [])
34 28
    {
35
        $this->accountId = $accountId;
36 28
        $this->applicationKey = $applicationKey;
37 1
38
        if (isset($options['auth_timeout_seconds'])) {
39 27
            $this->authTimeoutSeconds = $options['auth_timeout_seconds'];
40
        } else {
41
            $this->authTimeoutSeconds = 12 * 60 * 60;  // 12 hour default
42
        }
43 28
44
        // set reauthorize time to force an authentication to take place
45 28
        $this->reAuthTime = Carbon::now('UTC')->subSeconds($this->authTimeoutSeconds * 2);
46 28
47
        if (isset($options['client'])) {
48
            $this->client = $options['client'];
49
        } else {
50
            $this->client = new HttpClient(['exceptions' => false]);
51 28
        }
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
        $response = $this->generateAuthenticatedClient([
72
            'accountId'  => $this->accountId,
73 4
            'bucketName' => $options['BucketName'],
74
            'bucketType' => $options['BucketType'],
75 4
        ])->request('POST', '/b2_create_bucket');
76
77
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
78 4
    }
79 4
80 4
    /**
81
     * Updates the type attribute of a bucket by the given ID.
82
     *
83
     * @param array $options
84 3
     *
85
     * @throws ValidationException
86
     *
87
     * @return Bucket
88
     */
89
    public function updateBucket(array $options)
90
    {
91
        if (!in_array($options['BucketType'], [Bucket::TYPE_PUBLIC, Bucket::TYPE_PRIVATE])) {
92
            throw new ValidationException(
93
                sprintf('Bucket type must be %s or %s', Bucket::TYPE_PRIVATE, Bucket::TYPE_PUBLIC)
94
            );
95
        }
96 2
97
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
98 2
            $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...
99
        }
100
101
        $response = $this->generateAuthenticatedClient([
102
            'accountId'  => $this->accountId,
103
            'bucketId'   => $options['BucketId'],
104 2
            'bucketType' => $options['BucketType'],
105
        ])->request('POST', '/b2_update_bucket');
106
107
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
108 2
    }
109
110 2
    /**
111
     * Returns a list of bucket objects representing the buckets on the account.
112 2
     *
113
     * @return array
114
     *
115 2
     * @throws \Exception
116 2
     *
117 2
     * @throws GuzzleException
118
     */
119
    public function listBuckets()
120
    {
121 2
        $buckets = [];
122
123
        $response = $this->generateAuthenticatedClient([
124
            'accountId' => $this->accountId,
125
        ])->request('POST', '/b2_list_buckets');
126
127
        foreach ($response['buckets'] as $bucket) {
128
            $buckets[] = new Bucket($bucket['bucketId'], $bucket['bucketName'], $bucket['bucketType']);
129 2
        }
130
131 2
        return $buckets;
132
    }
133 2
134
    /**
135 2
     * Deletes the bucket identified by its ID.
136
     *
137 2
     * @param array $options
138
     *
139
     * @return bool
140 2
     *
141
     * @throws \Exception
142
     *
143
     * @throws GuzzleException
144 2
     */
145 1
    public function deleteBucket(array $options)
146
    {
147
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
148 2
            $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...
149
        }
150
151
        try {
152
            $this->generateAuthenticatedClient([
153
                'accountId' => $this->accountId,
154
                'bucketId' => $options['BucketId'],
155
            ])->request('POST', $this->apiUrl . '/b2_delete_bucket');
156
        } catch (\Exception $e) {
157
            return false;
158 3
        }
159
160 3
        return true;
161
    }
162
163
    /**
164 3
     * Uploads a file to a bucket and returns a File object.
165
     *
166 3
     * @param array $options
167
     *
168 3
     * @return File
169
     *
170
     * @throws \Exception
171 3
     *
172 3
     * @throws GuzzleException
173
     */
174
    public function upload(array $options)
175
    {
176 1
        // Clean the path if it starts with /.
177
        if (substr($options['FileName'], 0, 1) === '/') {
178
            $options['FileName'] = ltrim($options['FileName'], '/');
179
        }
180
181
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
182
            $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...
183
        }
184
185
        // Retrieve the URL that we should be uploading to.
186 3
        $response = $this->generateAuthenticatedClient([
187
            'bucketId' => $options['BucketId'],
188
        ])->request('POST', '/b2_get_upload_url');
189 3
190
        $uploadEndpoint = $response['uploadUrl'];
191
        $uploadAuthToken = $response['authorizationToken'];
192
193 3
        if (is_resource($options['Body'])) {
194
            // We need to calculate the file's hash incrementally from the stream.
195
            $context = hash_init('sha1');
196
            hash_update_stream($context, $options['Body']);
197 3
            $hash = hash_final($context);
198
199
            // Similarly, we have to use fstat to get the size of the stream.
200 3
            $size = fstat($options['Body'])['size'];
201
202 3
            // Rewind the stream before passing it to the HTTP client.
203
            rewind($options['Body']);
204
        } else {
205 3
            // We've been given a simple string body, it's super simple to calculate the hash and size.
206
            $hash = sha1($options['Body']);
207
            $size = strlen($options['Body']);
208
        }
209 3
210 3
        if (!isset($options['FileLastModified'])) {
211
            $options['FileLastModified'] = round(microtime(true) * 1000);
212 3
        }
213
214 1
        if (!isset($options['FileContentType'])) {
215 1
            $options['FileContentType'] = 'b2/x-auto';
216 1
        }
217
218
        $response = $this->client->request('POST', $uploadEndpoint, [
219 1
            'headers' => [
220
                'Authorization'                      => $uploadAuthToken,
221
                'Content-Type'                       => $options['FileContentType'],
222 1
                'Content-Length'                     => $size,
223
                'X-Bz-File-Name'                     => $options['FileName'],
224
                'X-Bz-Content-Sha1'                  => $hash,
225 2
                'X-Bz-Info-src_last_modified_millis' => $options['FileLastModified'],
226 2
            ],
227
            'body' => $options['Body'],
228
        ]);
229 3
230 2
        return new File(
231
            $response['fileId'],
232
            $response['fileName'],
233 3
            $response['contentSha1'],
234 2
            $response['contentLength'],
235
            $response['contentType'],
236
            $response['fileInfo']
237 3
        );
238
    }
239 3
240 3
    /**
241 3
     * Download a file from a B2 bucket.
242 3
     *
243 3
     * @param array $options
244 3
     *
245
     * @return bool|mixed|string
246 3
     *
247
     * @throws \Exception
248
     */
249 3
    public function download(array $options)
250 3
    {
251 3
        $requestUrl = null;
252 3
        $requestOptions = [
253 3
            'headers' => [
254 3
                'Authorization' => $this->authToken,
255 3
            ],
256
            'sink' => isset($options['SaveAs']) ? $options['SaveAs'] : null,
257
        ];
258
259
        if (isset($options['FileId'])) {
260
            $requestOptions['query'] = ['fileId' => $options['FileId']];
261
            $requestUrl = $this->downloadUrl.'/b2api/v1/b2_download_file_by_id';
262
        } else {
263
            if (!isset($options['BucketName']) && isset($options['BucketId'])) {
264
                $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...
265
            }
266 6
267
            $requestUrl = sprintf('%s/file/%s/%s', $this->downloadUrl, $options['BucketName'], $options['FileName']);
268 6
        }
269
270 6
        $this->authorizeAccount();
271 6
272
        $response = $this->client->request('GET', $requestUrl, $requestOptions, false);
273 6
274
        return isset($options['SaveAs']) ? true : $response;
275
    }
276 6
277 3
    /**
278 3
     * Retrieve a collection of File objects representing the files stored inside a bucket.
279
     *
280 3
     * @param array $options
281
     *
282
     * @return array
283
     *
284 3
     * @throws \Exception
285
     *
286
     * @throws GuzzleException
287 6
     */
288
    public function listFiles(array $options)
289 6
    {
290
        // if FileName is set, we only attempt to retrieve information about that single file.
291 4
        $fileName = !empty($options['FileName']) ? $options['FileName'] : null;
292
293
        $nextFileName = null;
294
        $maxFileCount = 1000;
295
296
        $prefix = isset($options['Prefix']) ? $options['Prefix'] : '';
297
        $delimiter = isset($options['Delimiter']) ? $options['Delimiter'] : null;
298
299
        $files = [];
300
301 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
302
            $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...
303
        }
304 2
305
        if ($fileName) {
306 2
            $nextFileName = $fileName;
307 2
            $maxFileCount = 1;
308
        }
309 2
310 2
        $this->authorizeAccount();
311
312 2
        // B2 returns, at most, 1000 files per "page". Loop through the pages and compile an array of File objects.
313
        while (true) {
314 2
            $response = $this->generateAuthenticatedClient([
315
                'bucketId'      => $options['BucketId'],
316
                'startFileName' => $nextFileName,
317
                'maxFileCount'  => $maxFileCount,
318 2
                'prefix'        => $prefix,
319
                'delimiter'     => $delimiter,
320
            ])->request('POST', '/b2_list_file_names');
321
322
            foreach ($response['files'] as $file) {
323 2
                // if we have a file name set, only retrieve information if the file name matches
324
                if (!$fileName || ($fileName === $file['fileName'])) {
325
                    $files[] = new File($file['fileId'], $file['fileName'], null, $file['size']);
326 2
                }
327 2
            }
328
329 2
            if ($fileName || $response['nextFileName'] === null) {
330
                // We've got all the files - break out of loop.
331
                break;
332 2
            }
333 2
334 2
            $nextFileName = $response['nextFileName'];
335 2
        }
336 2
337
        return $files;
338
    }
339
340 2
    /**
341
     * Test whether a file exists in B2 for the given bucket.
342 1
     *
343 1
     * @param array $options
344
     *
345
     * @return bool
346
     * @throws \Exception
347 2
     *
348
     * @throws GuzzleException
349 2
     */
350
    public function fileExists(array $options)
351
    {
352 1
        $files = $this->listFiles($options);
353
354
        return !empty($files);
355 2
    }
356
357
    /**
358
     * Returns a single File object representing a file stored on B2.
359
     *
360
     * @param array $options
361
     *
362
     * @throws NotFoundException If no file id was provided and BucketName + FileName does not resolve to a file, a NotFoundException is thrown.
363
     *
364
     * @return File
365
     */
366
    public function getFile(array $options)
367
    {
368
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
369
            $options['FileId'] = $this->getFileIdFromBucketAndFileName($options['BucketName'], $options['FileName']);
370
371
            if (!$options['FileId']) {
372
                throw new NotFoundException();
373
            }
374
        }
375
376
        $response = $this->generateAuthenticatedClient([
377
            'fileId' => $options['FileId'],
378
        ])->request('POST', '/b2_get_file_info');
379
380
        return new File(
381 4
            $response['fileId'],
382
            $response['fileName'],
383 4
            $response['contentSha1'],
384
            $response['contentLength'],
385
            $response['contentType'],
386
            $response['fileInfo'],
387
            $response['bucketId'],
388
            $response['action'],
389
            $response['uploadTimestamp']
390
        );
391 4
    }
392
393 4
    /**
394
     * Deletes the file identified by ID from Backblaze B2.
395 4
     *
396
     * @param array $options
397
     *
398 4
     * @return bool
399
     *
400
     * @throws NotFoundException
401
     *
402 3
     * @throws GuzzleException
403 3
     */
404 3
    public function deleteFile(array $options)
405 3
    {
406 3
        if (!isset($options['FileName'])) {
407 3
            $file = $this->getFile($options);
408 3
409 3
            $options['FileName'] = $file->getName();
410 3
        }
411 3
412
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
413
            $file = $this->getFile($options);
414
415
            $options['FileId'] = $file->getId();
416
        }
417
418
        try {
419
            $this->generateAuthenticatedClient([
420
                'fileName' => $options['FileName'],
421
                'fileId' => $options['FileId'],
422 3
            ])->request('POST', '/b2_delete_file_version');
423
        } catch (\Exception $e) {
424 3
            return false;
425 2
        }
426
427 2
        return true;
428
    }
429
430 3
    /**
431
     * Authorize the B2 account in order to get an auth token and API/download URLs.
432
     *
433
     * @throws \Exception
434
     */
435
    protected function authorizeAccount()
436 3
    {
437
        if (Carbon::now('UTC')->timestamp > $this->reAuthTime->timestamp) {
438 3
            $response = $this->client->request('GET', 'https://api.backblazeb2.com/b2api/v1/b2_authorize_account', [
439
                'auth' => [$this->accountId, $this->applicationKey],
440 3
            ]);
441
442
            $this->authToken = $response['authorizationToken'];
443 3
            $this->apiUrl = $response['apiUrl'].'/b2api/v1';
444 3
            $this->downloadUrl = $response['downloadUrl'];
445
            $this->reAuthTime = Carbon::now('UTC');
446
            $this->reAuthTime->addSeconds($this->authTimeoutSeconds);
447
        }
448 2
    }
449
450
    /**
451
     * Maps the provided bucket name to the appropriate bucket ID.
452
     *
453
     * @param $name
454
     *
455
     * @return null
456 28
     *
457
     * @throws \Exception
458 28
     */
459 28
    protected function getBucketIdFromName($name)
460 28
    {
461
        $buckets = $this->listBuckets();
462
463 28
        foreach ($buckets as $bucket) {
464 28
            if ($bucket->getName() === $name) {
465 28
                return $bucket->getId();
466 28
            }
467 28
        }
468
    }
469 28
470
    /**
471
     * Maps the provided bucket ID to the appropriate bucket name.
472
     *
473
     * @param $id
474
     *
475
     * @return null
476
     *
477
     * @throws \Exception
478
     */
479
    protected function getBucketNameFromId($id)
480
    {
481
        $buckets = $this->listBuckets();
482
483
        foreach ($buckets as $bucket) {
484
            if ($bucket->getId() === $id) {
485
                return $bucket->getName();
486
            }
487
        }
488
    }
489
490
    protected function getFileIdFromBucketAndFileName($bucketName, $fileName)
491
    {
492
        $files = $this->listFiles([
493
            'BucketName' => $bucketName,
494
            'FileName'   => $fileName,
495
        ]);
496
497
        foreach ($files as $file) {
498
            if ($file->getName() === $fileName) {
499
                return $file->getId();
500
            }
501
        }
502
    }
503
504
    /**
505
     * Uploads a large file using b2 large file proceedure.
506
     *
507
     * @param array $options
508
     *
509
     * @return \BackblazeB2\File
510
     *
511
     * @throws \Exception
512
     * @throws GuzzleException
513
     */
514
    public function uploadLargeFile(array $options)
515
    {
516
        if (substr($options['FileName'], 0, 1) === '/') {
517
            $options['FileName'] = ltrim($options['FileName'], '/');
518
        }
519
520
        //if last char of path is not a "/" then add a "/"
521
        if (substr($options['FilePath'], -1) != '/') {
522
            $options['FilePath'] = $options['FilePath'].'/';
523
        }
524
525
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
526
            $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...
527
        }
528
529
        if (!isset($options['FileContentType'])) {
530
            $options['FileContentType'] = 'b2/x-auto';
531
        }
532
533
        $this->authorizeAccount();
534
535
        // 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...
536
        $start = $this->startLargeFile($options['FileName'], $options['FileContentType'], $options['BucketId']);
537
538
        // 2) b2_get_upload_part_url for each thread uploading (takes fileId)
539
        $url = $this->getUploadPartUrl($start['fileId']);
540
541
        // 3) b2_upload_part for each part of the file
542
        $parts = $this->uploadParts($options['FilePath'].$options['FileName'], $url['uploadUrl'], $url['authorizationToken']);
543
544
        $sha1s = [];
545
546
        foreach ($parts as $part) {
547
            $sha1s[] = $part['contentSha1'];
548
        }
549
550
        // 4) b2_finish_large_file.
551
        return $this->finishLargeFile($start['fileId'], $sha1s);
552
    }
553
554
    /**
555
     * starts the large file upload process.
556
     *
557
     * @param $fileName
558
     * @param $contentType
559
     * @param $bucketId
560
     *
561
     * @return array
562
     * @throws GuzzleException
563
     */
564
    protected function startLargeFile($fileName, $contentType, $bucketId)
565
    {
566
        $response = $this->generateAuthenticatedClient([
567
            'fileName'      => $fileName,
568
            'contentType'   => $contentType,
569
            'bucketId'      => $bucketId,
570
        ])->request('POST', '/b2_start_large_file');
571
572
        return $response;
573
    }
574
575
    /**
576
     * gets the url for the next large file part upload.
577
     *
578
     * @param $fileId
579
     *
580
     * @return array
581
     * @throws GuzzleException
582
     */
583
    protected function getUploadPartUrl($fileId)
584
    {
585
        $response = $this->generateAuthenticatedClient([
586
            'fileId' => $fileId,
587
        ])->request('POST', '/b2_get_upload_part_url');
588
589
        return $response;
590
    }
591
592
    /**
593
     * uploads the file as "parts" of 100MB each.
594
     *
595
     * @param $filePath
596
     * @param $uploadUrl
597
     * @param $largeFileAuthToken
598
     *
599
     * @return array
600
     */
601
    protected function uploadParts($filePath, $uploadUrl, $largeFileAuthToken)
602
    {
603
        $return = [];
604
605
        $minimum_part_size = 100 * (1000 * 1000);
606
607
        $local_file_size = filesize($filePath);
608
        $total_bytes_sent = 0;
609
        $bytes_sent_for_part = $minimum_part_size;
610
        $sha1_of_parts = [];
611
        $part_no = 1;
612
        $file_handle = fopen($filePath, 'r');
613
614
        while ($total_bytes_sent < $local_file_size) {
615
616
            // Determine the number of bytes to send based on the minimum part size
617
            if (($local_file_size - $total_bytes_sent) < $minimum_part_size) {
618
                $bytes_sent_for_part = ($local_file_size - $total_bytes_sent);
619
            }
620
621
            // Get a sha1 of the part we are going to send
622
            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

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

623
            $data_part = fread(/** @scrutinizer ignore-type */ $file_handle, $bytes_sent_for_part);
Loading history...
624
            array_push($sha1_of_parts, sha1($data_part));
625
            fseek($file_handle, $total_bytes_sent);
626
627
            $response = $this->client->request('POST', $uploadUrl, [
628
                'headers' => [
629
                    'Authorization'                      => $largeFileAuthToken,
630
                    'Content-Length'                     => $bytes_sent_for_part,
631
                    'X-Bz-Part-Number'                   => $part_no,
632
                    'X-Bz-Content-Sha1'                  => $sha1_of_parts[$part_no - 1],
633
                ],
634
                'body' => $data_part,
635
            ]);
636
637
            $return[] = $response;
638
639
            // Prepare for the next iteration of the loop
640
            $part_no++;
641
            $total_bytes_sent = $bytes_sent_for_part + $total_bytes_sent;
642
        }
643
644
        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

644
        fclose(/** @scrutinizer ignore-type */ $file_handle);
Loading history...
645
646
        return $return;
647
    }
648
649
    /**
650
     * finishes the large file upload proceedure.
651
     *
652
     * @param $fileId
653
     * @param array $sha1s
654
     *
655
     * @return File
656
     * @throws GuzzleException
657
     */
658
    protected function finishLargeFile($fileId, array $sha1s)
659
    {
660
        $response = $this->generateAuthenticatedClient([
661
            'fileId'        => $fileId,
662
            'partSha1Array' => $sha1s,
663
        ])->request('POST', '/b2_finish_large_file');
664
665
        return new File(
666
            $response['fileId'],
667
            $response['fileName'],
668
            $response['contentSha1'],
669
            $response['contentLength'],
670
            $response['contentType'],
671
            $response['fileInfo'],
672
            $response['bucketId'],
673
            $response['action'],
674
            $response['uploadTimestamp']
675
        );
676
    }
677
678
    /*
679
     * Generates Authenticated Guzzle Client
680
     */
681
    protected function generateAuthenticatedClient($json = [])
682
    {
683
        $this->authorizeAccount();
684
685
        $client = new \GuzzleHttp\Client([
686
            'base_uri' => $apiUrl,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $apiUrl seems to be never defined.
Loading history...
687
            'headers' => [
688
                'Authorization' => $this->authToken,
689
            ],
690
            'json' => $json
691
        ]);
692
693
        return $client;
694
    }
695
}
696