GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Branch dev (9e5a4e)
by t
02:34
created

LocalFile::getExtension()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 1
c 1
b 1
f 0
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * Class LocalFile
4
 *
5
 * @link https://www.icy2003.com/
6
 * @author icy2003 <[email protected]>
7
 * @copyright Copyright (c) 2019, icy2003
8
 */
9
namespace icy2003\php\icomponents\file;
10
11
use Exception;
12
use icy2003\php\C;
13
use icy2003\php\I;
14
use icy2003\php\icomponents\file\FileInterface;
15
use icy2003\php\ihelpers\Arrays;
16
use icy2003\php\ihelpers\Charset;
17
use icy2003\php\ihelpers\Header;
18
use icy2003\php\ihelpers\Request;
19
use icy2003\php\ihelpers\Strings;
20
21
/**
22
 * 本地文件
23
 *
24
 * - 支持本地文件操作
25
 * - 支持网络文件:文件是否存在、文件大小
26
 * - 下载文件请使用 download() 而不是 downloadFile()
27
 */
28
class LocalFile extends Base implements FileInterface
29
{
30
31
    /**
32
     * 加载文件用的函数
33
     *
34
     * @var callback
35
     */
36
    protected $_functions = [
37
        'loader' => null,
38
    ];
39
40
    /**
41
     * 文件属性
42
     *
43
     * - 文件名为键,属性为值
44
     *
45
     * @var array
46
     */
47
    protected $_attributes = [];
48
49
    /**
50
     * 初始化
51
     *
52
     * @param mixed $locale 地区信息
53
     */
54 18
    public function __construct($locale = 'zh_CN.UTF-8')
55
    {
56 18
        setlocale(LC_ALL, $locale);
57 18
        clearstatcache();
58
        $this->_functions['loader'] = function ($fileName, $mode = 'rb') {
59 3
            $hashName = $this->__hash($fileName);
60 3
            $this->_attributes[$hashName] = I::get($this->_attributes, $hashName, [
61 3
                'isCached' => false,
62
                'isLocal' => true,
63 3
                'file' => $this->__file($fileName),
64
                // 以下属性需要重新设置
65
                'isExists' => false,
66 3
                'fileSize' => 0,
67
                'spl' => null,
68
                'splInfo' => null,
69
            ]);
70
            try {
71 3
                $this->chmod($this->_attributes[$hashName]['file']);
72 3
                null === $this->_attributes[$hashName]['spl'] && $this->_attributes[$hashName]['spl'] = new \SplFileObject($this->__file($fileName), $mode);
73 3
                null === $this->_attributes[$hashName]['splInfo'] && $this->_attributes[$hashName]['splInfo'] = new \SplFileInfo($this->__file($fileName));
74
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
75
76
            }
77
            // 如果已经被缓存了,直接返回
78 3
            if (true === $this->_attributes[$hashName]['isCached']) {
79
                return;
80
            }
81 3
            $this->_attributes[$hashName]['isCached'] = true;
82 3
            if (preg_match('/^https?:\/\//', $fileName)) {
83
                $this->_attributes[$hashName]['isLocal'] = false;
84
                // 加载网络文件
85
                if (extension_loaded('curl')) {
86
                    $curl = curl_init($fileName);
87
                    curl_setopt($curl, CURLOPT_NOBODY, true);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_setopt() 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

87
                    curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_NOBODY, true);
Loading history...
88
                    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
89
                    curl_setopt($curl, CURLOPT_HEADER, true);
90
                    // 公用名(Common Name)一般来讲就是填写你将要申请SSL证书的域名 (domain)或子域名(sub domain)
91
                    // - 设置为 1 是检查服务器SSL证书中是否存在一个公用名(common name)
92
                    // - 设置成 2,会检查公用名是否存在,并且是否与提供的主机名匹配
93
                    // - 0 为不检查名称。 在生产环境中,这个值应该是 2(默认值)
94
                    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
95
                    // 禁止 cURL 验证对等证书(peer's certificate)。要验证的交换证书可以在 CURLOPT_CAINFO 选项中设置
96
                    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
97
                    $result = curl_exec($curl);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_exec() 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

97
                    $result = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
98
                    if ($result && $info = curl_getinfo($curl)) {
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_getinfo() 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

98
                    if ($result && $info = curl_getinfo(/** @scrutinizer ignore-type */ $curl)) {
Loading history...
99
                        if (200 == $info['http_code']) {
100
                            $this->_attributes[$hashName]['isExists'] = true;
101
                            $this->_attributes[$hashName]['fileSize'] = $info['download_content_length'];
102
                        }
103
                    }
104
                    curl_close($curl);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_close() 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

104
                    curl_close(/** @scrutinizer ignore-type */ $curl);
Loading history...
105
                } elseif ((bool) ini_get('allow_url_fopen')) {
106
                    $headArray = (array) get_headers($fileName, 1);
107
                    if (preg_match('/200/', $headArray[0])) {
108
                        $this->_attributes[$hashName]['isExists'] = true;
109
                        $this->_attributes[$hashName]['fileSize'] = $headArray['Content-Length'];
110
                    }
111
                } else {
112
                    $url = parse_url($fileName);
113
                    $host = $url['host'];
114
                    $path = (string) I::get($url, 'path', '/');
115
                    $port = (int) I::get($url, 'port', 80);
116
                    $fp = fsockopen($host, $port);
117
                    if (is_resource($fp)) {
118
                        $header = [
119
                            'GET ' . $path . ' HTTP/1.0',
120
                            'HOST: ' . $host . ':' . $port,
121
                            'Connection: Close',
122
                        ];
123
                        fwrite($fp, implode('\r\n', $header) . '\r\n\r\n');
124
                        while (!feof($fp)) {
125
                            $line = fgets($fp);
126
                            if ('' == trim($line)) {
127
                                break;
128
                            } else {
129
                                preg_match('/HTTP.*(\s\d{3}\s)/', $line, $arr) && $this->_attributes[$hashName]['isExists'] = true;
130
                                preg_match('/Content-Length:(.*)/si', $line, $arr) && $this->_attributes[$hashName]['fileSize'] = trim($arr[1]);
131
                            }
132
                        }
133
                    }
134
                    fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp 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

134
                    fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
135
                }
136
            } else {
137 3
                $this->_attributes[$hashName]['isLocal'] = true;
138 3
                $this->_attributes[$hashName]['isExists'] = file_exists($this->_attributes[$hashName]['file']);
139 3
                if ($this->_attributes[$hashName]['isExists']) {
140 3
                    $this->_attributes[$hashName]['fileSize'] = filesize($this->_attributes[$hashName]['file']);
141
                }
142
            }
143 3
        };
144 18
    }
145
146
    /**
147
     * 获取 Hash 值
148
     *
149
     * @param string $fileName
150
     *
151
     * @return string
152
     */
153 3
    private function __hash($fileName)
154
    {
155 3
        return md5($fileName);
156
    }
157
158
    /**
159
     * 返回路径别名
160
     *
161
     * @param string $file
162
     *
163
     * @return string
164
     */
165 18
    private function __file($file)
166
    {
167 18
        return (string) I::getAlias($file);
168
    }
169
170
    /**
171
     * 获取网络文件的属性
172
     *
173
     * @param string $fileName
174
     * @param string $name
175
     * @param string $mode 读写的模式,默认 rb
176
     *
177
     * @return mixed
178
     */
179 3
    public function attribute($fileName, $name, $mode = 'rb')
180
    {
181 3
        I::call($this->_functions['loader'], [$fileName, $mode]);
182 3
        return I::get($this->_attributes, $this->__hash($fileName) . '.' . $name);
183
    }
184
185
    /**
186
     * 获取文件对象
187
     *
188
     * @param string $fileName
189
     * @param string $mode 读写的模式,默认 rb
190
     *
191
     * @return \SplFileObject
192
     */
193
    public function spl($fileName, $mode = 'rb')
194
    {
195
        $spl = $this->attribute($fileName, 'spl', $mode);
196
        if ($spl instanceof \SplFileObject) {
197
            return $spl;
198
        }
199
        throw new Exception('文件打开失败:' . $fileName);
200
    }
201
202
    /**
203
     * 获取文件信息对象
204
     *
205
     * @param string $fileName
206
     *
207
     * @return \SplFileInfo
208
     */
209
    public function splInfo($fileName)
210
    {
211
        $splInfo = $this->attribute($fileName, 'splInfo');
212
        if ($splInfo instanceof \SplFileInfo) {
213
            return $splInfo;
214
        }
215
        throw new Exception('文件打开失败:' . $fileName);
216
    }
217
218
    /**
219
     * 遍历行的生成器
220
     *
221
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
222
     *
223
     * @param string $fileName
224
     * @param boolean $autoClose 是否自动关闭文件,默认 false
225
     *
226
     * @return \Generator
227
     */
228
    public function linesGenerator($fileName, $autoClose = false)
229
    {
230
        try {
231
            $spl = $this->spl($fileName);
232
            while (false === $spl->eof() && $line = $spl->fgets()) {
233
                yield $line;
234
            }
235
        } finally {
236
            true === $autoClose && $this->close($fileName);
237
        }
238
    }
239
240
    /**
241
     * 返回文本的某行
242
     *
243
     * - 每取一行,文件指针会回到初始位置,如果需要大量的行,请直接使用 linesGenerator
244
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
245
     *
246
     * @param string $fileName
247
     * @param integer $num 行号
248
     * @param boolean $autoClose 是否自动关闭文件,默认 false
249
     *
250
     * @return string|null
251
     */
252
    public function line($fileName, $num = 0, $autoClose = false)
253
    {
254
        $spl = $this->spl($fileName);
255
        foreach ($this->linesGenerator($fileName, $autoClose) as $k => $line) {
256
            if ($k == $num) {
257
                $spl->rewind();
258
                return $line;
259
            }
260
        }
261
        return null;
262
    }
263
264
    /**
265
     * 遍历字节的生成器
266
     *
267
     * @param string $fileName
268
     * @param boolean $autoClose 是否自动关闭文件,默认 false
269
     * @param integer $buffer 缓冲区长度,默认 1024
270
     *
271
     * @return \Generator
272
     */
273
    public function dataGenerator($fileName, $autoClose = false, $buffer = 1024)
274
    {
275
        $bufferSize = 0;
276
        try {
277
            $spl = $this->spl($fileName);
278
            $size = $this->getFilesize($fileName);
279
            while (!$spl->eof() && $size > $bufferSize) {
280
                $bufferSize += $buffer;
281
                yield $spl->fread($bufferSize);
282
            }
283
        } finally {
284
            true === $autoClose && $this->close($fileName);
285
        }
286
    }
287
288
    /**
289
     * @ignore
290
     */
291
    public function getATime($fileName)
292
    {
293
        return fileatime($this->__file($fileName));
294
    }
295
296
    /**
297
     * @ignore
298
     */
299
    public function getBasename($path, $suffix = null)
300
    {
301
        return parent::getBasename($this->__file($path), $suffix);
302
    }
303
304
    /**
305
     * @ignore
306
     */
307
    public function getCTime($fileName)
308
    {
309
        return filectime($this->__file($fileName));
310
    }
311
312
    /**
313
     * @ignore
314
     */
315
    public function getExtension($fileName)
316
    {
317
        return pathinfo($this->__file($fileName), PATHINFO_EXTENSION);
318
    }
319
320
    /**
321
     * @ignore
322
     */
323
    public function getFilename($fileName)
324
    {
325
        return pathinfo($this->__file($fileName), PATHINFO_FILENAME);
326
    }
327
328
    /**
329
     * @ignore
330
     */
331
    public function getMtime($fileName)
332
    {
333
        return filemtime($this->__file($fileName));
334
    }
335
336
    /**
337
     * @ignore
338
     */
339
    public function getDirname($path)
340
    {
341
        return parent::getDirname($this->__file($path));
342
    }
343
344
    /**
345
     * @ignore
346
     */
347
    public function getPerms($path)
348
    {
349
        return fileperms($this->__file($path));
350
    }
351
352
    /**
353
     * @ignore
354
     */
355
    public function getFilesize($fileName)
356
    {
357
        return (int) $this->attribute($fileName, 'fileSize');
358
    }
359
360
    /**
361
     * @ignore
362
     */
363
    public function getType($path)
364
    {
365
        return filetype($this->__file($path));
366
    }
367
368
    /**
369
     * @ignore
370
     */
371 3
    public function isDir($dir)
372
    {
373 3
        return is_dir($this->__file($dir));
374
    }
375
376
    /**
377
     * @ignore
378
     */
379
    public function isDot($dir)
380
    {
381
        return in_array($this->getBasename($dir), ['.', '..']);
382
    }
383
384
    /**
385
     * @ignore
386
     */
387 3
    public function isFile($file)
388
    {
389 3
        return (bool) $this->attribute($file, 'isExists');
390
    }
391
392
    /**
393
     * @ignore
394
     */
395
    public function isLink($link)
396
    {
397
        return is_link($this->__file($link));
398
    }
399
400
    /**
401
     * @ignore
402
     */
403
    public function isReadable($path)
404
    {
405
        return is_readable($this->__file($path));
406
    }
407
408
    /**
409
     * @ignore
410
     */
411
    public function isWritable($path)
412
    {
413
        return is_writable($this->__file($path));
414
    }
415
416
    /**
417
     * @ignore
418
     */
419
    public function getCommandResult($command)
420
    {
421
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the return type mandated by icy2003\php\icomponents\...ase::getCommandResult() of string.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
422
    }
423
424
    /**
425
     * @ignore
426
     */
427 18
    public function getRealpath($path)
428
    {
429 18
        $file = realpath($this->__file($path));
430 18
        if (false === $file) {
431 18
            $file = $path;
432
        }
433 18
        return $file;
434
    }
435
436
    /**
437
     * @ignore
438
     */
439
    public function getLists($dir = null, $flags = FileConstants::COMPLETE_PATH)
440
    {
441
        null === $dir && $dir = $this->getRealpath('./');
442
        $dir = $this->__file(rtrim($dir, '/') . '/');
443
        $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS);
444
        if (I::hasFlag($flags, FileConstants::RECURSIVE)) {
445
            $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
446
        }
447
        $files = [];
448
        /**
449
         * @var \RecursiveDirectoryIterator $file
450
         */
451
        foreach ($iterator as $file) {
452
            if (I::hasFlag($flags, FileConstants::COMPLETE_PATH)) {
453
                $files[] = $file->getPathname();
454
            } else {
455
                $files[] = $file->getFilename();
456
            }
457
        }
458
        return $files;
459
    }
