Completed
Push — master ( bfc0cc...f0e226 )
by sabaku
04:05
created

upyun.class.php (24 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
class UpYun
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
3
{
4
    const VERSION = '2.2.0';
5
6
    const ED_AUTO = 'v0.api.upyun.com';
7
    const ED_TELECOM = 'v1.api.upyun.com';
8
    const ED_CNC = 'v2.api.upyun.com';
9
    const ED_CTT = 'v3.api.upyun.com';
10
11
    const CONTENT_TYPE = 'Content-Type';
12
    const CONTENT_MD5 = 'Content-MD5';
13
    const CONTENT_SECRET = 'Content-Secret';
14
15
    // 缩略图
16
    const X_GMKERL_THUMBNAIL = 'x-gmkerl-thumbnail';
17
    const X_GMKERL_TYPE = 'x-gmkerl-type';
18
    const X_GMKERL_VALUE = 'x-gmkerl-value';
19
    const X_GMKERL_QUALITY = 'x­gmkerl-quality';
20
    const X_GMKERL_UNSHARP = 'x­gmkerl-unsharp';
21
22
23
    private $_bucketname;
24
    private $_username;
25
    private $_password;
26
    private $_timeout = 30;
27
    private $_file_secret = null;
28
    private $_content_md5 = null;
29
30
    protected $endpoint;
31
32
    /**
33
     * @var string: UPYUN 请求唯一id, 出现错误时, 可以将该id报告给 UPYUN,进行调试
34
     */
35
    private $x_request_id;
36
37
    /**
38
     * 初始化 UpYun 存储接口
39
     * @param $bucketname string 空间名称
40
     * @param $username string 操作员名称
41
     * @param $password string 密码
42
     *
43
     * @param null $endpoint
44
     * @param int $timeout
45
     */
46
    public function __construct($bucketname, $username, $password, $endpoint = NULL, $timeout = 30)
47
    {
48
        $this->_bucketname = $bucketname;
49
        $this->_username = $username;
50
        $this->_password = md5($password);
51
        $this->_timeout = $timeout;
52
53
        $this->endpoint = is_null($endpoint) ? self::ED_AUTO : $endpoint;
54
    }
55
56
    /**
57
     * 获取当前SDK版本号
58
     */
59
    public function version()
60
    {
61
        return self::VERSION;
62
    }
63
64
    /**
65
     * 创建目录
66
     * @param $path string 路径
67
     * @param $auto_mkdir bool 是否自动创建父级目录,最多10层次
68
     *
69
     * @return mixed
70
     */
71
    public function makeDir($path, $auto_mkdir = true)
72
    {
73
        $headers = array('Folder' => 'true');
74
        if ($auto_mkdir) $headers['Mkdir'] = 'true';
75
        return $this->_do_request('PUT', $path, $headers);
76
    }
77
78
    /**
79
     * 删除目录和文件
80
     * @param string $path 路径
81
     *
82
     * @return boolean
83
     */
84
    public function delete($path)
85
    {
86
        return $this->_do_request('DELETE', $path);
87
    }
88
89
90
    /**
91
     * 上传文件
92
     * @param string $path 存储路径
93
     * @param mixed $file 需要上传的文件,可以是文件流或者文件内容
94
     * @param boolean $auto_mkdir 自动创建目录
95
     * @param array $opts 可选参数
96
     * @return mixed|null
97
     */
98
    public function writeFile($path, $file, $auto_mkdir = true, $opts = NULL)
99
    {
100
        if (is_null($opts)) $opts = array();
101
102
        if (!is_null($this->_content_md5)) $opts[self::CONTENT_MD5] = $this->_content_md5;
103
        if (!is_null($this->_file_secret)) $opts[self::CONTENT_SECRET] = $this->_file_secret;
104
105
        if ($auto_mkdir === true) $opts['Mkdir'] = 'true';
106
107
        return $this->_do_request('PUT', $path, $opts, $file);
108
    }
109
110
    /**
111
     * 下载文件
112
     * @param string $path 文件路径
113
     * @param mixed $file_handle
114
     *
115
     * @return mixed
116
     */
117
    public function readFile($path, $file_handle = NULL)
118
    {
119
        return $this->_do_request('GET', $path, NULL, NULL, $file_handle);
120
    }
121
122
    /**
123
     * 获取目录文件列表
124
     *
125
     * @param string $path 查询路径
126
     *
127
     * @return mixed
128
     */
129
    public function getList($path = '/')
130
    {
131
        $rsp = $this->_do_request('GET', $path);
132
133
        $list = array();
134
        if ($rsp) {
135
            $rsp = explode("\n", $rsp);
136
            foreach ($rsp as $item) {
137
                @list($name, $type, $size, $time) = explode("\t", trim($item));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
138
                if (!empty($time)) {
139
                    $type = ($type == 'N') ? 'file' : 'folder';
140
                }
141
142
                $item = array(
143
                    'name' => $name,
144
                    'type' => $type,
145
                    'size' => intval($size),
146
                    'time' => intval($time),
147
                );
148
                array_push($list, $item);
149
            }
150
        }
151
152
        return $list;
153
    }
154
155
    /**
156
     * 获取文件、目录信息
157
     *
158
     * @param string $path 路径
159
     *
160
     * @return mixed
161
     */
162
    public function getFileInfo($path)
163
    {
164
        $rsp = $this->_do_request('HEAD', $path);
165
        return $rsp;
166
    }
167
168
    /**
169
     * 获取空间使用情况
170
     * @param string $bucket
171
     * @return mixed
172
     * @throws UpYunAuthorizationException
173
     * @throws UpYunException
174
     * @throws UpYunForbiddenException
175
     * @throws UpYunNotAcceptableException
176
     * @throws UpYunNotFoundException
177
     * @throws UpYunServiceUnavailable
178
     */
179
    public function getFolderUsage($bucket = '/')
180
    {
181
        return $this->_do_request('GET', "{$bucket}?usage");
182
    }
183
184
    /**
185
     * 获取空间存储使用量,单位 byte
186
     */
187
    public function getBucketUsage()
188
    {
189
        return $this->getFolderUsage('/');
190
    }
191
192
    public function getXRequestId()
193
    {
194
        return $this->x_request_id;
195
    }
196
197
    /**
198
     * 设置文件访问密钥
199
     */
200
    public function setFileSecret($str)
201
    {
202
        $this->_file_secret = $str;
203
    }
204
205
    /**
206
     * 这是文件 md5 校验值
207
     */
208
    public function setContentMd5($str)
209
    {
210
        $this->_content_md5 = $str;
211
    }
212
213
    /**
214
     * 连接签名方法
215
     * @param $method string 请求方式 {GET, POST, PUT, DELETE}
216
     * @return string 签名字符串
217
     */
218
    private function sign($method, $uri, $date, $length)
219
    {
220
        //$uri = urlencode($uri);
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
221
        $sign = "{$method}&{$uri}&{$date}&{$length}&{$this->_password}";
222
        return 'UpYun ' . $this->_username . ':' . md5($sign);
223
    }
224
225
    /**
226
     * HTTP REQUEST 封装
227
     * @param string $method HTTP REQUEST方法,包括PUT、POST、GET、OPTIONS、DELETE
228
     * @param string $path 除Bucketname之外的请求路径,包括get参数
229
     * @param array $headers 请求需要的特殊HTTP HEADERS
230
     * @param array $body 需要POST发送的数据
231
     * @param null $file_handle
232
     * @return mixed
233
     * @throws UpYunAuthorizationException
234
     * @throws UpYunException
235
     * @throws UpYunForbiddenException
236
     * @throws UpYunNotAcceptableException
237
     * @throws UpYunNotFoundException
238
     * @throws UpYunServiceUnavailable
239
     */
240
    protected function _do_request($method, $path, $headers = NULL, $body = NULL, $file_handle = NULL)
241
    {
242
        $uri = "/{$this->_bucketname}{$path}";
243
        $ch = curl_init("http://{$this->endpoint}{$uri}");
244
245
        $_headers = array('Expect:');
246
        if (!is_null($headers) && is_array($headers)) {
247
            foreach ($headers as $k => $v) {
248
                array_push($_headers, "{$k}: {$v}");
249
            }
250
        }
251
252
        $length = 0;
253
        $date = gmdate('D, d M Y H:i:s \G\M\T');
254
255
        if (!is_null($body)) {
256
            if (is_resource($body)) {
257
                fseek($body, 0, SEEK_END);
258
                $length = ftell($body);
259
                fseek($body, 0);
260
261
                array_push($_headers, "Content-Length: {$length}");
262
                curl_setopt($ch, CURLOPT_INFILE, $body);
263
                curl_setopt($ch, CURLOPT_INFILESIZE, $length);
264
            } else {
265
                $length = @strlen($body);
266
                array_push($_headers, "Content-Length: {$length}");
267
                curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
268
            }
269
        } else {
270
            array_push($_headers, "Content-Length: {$length}");
271
        }
272
273
        array_push($_headers, "Authorization: {$this->sign($method, $uri, $date, $length)}");
274
        array_push($_headers, "Date: {$date}");
275
276
        curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
277
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->_timeout);
278
        curl_setopt($ch, CURLOPT_HEADER, 1);
279
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
280
        //curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% 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...
281
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
282
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
283
284
        if ($method == 'PUT' || $method == 'POST') {
285
            curl_setopt($ch, CURLOPT_POST, 1);
286
        } else {
287
            curl_setopt($ch, CURLOPT_POST, 0);
288
        }
289
290
        if ($method == 'GET' && is_resource($file_handle)) {
291
            curl_setopt($ch, CURLOPT_HEADER, 0);
292
            curl_setopt($ch, CURLOPT_FILE, $file_handle);
293
        }
294
295
        if ($method == 'HEAD') {
296
            curl_setopt($ch, CURLOPT_NOBODY, true);
297
        }
298
299
        $response = curl_exec($ch);
300
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
301
302
        if ($http_code == 0) throw new UpYunException('Connection Failed', $http_code);
303
304
        curl_close($ch);
305
306
        $header_string = '';
307
        $body = '';
308
309
        if ($method == 'GET' && is_resource($file_handle)) {
310
            $header_string = '';
311
            $body = $response;
312
        } else {
313
            list($header_string, $body) = explode("\r\n\r\n", $response, 2);
314
        }
315
        $this->setXRequestId($header_string);
316
        if ($http_code == 200) {
317
            if ($method == 'GET' && is_null($file_handle)) {
318
                return $body;
319
            } else {
320
                $data = $this->_getHeadersData($header_string);
321
                return count($data) > 0 ? $data : true;
322
            }
323
        } else {
324
            $message = $this->_getErrorMessage($header_string);
325
            if (is_null($message) && $method == 'GET' && is_resource($file_handle)) {
326
                $message = 'File Not Found';
327
            }
328
            switch ($http_code) {
329
                case 401:
330
                    throw new UpYunAuthorizationException($message);
331
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
332
                case 403:
333
                    throw new UpYunForbiddenException($message);
334
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
335
                case 404:
336
                    throw new UpYunNotFoundException($message);
337
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
338
                case 406:
339
                    throw new UpYunNotAcceptableException($message);
340
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
341
                case 503:
342
                    throw new UpYunServiceUnavailable($message);
343
                    break;
0 ignored issues
show
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
344
                default:
345
                    throw new UpYunException($message, $http_code);
346
            }
347
        }
348
    }
349
350
    /**
351
     * 处理HTTP HEADERS中返回的自定义数据
352
     *
353
     * @param string $text header字符串
354
     *
355
     * @return array
356
     */
357
    private function _getHeadersData($text)
358
    {
359
        $headers = explode("\r\n", $text);
360
        $items = array();
361
        foreach ($headers as $header) {
362
            $header = trim($header);
363
            if (stripos($header, 'x-upyun') !== False) {
364
                list($k, $v) = explode(':', $header);
365
                $items[trim($k)] = in_array(substr($k, 8, 5), array('width', 'heigh', 'frame')) ? intval($v) : trim($v);
366
            }
367
        }
368
        return $items;
369
    }
370
371
    /**
372
     * 获取返回的错误信息
373
     *
374
     * @param string $header_string
375
     *
376
     * @return mixed
377
     */
378
    private function _getErrorMessage($header_string)
379
    {
380
        list($status, $stash) = explode("\r\n", $header_string, 2);
0 ignored issues
show
The assignment to $stash is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
381
        list($v, $code, $message) = explode(" ", $status, 3);
0 ignored issues
show
The assignment to $v is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
The assignment to $code is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
382
        return $message . " X-Request-Id: " . $this->getXRequestId();
383
    }
384
385
    private function setXRequestId($header_string)
386
    {
387
        preg_match('~^X-Request-Id: ([0-9a-zA-Z]{32})~ism', $header_string, $result);
388
        $this->x_request_id = isset($result[1]) ? $result[1] : '';
389
    }
390
}
391
392
393
class UpYunException extends Exception
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
394
{
395
    public function __construct($message, $code, Exception $previous = null)
396
    {
397
        parent::__construct($message, $code);   // For PHP 5.2.x
398
    }
399
400
    public function __toString()
401
    {
402
        return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
403
    }
404
}
405
406
class UpYunAuthorizationException extends UpYunException
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
407
{
408
    public function __construct($message, $code = 0, Exception $previous = null)
409
    {
410
        parent::__construct($message, 401, $previous);
411
    }
412
}
413
414
class UpYunForbiddenException extends UpYunException
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
415
{
416
    public function __construct($message, $code = 0, Exception $previous = null)
417
    {
418
        parent::__construct($message, 403, $previous);
419
    }
420
}
421
422
class UpYunNotFoundException extends UpYunException
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
423
{
424
    public function __construct($message, $code = 0, Exception $previous = null)
425
    {
426
        parent::__construct($message, 404, $previous);
427
    }
428
}
429
430
class UpYunNotAcceptableException extends UpYunException
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
431
{
432
    public function __construct($message, $code = 0, Exception $previous = null)
433
    {
434
        parent::__construct($message, 406, $previous);
435
    }
436
}
437
438
class UpYunServiceUnavailable extends UpYunException
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
439
{
440
    public function __construct($message, $code = 0, Exception $previous = null)
441
    {
442
        parent::__construct($message, 503, $previous);
443
    }
444
}
445