ResumeUploader::uploadV1()   F
last analyzed

Complexity

Conditions 30
Paths 636

Size

Total Lines 113
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 30

Importance

Changes 0
Metric Value
cc 30
eloc 71
nc 636
nop 2
dl 0
loc 113
ccs 4
cts 4
cp 1
crap 30
rs 0.5054
c 0
b 0
f 0

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 Qiniu\Storage;
4
5
use Qiniu\Config;
6
use Qiniu\Http\Client;
7
use Qiniu\Http\Error;
8
use Qiniu\Enum\SplitUploadVersion;
9
use Qiniu\Http\RequestOptions;
10
11
/**
12
 * 断点续上传类, 该类主要实现了断点续上传中的分块上传,
13
 * 以及相应地创建块和创建文件过程.
14
 *
15
 * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html
16
 * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html
17
 */
18
final class ResumeUploader
19
{
20
    private $upToken;
21
    private $key;
22
    private $inputStream;
23
    private $size;
24
    private $params;
25
    private $mime;
26
    private $contexts;
27
    private $finishedEtags;
28
    private $host;
29
    private $bucket;
30
    private $currentUrl;
31
    private $config;
32
    private $resumeRecordFile;
33
    private $version;
34
    private $partSize;
35
    /**
36
     * @var RequestOptions
37
     */
38
    private $reqOpt;
39
40 6
    /**
41
     * 上传二进制流到七牛
42
     *
43
     * @param string $upToken 上传凭证
44
     * @param string $key 上传文件名
45
     * @param resource $inputStream 上传二进制流
46
     * @param int $size 上传流的大小
47
     * @param array<string, string> $params 自定义变量
48
     * @param string $mime 上传数据的mimeType
49
     * @param Config $config
50 6
     * @param string $resumeRecordFile 断点续传的已上传的部分信息记录文件
51 6
     * @param string $version 分片上传版本 目前支持v1/v2版本 默认v1
52 6
     * @param int $partSize 分片上传v2字段 默认大小为4MB 分片大小范围为1 MB - 1 GB
53 6
     * @param RequestOptions $reqOpt 分片上传v2字段 默认大小为4MB 分片大小范围为1 MB - 1 GB
54 6
     * @throws \Exception
55 6
     *
56 6
     * @link http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
57 6
     */
58
    public function __construct(
59 6
        $upToken,
60 6
        $key,
61
        $inputStream,
62
        $size,
63
        $params,
64 6
        $mime,
65 6
        $config,
66
        $resumeRecordFile = null,
67
        $version = 'v1',
68 6
        $partSize = config::BLOCK_SIZE,
69 6
        $reqOpt = null
70
    ) {
71
72
        $this->upToken = $upToken;
73
        $this->key = $key;
74 6
        $this->inputStream = $inputStream;
75
        $this->size = $size;
76 6
        $this->params = $params;
77 6
        $this->mime = $mime;
78 6
        $this->contexts = array();
79 6
        $this->finishedEtags = array("etags" => array(), "uploadId" => "", "expiredAt" => 0, "uploaded" => 0);
80 6
        $this->config = $config;
81
        $this->resumeRecordFile = $resumeRecordFile ? $resumeRecordFile : null;
82
        $this->partSize = $partSize ? $partSize : config::BLOCK_SIZE;
83 6
84 6
        if ($reqOpt === null) {
85 6
            $reqOpt = new RequestOptions();
86 6
        }
87 6
        $this->reqOpt = $reqOpt;
88 6
89 6
        try {
90 3
            $this->version = SplitUploadVersion::from($version ? $version : 'v1');
91 3
        } catch (\Exception $e) {
92
            throw new \Exception("only support v1/v2 now!", 0, $e);
93
        }
94
95 3
        list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
96 3
        $this->bucket = $bucket;
97 3
        if ($err != null) {
98 6
            return array(null, $err);
99 3
        }
100 3
101 3
        list($upHost, $err) = $config->getUpHostV2($accessKey, $bucket, $reqOpt);
102
        if ($err != null) {
103 6
            throw new \Exception($err->message(), 1);
104
        }
105
        $this->host = $upHost;
106 6
    }
107 6
108 6
    /**
109 6
     * 上传操作
110
     * @param $fname string 文件名
111
     *
112
     * @throws \Exception
113
     */
114
    public function upload($fname)
115 6
    {
116
        $blkputRets = null;
117 6
        // get upload record from resumeRecordFile
118 6
        if ($this->resumeRecordFile != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $this->resumeRecordFile of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
119
            if (file_exists($this->resumeRecordFile)) {
120
                $stream = fopen($this->resumeRecordFile, 'r');
121 6
                if ($stream) {
0 ignored issues
show
introduced by
$stream is of type resource, thus it always evaluated to false.
Loading history...
122
                    $streamLen = filesize($this->resumeRecordFile);
123 6
                    if ($streamLen > 0) {
124 6
                        $contents = fread($stream, $streamLen);
125 6
                        fclose($stream);
126 6
                        if ($contents) {
127 6
                            $blkputRets = json_decode($contents, true);
128 6
                            if ($blkputRets === null) {
129 6
                                error_log("resumeFile contents decode error");
130
                            }
131
                        } else {
132
                            error_log("read resumeFile failed");
133
                        }
134
                    } else {
135 6
                        error_log("resumeFile is empty");
136
                    }
137
                } else {
138
                    error_log("resumeFile open failed");
139
                }
140
            } else {
141 6
                error_log("resumeFile not exists");
142
            }
143 6
        }
144 6
145 6
        if ($this->version == SplitUploadVersion::V1) {
0 ignored issues
show
introduced by
The condition $this->version == Qiniu\...\SplitUploadVersion::V1 is always false.
Loading history...
146 6
            return $this->uploadV1($fname, $blkputRets);
147
        } elseif ($this->version == SplitUploadVersion::V2) {
0 ignored issues
show
introduced by
The condition $this->version == Qiniu\...\SplitUploadVersion::V2 is always false.
Loading history...
148
            return $this->uploadV2($fname, $blkputRets);
149 6
        } else {
150
            throw new \Exception("only support v1/v2 now!");
151
        }
152 6
    }
153
154
    /**
155 6
     * @param string $fname 文件名
156
     * @param null|array $blkputRets
157 6
     *
158 6
     * @throws \Exception
159 6
     */
160
    private function uploadV1($fname, $blkputRets = null)
161
    {
162 6
        // 尝试恢复恢复已上传的数据
163
        $isResumeUpload = $blkputRets !== null;
164 6
        $this->contexts = array();
165 6
166
        if ($blkputRets) {
167 6
            if (isset($blkputRets['contexts']) && isset($blkputRets['uploaded']) &&
168
                is_array($blkputRets['contexts']) && is_int($blkputRets['uploaded'])
169
            ) {
170
                $this->contexts = array_map(function ($ctx) {
171
                    if (is_array($ctx)) {
172
                        return $ctx;
173
                    } else {
174
                        // 兼容旧版本(旧版本没有存储 expireAt)
175
                        return array(
176
                            "ctx" => $ctx,
177
                            "expiredAt" => 0,
178
                        );
179
                    }
180
                }, $blkputRets['contexts']);
181
            }
182
        }
183
184
        // 上传分片
185
        $uploaded = 0;
186
        while ($uploaded < $this->size) {
187
            $blockSize = $this->blockSize($uploaded);
188
            $blockIndex = $uploaded / $this->partSize;
189
            if (!is_int($blockIndex)) {
190
                throw new \Exception("v1 part size changed");
191
            }
192
            // 如果已上传该分片且没有过期
193
            if (isset($this->contexts[$blockIndex]) && $this->contexts[$blockIndex]["expiredAt"] >= time()) {
194
                $uploaded += $blockSize;
195
                fseek($this->inputStream, $blockSize, SEEK_CUR);
196
                continue;
197
            }
198
            $data = fread($this->inputStream, $blockSize);
199
            if ($data === false) {
200
                throw new \Exception("file read failed", 1);
201
            }
202
            $crc = \Qiniu\crc32_data($data);
203
            $response = $this->makeBlock($data, $blockSize);
204
205
206
            $ret = null;
207
            if ($response->ok() && $response->json() != null) {
208
                $ret = $response->json();
209
            }
210
            if ($response->statusCode < 0) {
211
                list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
212
                if ($err != null) {
213
                    return array(null, $err);
214
                }
215
                list($upHostBackup, $err) = $this->config->getUpBackupHostV2($accessKey, $bucket, $this->reqOpt);
216
                if ($err != null) {
217
                    return array(null, $err);
218
                }
219
                $this->host = $upHostBackup;
220
            }
221
222
            if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
223
                $response = $this->makeBlock($data, $blockSize);
224
                $ret = $response->json();
225
            }
226
            if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
227
                return array(null, new Error($this->currentUrl, $response));
228
            }
229
230
            // 如果可以在已上传取到说明是过期分片直接修改已上传信息,否则是新的片添加到已上传分片尾部
231
            if (isset($this->contexts[$blockIndex])) {
232
                $this->contexts[$blockIndex] = array(
233
                    'ctx' => $ret['ctx'],
234
                    'expiredAt' => $ret['expired_at'],
235
                );
236
            } else {
237
                array_push($this->contexts, array(
238
                    'ctx' => $ret['ctx'],
239
                    'expiredAt' => $ret['expired_at'],
240
                ));
241
            }
242
            $uploaded += $blockSize;
243
244
            // 记录断点
245
            if ($this->resumeRecordFile !== null) {
246
                $recordData = array(
247
                    'contexts' => $this->contexts,
248
                    'uploaded' => $uploaded
249
                );
250
                $recordData = json_encode($recordData);
251
252
                if ($recordData) {
253
                    $isWritten = file_put_contents($this->resumeRecordFile, $recordData);
254
                    if ($isWritten === false) {
255
                        error_log("write resumeRecordFile failed");
256
                    }
257
                } else {
258
                    error_log('resumeRecordData encode failed');
259
                }
260
            }
261
        }