460
461
    /**
462
     * @ignore
463
     */
464 1
    public function getFileContent($file)
465
    {
466 1
        return file_get_contents($this->__file($file));
467
    }
468
469
    /**
470
     * @ignore
471
     */
472
    public function putFileContent($file, $string, $mode = 0777)
473
    {
474
        $file = $this->__file($file);
475
        $this->createDir($this->getDirname($file), $mode);
476
        $isCreated = false !== file_put_contents($file, $string);
477
        $this->chmod($file, $mode, FileConstants::RECURSIVE_DISABLED);
478
        return $isCreated;
479
    }
480
481
    /**
482
     * @ignore
483
     */
484 1
    public function deleteFile($file)
485
    {
486 1
        $file = $this->__file($file);
487 1
        if ($this->isFile($file)) {
488 1
            $this->close($file);
489 1
            return unlink($file);
490
        }
491
        return true;
492
    }
493
494
    /**
495
     * @ignore
496
     */
497
    public function uploadFile($toFile, $fromFile = null, $overwrite = true)
498
    {
499
        return false;
500
    }
501
502
    /**
503
     * - 从远程下载文件到本地
504
     * - @param callback|null $callback 执行中的回调
505
     *
506
     * @ignore
507
     */
508
    public function downloadFile($fromFile, $toFile = null, $overwrite = true, $callback = null)
