ResumeUploader::__construct()   B
last analyzed

Complexity

Conditions 8
Paths 32

Size

Total Lines 48
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 8.2037

Importance

Changes 0
Metric Value
cc 8
eloc 26
nc 32
nop 11
dl 0
loc 48
ccs 29
cts 34
cp 0.8529
crap 8.2037
rs 8.4444
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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