262
263
        // 完成上传
264
        list($ret, $err) = $this->makeFile($fname);
265
        if ($err !== null) {
266
            $response = $err->getResponse();
267
            if ($isResumeUpload && $response->statusCode === 701) {
268
                fseek($this->inputStream, 0);
269
                return $this->uploadV1($fname);
270
            }
271
        }
272
        return array($ret, $err);
273
    }
274
275
    /**
276
     * @param string $fname 文件名
277
     * @param null|array $blkputRets
278
     *
279
     * @throws \Exception
280
     */
281
    private function uploadV2($fname, $blkputRets = null)
282
    {
283
        $uploaded = 0;
284
        $partNumber = 1;
285
        $encodedObjectName = $this->key ? \Qiniu\base64_urlSafeEncode($this->key) : '~';
286
        $isResumeUpload = $blkputRets !== null;
287
288
        // 初始化 upload id
289
        $err = null;
290
        if ($blkputRets) {
291
            if (isset($blkputRets["etags"]) && isset($blkputRets["uploadId"]) &&
292
                isset($blkputRets["expiredAt"]) && $blkputRets["expiredAt"] > time() &&
293
                $blkputRets["uploaded"] > 0 && is_array($blkputRets["etags"]) &&
294
                is_string($blkputRets["uploadId"]) && is_int($blkputRets["expiredAt"])
295
            ) {
296
                $this->finishedEtags['etags'] = $blkputRets["etags"];
297
                $this->finishedEtags["uploadId"] = $blkputRets["uploadId"];
298
                $this->finishedEtags["expiredAt"] = $blkputRets["expiredAt"];
299
                $this->finishedEtags["uploaded"] = $blkputRets["uploaded"];
300
                $uploaded = $blkputRets["uploaded"];
301
                $partNumber = count($this->finishedEtags["etags"]) + 1;
302
            } else {
303
                $err = $this->makeInitReq($encodedObjectName);
304
            }
305
        } else {
306
            $err = $this->makeInitReq($encodedObjectName);
307
        }
308
        if ($err != null) {
309
            return array(null, $err);
310
        }
311
312
        // 上传分片
313
        fseek($this->inputStream, $uploaded);
314
        while ($uploaded < $this->size) {
315
            $blockSize = $this->blockSize($uploaded);
316
            $data = fread($this->inputStream, $blockSize);
317
            if ($data === false) {
318
                throw new \Exception("file read failed", 1);
319
            }
320
            $md5 = md5($data);
321
            $response = $this->uploadPart(
322
                $data,
323
                $partNumber,
324
                $this->finishedEtags["uploadId"],
325
                $encodedObjectName,
326
                $md5
327
            );
328
329
            $ret = null;
330
            if ($response->ok() && $response->json() != null) {
331
                $ret = $response->json();
332
            }
333
            if ($response->statusCode < 0) {
334
                list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
335
                if ($err != null) {
336
                    return array(null, $err);
337
                }
338
                list($upHostBackup, $err) = $this->config->getUpBackupHostV2($accessKey, $bucket, $this->reqOpt);
339
                if ($err != null) {
340
                    return array(null, $err);
341
                }
342
                $this->host = $upHostBackup;
343
            }
344
345
            if ($response->needRetry() || !isset($ret['md5']) || $md5 != $ret['md5']) {
346
                $response = $this->uploadPart(
347
                    $data,
348
                    $partNumber,
349
                    $this->finishedEtags["uploadId"],
350
                    $encodedObjectName,
351
                    $md5
352
                );
353
                $ret = $response->json();
354
            }
355
            if ($isResumeUpload && $response->statusCode === 612) {
356
                return $this->uploadV2($fname);
357
            }
358
            if (!$response->ok() || !isset($ret['md5']) || $md5 != $ret['md5']) {
359
                return array(null, new Error($this->currentUrl, $response));
360
            }
361
            $blockStatus = array('etag' => $ret['etag'], 'partNumber' => $partNumber);
362
            array_push($this->finishedEtags['etags'], $blockStatus);
363
            $partNumber += 1;
364
365
            $uploaded += $blockSize;
366
            $this->finishedEtags['uploaded'] = $uploaded;
367
368
            if ($this->resumeRecordFile !== null) {
369
                $recordData = json_encode($this->finishedEtags);
370
                if ($recordData) {
371
                    $isWritten = file_put_contents($this->resumeRecordFile, $recordData);
372
                    if ($isWritten === false) {
373
                        error_log("write resumeRecordFile failed");
374
                    }
375
                } else {
376
                    error_log('resumeRecordData encode failed');
377
                }
378
            }
379
        }
380
381
        list($ret, $err) = $this->completeParts($fname, $this->finishedEtags['uploadId'], $encodedObjectName);
0 ignored issues
show
Bug introduced by
$this->finishedEtags['uploadId'] of type string is incompatible with the type integer expected by parameter $uploadId of Qiniu\Storage\ResumeUploader::completeParts(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

381
        list($ret, $err) = $this->completeParts($fname, /** @scrutinizer ignore-type */ $this->finishedEtags['uploadId'], $encodedObjectName);
Loading history...
382
        if ($err !== null) {
383
            $response = $err->getResponse();
384
            if ($isResumeUpload && $response->statusCode === 612) {
385
                return $this->uploadV2($fname);
386
            }
387
        }
388
        return array($ret, $err);
389
    }
390
391
    /**
392
     * 创建块
393
     */
394
    private function makeBlock($block, $blockSize)
395
    {
396
        $url = $this->host . '/mkblk/' . $blockSize;
397
        return $this->post($url, $block);
398
    }
399
400
    private function fileUrl($fname)
401
    {
402
        $url = $this->host . '/mkfile/' . $this->size;
403
        $url .= '/mimeType/' . \Qiniu\base64_urlSafeEncode($this->mime);
404
        if ($this->key != null) {
405
            $url .= '/key/' . \Qiniu\base64_urlSafeEncode($this->key);
406
        }
407
        $url .= '/fname/' . \Qiniu\base64_urlSafeEncode($fname);
408
        if (!empty($this->params)) {
409
            foreach ($this->params as $key => $value) {
410
                $val = \Qiniu\base64_urlSafeEncode($value);
411
                $url .= "/$key/$val";
412
            }
413
        }
414
        return $url;
415
    }
416
417
    /**
418
     * 创建文件
419
     *
420
     * @param string $fname 文件名
421
     * @return array{array | null, Error | null}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{array | null, Error | null} at position 2 could not be parsed: Expected ':' at position 2, but found 'array'.
Loading history...
422
     */
423
    private function makeFile($fname)
424
    {
425
        $url = $this->fileUrl($fname);
426
        $body = implode(',', array_map(function ($ctx) {
427
            return $ctx['ctx'];
428
        }, $this->contexts));
429
        $response = $this->post($url, $body);
430
        if ($response->needRetry()) {
431
            $response = $this->post($url, $body);
432
        }
433
        if ($response->statusCode === 200 || $response->statusCode === 701) {
434
            if ($this->resumeRecordFile !== null) {
435
                @unlink($this->resumeRecordFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

435
                /** @scrutinizer ignore-unhandled */ @unlink($this->resumeRecordFile);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
436
            }
437
        }
438
        if (!$response->ok()) {
439
            return array(null, new Error($this->currentUrl, $response));
440
        }
441
        return array($response->json(), null);
442
    }
443
444
    private function post($url, $data)
445
    {
446
        $this->currentUrl = $url;
447
        $headers = array('Authorization' => 'UpToken ' . $this->upToken);
448
        return Client::post($url, $data, $headers, $this->reqOpt);
449
    }
450
451
    private function blockSize($uploaded)
452
    {
453
        if ($this->size < $uploaded + $this->partSize) {
454
            return $this->size - $uploaded;
455
        }
456
        return $this->partSize;
457
    }
458
459
    private function makeInitReq($encodedObjectName)
460
    {
461
        list($ret, $err) = $this->initReq($encodedObjectName);
462
463
        if ($ret == null) {
464
            return $err;
465
        }
466
467
        $this->finishedEtags["uploadId"] = $ret['uploadId'];
468
        $this->finishedEtags["expiredAt"] = $ret['expireAt'];
469
        return $err;
470
    }
471
472
    /**
473
     * 初始化上传任务
474
     */
475
    private function initReq($encodedObjectName)
476
    {
477
        $url = $this->host . '/buckets/' . $this->bucket . '/objects/' . $encodedObjectName . '/uploads';
478
        $headers = array(
479
            'Authorization' => 'UpToken ' . $this->upToken,
480
            'Content-Type' => 'application/json'
481
        );
482
        $response = $this->postWithHeaders($url, null, $headers);
483
        $ret = $response->json();
484
        if ($response->ok() && $ret != null) {
485
            return array($ret, null);
486
        }
487
488
        return array(null, new Error($url, $response));
489
    }
490
491
    /**
492
     * 分块上传v2
493
     */
494
    private function uploadPart($block, $partNumber, $uploadId, $encodedObjectName, $md5)
495
    {
496
        $headers = array(
497
            'Authorization' => 'UpToken ' . $this->upToken,
498
            'Content-Type' => 'application/octet-stream',
499
            'Content-MD5' => $md5
500
        );
501
        $url = $this->host . '/buckets/' . $this->bucket . '/objects/' . $encodedObjectName .
502
            '/uploads/' . $uploadId . '/' . $partNumber;
503
        $response = $this->put($url, $block, $headers);
504
        if ($response->statusCode === 612) {
505
            if ($this->resumeRecordFile !== null) {
506
                @unlink($this->resumeRecordFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

506
                /** @scrutinizer ignore-unhandled */ @unlink($this->resumeRecordFile);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
507
            }
508
        }
509
        return $response;
510
    }
511
512
    /**
513
     * 完成分片上传V2
514
     *
515
     * @param string $fname 文件名
516
     * @param int $uploadId 由 {@see initReq} 获取
517
     * @param string $encodedObjectName 经过编码的存储路径
518
     * @return array{array | null, Error | null}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{array | null, Error | null} at position 2 could not be parsed: Expected ':' at position 2, but found 'array'.
Loading history...
519
     */
520
    private function completeParts($fname, $uploadId, $encodedObjectName)
521
    {
522
        $headers = array(
523
            'Authorization' => 'UpToken ' . $this->upToken,
524
            'Content-Type' => 'application/json'
525
        );
526
        $etags = $this->finishedEtags['etags'];
527
        $sortedEtags = \Qiniu\arraySort($etags, 'partNumber');
528
        $metadata = array();
529
        $customVars = array();
530
        if ($this->params) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->params of type array<string,string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
531
            foreach ($this->params as $k => $v) {
532
                if (strpos($k, 'x:') === 0) {
533
                    $customVars[$k] = $v;
534
                } elseif (strpos($k, 'x-qn-meta-') === 0) {
535
                    $metadata[$k] = $v;
536
                }
537
            }
538
        }
539
        if (empty($metadata)) {
540
            $metadata = null;
541
        }
542
        if (empty($customVars)) {
543
            $customVars = null;
544
        }
545
        $body = array(
546
            'fname' => $fname,
547
            'mimeType' => $this->mime,
548
            'metadata' => $metadata,
549
            'customVars' => $customVars,
550
            'parts' => $sortedEtags
551
        );
552
        $jsonBody = json_encode($body);
553
        $url = $this->host . '/buckets/' . $this->bucket . '/objects/' . $encodedObjectName . '/uploads/' . $uploadId;
554
        $response = $this->postWithHeaders($url, $jsonBody, $headers);
555
        if ($response->needRetry()) {
556
            $response = $this->postWithHeaders($url, $jsonBody, $headers);
557
        }
558
        if ($response->statusCode === 200 || $response->statusCode === 612) {
559
            if ($this->resumeRecordFile !== null) {
560
                @unlink($this->resumeRecordFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

560
                /** @scrutinizer ignore-unhandled */ @unlink($this->resumeRecordFile);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
561
            }
562
        }
563
        if (!$response->ok()) {
564
            return array(null, new Error($this->currentUrl, $response));
565
        }
566
        return array($response->json(), null);
567
    }
568
569
    private function put($url, $data, $headers)
570
    {
571
        $this->currentUrl = $url;
572
        return Client::put($url, $data, $headers, $this->reqOpt);
573
    }
574
575
    private function postWithHeaders($url, $data, $headers)
576
    {
577
        $this->currentUrl = $url;
578
        return Client::post($url, $data, $headers, $this->reqOpt);
579
    }
580
}
581