509
    {
510
        if (null === $toFile) {
511
            list($fromFile, $toFile) = $this->fileMap($fromFile);
512
        } else {
513
            list($fromFile) = $this->fileMap($fromFile);
514
        }
515
        if ($this->isFile($toFile) && false === $overwrite) {
516
            return true;
517
        }
518
        $fromSpl = $this->spl($fromFile, 'rb');
519
        $toSpl = $this->spl($toFile, 'wb');
520
        $size = 0;
521
        $total = $this->getFilesize($fromFile);
522
        while (false === $fromSpl->eof()) {
523
            $out = $fromSpl->fread(1024 * 8);
524
            $toSpl->fwrite($out);
525
            $size += Strings::byteLength($out);
526
            I::call($callback, [$size, $total]);
527
        }
528
        $this->close([$fromFile, $toFile]);
529
    }
530
531
    /**
532
     * download() 配置名:ip
533
     */
534
    const C_DOWNLOAD_IP = 'ip';
535
    /**
536
     * download() 配置名:speed
537
     */
538
    const C_DOWNLOAD_SPEED = 'speed';
539
    /**
540
     * download() 配置名:xSendFile
541
     */
542
    const C_DOWNLOAD_X_SEND_FILE = 'xSendFile';
543
    /**
544
     * download() 配置名:xSendFileRoot
545
     */
546
    const C_DOWNLOAD_X_SEND_FILE_ROOT = 'xSendFileRoot';
547
548
    /**
549
     * 服务端给客户端提供下载请求
550
     *
551
     * @param string|array $fileName 如果是数组,第一个元素是原名,第二个元素为下载名,原名需要指定路径,下载名不需要
552
     * @param null|array $config 配置项
553
     *      - ip:限特定 IP 访问,数组或逗号字符串,默认为 *,即对所有 IP 不限制
554
     *      - speed:限速,默认不限速,单位 kb/s
555
     *      - xSendFile:是否使用 X-Sendfile 进行下载,默认 false,即不使用。X-Sendfile 缓解了 PHP 的压力,但同时 PHP 将失去对资源的控制权,因为 PHP 并不知道资源发完了没
556
     *      - xSendFileRoot:文件根路径,默认为 /protected/。此时 Nginx 可作如下配置,更多 @link https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
557
     *      ```nginx.conf
558
     *      location /protected/ {
559
     *          internal; # 表示这个路径只能在 Nginx 内部访问,不能用浏览器直接访问防止未授权的下载
560
     *          alias   /usr/share/nginx/html/protected/; # 别名
561
     *          # root    /usr/share/nginx/html; # 根目录
562
     *      }
563
     *      ```
564
     * @param callback $callback 下载完成后的回调,参数列表:文件属性数组
565
     *
566
     * @return void
567
     * @throws Exception
568
     */
569
    public function download($fileName, $config = null, $callback = null)
570
    {
571
        Header::xPoweredBy();
572
        set_time_limit(0);
573
        list($originName, $downloadName) = $this->fileMap($fileName);
574
        $originName = $this->__file($originName);
575
        try {
576
            $ip = I::get($config, self::C_DOWNLOAD_IP, '*');
577
            if ('*' !== $ip) {
578
                C::assertTrue(Arrays::in((new Request())->getUserIP(), Strings::toArray($ip)), 'http/1.1 403.6 此 IP 禁止访问');
0 ignored issues
show
Bug introduced by
It seems like $ip can also be of type false; however, parameter $mixed of icy2003\php\ihelpers\Strings::toArray() does only seem to accept array|string, 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

578
                C::assertTrue(Arrays::in((new Request())->getUserIP(), Strings::toArray(/** @scrutinizer ignore-type */ $ip)), 'http/1.1 403.6 此 IP 禁止访问');
Loading history...
579
            }
580
            if ($this->isFile($originName)) {
581
                $fileSize = $this->getFilesize($originName);
582
                header('Content-type:application/octet-stream');
583
                header('Accept-Ranges:bytes');
584
                header('Content-Length:' . $fileSize);
585
                header('Content-Disposition: attachment; filename=' . $downloadName);
586
                $speed = I::get($config, self::C_DOWNLOAD_SPEED, 0);
587
                $xSendFile = I::get($config, self::C_DOWNLOAD_X_SEND_FILE, false);
588
                $xSendFileRoot = I::get($config, self::C_DOWNLOAD_X_SEND_FILE_ROOT, '/protected/');
589
                if (true === $xSendFile) {
590
                    $path = rtrim($xSendFileRoot, '/') . '/' . $this->getBasename($originName);
0 ignored issues
show
Bug introduced by
It seems like $xSendFileRoot can also be of type false; however, parameter $str of rtrim() does only seem to accept string, 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

590
                    $path = rtrim(/** @scrutinizer ignore-type */ $xSendFileRoot, '/') . '/' . $this->getBasename($originName);
Loading history...
591
                    header('X-Accel-Redirect: ' . $path); // Nginx、Cherokee 实现了该头
592
                    header('X-Sendfile: ' . $path); // Apache、Lighttpd v1.5、Cherokee 实现了该头
593
                    header('X-LIGHTTPD-send-file: ', $path); // Lighttpd v1.4 实现了该头
0 ignored issues
show
Bug introduced by
$path of type string is incompatible with the type boolean expected by parameter $replace of header(). ( Ignorable by Annotation )

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

593
                    header('X-LIGHTTPD-send-file: ', /** @scrutinizer ignore-type */ $path); // Lighttpd v1.4 实现了该头
Loading history...
594
                    if ($speed) {
595
                        header('X-Accel-Limit-Rate: ' . $speed); // 单位 kb/s
596
                    }
597
                } else {
598
                    flush();
599
                    foreach ($this->dataGenerator($originName, true, ($speed ? $speed : 1024) * 1024) as $data) {
600
                        echo $data;
601
                        flush();
602
                        $speed > 0 && sleep(1);
603
                    }
604
                }
605
            }
606
        } catch (Exception $e) {
607
            header($e->getMessage());
608
        } finally {
609
            I::call($callback, [$this->_attributes]);
610
            // 必须要终止掉,防止发送其他数据导致错误
611
            die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
612
        }
613
    }
614
615
    /**
616
     * 返回文件映射
617
     *
618
     * @param string|array $file 支持别名
619
     *
620
     * @return array
621
     */
622
    public function fileMap($file)
623
    {
624
        if (is_string($file)) {
625
            $file = [$file, Charset::toCn($this->getBasename($file))];
626
        } elseif (is_array($file)) {
0 ignored issues
show
introduced by
The condition is_array($file) is always true.
Loading history...
627
            $file = Arrays::lists($file, 2);
628
            if ($this->isDir($file[1])) {
629
                $file[1] = rtrim($file[1], '/') . '/' . Charset::toCn($this->getBasename($file[0]));
630
            }
631
        } else {
632
            $file = ['', ''];
633
        }
634
        return $file;
635
    }
636
637
    /**
638
     * @ignore
639
     */
640
    public function chown($file, $user, $flags = FileConstants::RECURSIVE_DISABLED)
641
    {
642
        $file = $this->__file($file);
643
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
644
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
645
            foreach ($files as $subFile) {
646
                chown($subFile, $user);
647
            }
648
        }
649
        return chown($file, $user);
650
    }
651
652
    /**
653
     * @ignore
654
     */
655
    public function chgrp($file, $group, $flags = FileConstants::RECURSIVE_DISABLED)
656
    {
657
        $file = $this->__file($file);
658
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
659
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
660
            foreach ($files as $subFile) {
661
                chgrp($subFile, $group);
662
            }
663
        }
664
        return chgrp($file, $group);
665
    }
666
667
    /**
668
     * @ignore
669
     */
670 3
    public function chmod($file, $mode = 0777, $flags = FileConstants::RECURSIVE_DISABLED)
671
    {
672 3
        $file = $this->__file($file);
673 3
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
674
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
675
            foreach ($files as $subFile) {
676
                @chmod($subFile, $mode);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). 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

676
                /** @scrutinizer ignore-unhandled */ @chmod($subFile, $mode);

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...
677
            }
678
        }
679 3
        return (bool) @chmod($file, $mode);
680
    }
681
682
    /**
683
     * @ignore
684
     */
685
    public function symlink($from, $to)
686
    {
687
        $from = $this->__file($from);
688
        $to = $this->__file($to);
689
        return symlink($from, $to);
690
    }
691
692
    /**
693
     * @ignore
694
     */
695 1
    public function close($fileName = null)
696
    {
697 1
        if (is_string($fileName)) {
698 1
            $fileName = [$this->__hash($fileName)];
699
        } elseif (is_array($fileName)) {
700
            foreach ($fileName as $k => $name) {
701
                $fileName[$k] = $this->__hash($name);
702
            }
703
        }
704 1
        foreach ($this->_attributes as $hashName => /** @scrutinizer ignore-unused */$attribute) {
705 1
            if (null === $fileName || is_array($fileName) && in_array($hashName, $fileName)) {
706 1
                unset($this->_attributes[$hashName]);
707
            }
708
        }
709 1
        return true;
710
    }
711
712
    /**
713
     * @ignore
714
     */
715
    protected function _copy($fromFile, $toFile)
716
    {
717
        $fromFile = $this->__file($fromFile);
718
        $toFile = $this->__file($toFile);
719
        return copy($fromFile, $toFile);
720
    }
721
722
    /**
723
     * @ignore
724
     */
725
    protected function _move($fromFile, $toFile)
726
    {
727
        $fromFile = $this->__file($fromFile);
728
        $toFile = $this->__file($toFile);
729
        return rename($fromFile, $toFile);
730
    }
731
732
    /**
733
     * @ignore
734
     */
735
    protected function _mkdir($dir, $mode = 0777)
736
    {
737
        $dir = $this->__file($dir);
738
        return mkdir($dir, $mode);
739
    }
740
741
    /**
742
     * @ignore
743
     */
744
    protected function _rmdir($dir)
745
    {
746
        $dir = $this->__file($dir);
747
        return rmdir($dir);
748
    }
749
750
}
751