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.
Passed
Branch dev (8e731f)
by t
02:48
created

LocalFile::attribute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 2 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 2
b 2
f 0
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\Console;
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
 */
27
class LocalFile extends Base implements FileInterface
28
{
29
30
    /**
31
     * 配置
32
     *
33
     * @var array
34
     */
35
    protected $_c = [
36
        'loader' => 'curl',
37
        'locale' => 'zh_CN.UTF-8',
38
        'buffer' => 4096,
39
        'mode' => 'rb',
40
        'rtrim' => true,
41
    ];
42
43
    /**
44
     * 文件属性
45
     *
46
     * - 文件名为键,属性为值
47
     *
48
     * @var array
49
     */
50
    protected $_attributes = [];
51
52
    /**
53
     * 初始化
54
     *
55
     * @param array $options 配置
56
     *      - locale:地区,默认 zh_CN.UTF-8
57
     *      - buffer:以字节方式读写时每段的字节长度,默认为 4096,即 4kb
58
     *      - mode:指定了所要求到该流的访问类型,默认 rb @link https://www.php.net/manual/zh/function.fopen.php
59
     *      - rtrim:在遍历行时是否去除行尾空白,默认 true,即去除
60
     *      - loader:读取远程资源时用的方法,默认为 curl(当其他方法无法读取时也会设置为 curl),支持值:curl、fopen、fsockopen
61
     *          - curl:使用 curl 获取远程文件信息
62
     *          - fopen:需要手动开启 allow_url_fopen 才能使用,不建议开启
63
     *          - fsockopen:使用 fsockopen 发送头获取信息
64
     * @return void
65
     */
66 24
    public function __construct($options = [])
67
    {
68 24
        $this->_c = Arrays::merge($this->_c, $options);
69 24
        setlocale(LC_ALL, (string) I::get($this->_c, 'locale', 'zh_CN.UTF-8'));
70 24
        clearstatcache();
71 24
    }
72
73
    /**
74
     * 获取 Hash 值
75
     *
76
     * @param string $fileName
77
     *
78
     * @return string
79
     */
80 9
    private function __hash($fileName)
81
    {
82 9
        $fileName = $this->__file($fileName);
83 9
        return md5($fileName);
84
    }
85
86
    /**
87
     * 以别名返回路径
88
     *
89
     * @param string $file
90
     *
91
     * @return string
92
     */
93 9
    private function __file($file)
94
    {
95 9
        return (string) I::getAlias($file);
96
    }
97
98
    /**
99
     * 加载一个文件,本地(支持别名)或网络文件
100
     *
101
     * @param string $fileName
102
     *
103
     * @return static
104
     */
105 9
    protected function _load($fileName)
106
    {
107 9
        $fileName = $this->__file($fileName);
108 9
        $hashName = $this->__hash($fileName);
109 9
        $this->_attributes[$hashName] = I::get($this->_attributes, $hashName, [
110 9
            'file' => $fileName,
111
            'isCached' => false,
112
            'isLocal' => true,
113
            // 以下属性需要重新设置
114
            'isExists' => false,
115 9
            'fileSize' => 0,
116
            'spl' => null,
117
            'splInfo' => null,
118
        ]);
119
        try {
120 9
            null === $this->_attributes[$hashName]['spl'] && $this->_attributes[$hashName]['spl'] = new \SplFileObject($fileName, $this->_c['mode']);
121 9
            null === $this->_attributes[$hashName]['splInfo'] && $this->_attributes[$hashName]['splInfo'] = new \SplFileInfo($fileName);
122 1
        } catch (Exception $e) {
123
            // 报错了也得继续跑,如果跑完一次 spl 和 splInfo 属性还是 null,在调用它们的时候自然会报错
124 1
            $this->_c['error'] = $e->getMessage();
125
            // 尝试用 curl 获取
126 1
            $this->_c['loader'] = 'curl';
127
        }
128
        // 如果已经被缓存了,直接返回
129 9
        if (true === $this->_attributes[$hashName]['isCached']) {
130 9
            return $this;
131
        }
132
        // 加上缓存标记
133 9
        $this->_attributes[$hashName]['isCached'] = true;
134 9
        if (preg_match('/^https?:\/\//', $fileName)) {
135 1
            $this->_attributes[$hashName]['isLocal'] = false;
136
            // 加载网络文件
137 1
            if ('curl' === $this->_c['loader'] && extension_loaded('curl')) {
138 1
                $curl = curl_init($fileName);
139 1
                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

139
                curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_NOBODY, true);
Loading history...
140 1
                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
141 1
                curl_setopt($curl, CURLOPT_HEADER, true);
142
                // 公用名(Common Name)一般来讲就是填写你将要申请SSL证书的域名 (domain)或子域名(sub domain)
143
                // - 设置为 1 是检查服务器SSL证书中是否存在一个公用名(common name)
144
                // - 设置成 2,会检查公用名是否存在,并且是否与提供的主机名匹配
145
                // - 0 为不检查名称。 在生产环境中,这个值应该是 2(默认值)
146 1
                curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
147
                // 禁止 cURL 验证对等证书(peer's certificate)。要验证的交换证书可以在 CURLOPT_CAINFO 选项中设置
148 1
                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
149 1
                $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

149
                $result = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
150 1
                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

150
                if ($result && $info = curl_getinfo(/** @scrutinizer ignore-type */ $curl)) {
Loading history...
151 1
                    if (200 == $info['http_code']) {
152 1
                        $this->_attributes[$hashName]['isExists'] = true;
153 1
                        $this->_attributes[$hashName]['fileSize'] = (int) $info['download_content_length'];
154
                    }
155
                }
156 1
                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

156
                curl_close(/** @scrutinizer ignore-type */ $curl);
Loading history...
157 1
                return $this;
158
            }
159 1
            if ('fsockopen' === $this->_c['loader']) {
160 1
                $url = parse_url($fileName);
161 1
                $host = $url['host'];
162 1
                $path = (string) I::get($url, 'path', '/');
163 1
                $port = (int) I::get($url, 'port', 80);
164 1
                $fp = fsockopen($host, $port);
165 1
                if (is_resource($fp)) {
166 1
                    fputs($fp, "GET {$path} HTTP/1.1\r\n");
167 1
                    fputs($fp, "Host: {$host}:{$port}\r\n");
168 1
                    fputs($fp, "Connection: Close\r\n\r\n");
169 1
                    while (!feof($fp)) {
170 1
                        $line = fgets($fp);
171 1
                        preg_match('/HTTP.*(\s\d{3}\s)/', $line, $arr) && $this->_attributes[$hashName]['isExists'] = true;
172 1
                        preg_match('/Content-Length:(.*)/si', $line, $arr) && $this->_attributes[$hashName]['fileSize'] = (int) trim($arr[1]);
173
                    }
174
                }
175 1
                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

175
                fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
176 1
                return $this;
177
            }
178 1
            if ('fopen' === $this->_c['loader'] && (bool) ini_get('allow_url_fopen')) {
179 1
                $headArray = (array) get_headers($fileName, 1);
180 1
                if (preg_match('/200/', $headArray[0])) {
181 1
                    $this->_attributes[$hashName]['isExists'] = true;
182 1
                    $this->_attributes[$hashName]['fileSize'] = (int) $headArray['Content-Length'];
183
                }
184 1
                return $this;
185
            }
186
        } else {
187 9
            $this->_attributes[$hashName]['isLocal'] = true;
188 9
            $this->_attributes[$hashName]['isExists'] = file_exists($fileName);
189 9
            if ($this->_attributes[$hashName]['isExists']) {
190 8
                $this->_attributes[$hashName]['fileSize'] = filesize($fileName);
191
            }
192 9
            $this->chmod($fileName, 0777, FileConstants::RECURSIVE_DISABLED);
193
        }
194 8
        return $this;
195
    }
196
197
    /**
198
     * 获取文件的属性
199
     *
200
     * @param string $fileName
201
     * @param string $name
202
     *
203
     * @return mixed
204
     */
205 9
    public function attribute($fileName, $name)
206
    {
207 9
        $this->_load($fileName);
208 9
        return I::get($this->_attributes, $this->__hash($fileName) . '.' . $name);
209
    }
210
211
    /**
212
     * 获取文件对象
213
     *
214
     * @param string $fileName
215
     * @param string $mode 读写的模式,默认 rb
216
     *
217
     * @return \SplFileObject
218
     */
219 9
    public function spl($fileName, $mode = 'rb')
220
    {
221 9
        $this->_c['mode'] = $mode;
222 9
        $spl = $this->attribute($fileName, 'spl');
223 9
        C::assertTrue($spl instanceof \SplFileObject, '文件打开失败:' . $fileName);
224 8
        return $spl;
225
    }
226
227
    /**
228
     * 获取文件信息对象
229
     *
230
     * @param string $fileName
231
     *
232
     * @return \SplFileInfo
233
     */
234 1
    public function splInfo($fileName)
235
    {
236 1
        $splInfo = $this->attribute($fileName, 'splInfo');
237 1
        C::assertTrue($splInfo instanceof \SplFileInfo, '文件打开失败:' . $fileName);
238 1
        return $splInfo;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $splInfo also could return the type false|string which is incompatible with the documented return type SplFileInfo.
Loading history...
239
    }
240
241
    /**
242
     * 遍历行的生成器
243
     *
244
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
245
     *
246
     * @param string $fileName
247
     * @param boolean $autoClose 是否自动关闭文件,默认 false
248
     *
249
     * @return \Generator
250
     */
251 2
    public function linesGenerator($fileName, $autoClose = false)
252
    {
253
        try {
254 2
            $spl = $this->spl($fileName, 'r');
255 2
            while (false === $spl->eof() && ($line = $spl->fgets())) {
256 2
                true === $this->_c['rtrim'] && $line = rtrim($line);
257 2
                yield $line;
258
            }
259 2
        } finally {
260 2
            true === $autoClose && $this->close($fileName);
261
        }
262 2
    }
263
264
    /**
265
     * 返回文本的某行
266
     *
267
     * - 每取一行,文件指针会回到初始位置,如果需要大量的行,请直接使用 linesGenerator
268
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
269
     *
270
     * @param string $fileName
271
     * @param integer $lineNumber 行号
272
     * @param boolean $autoClose 是否自动关闭文件,默认 false
273
     *
274
     * @return string|null
275
     */
276 1
    public function line($fileName, $lineNumber = 0, $autoClose = false)
277
    {
278 1
        $spl = $this->spl($fileName, 'r');
279 1
        $lineNumber = (int) $lineNumber;
280 1
        foreach ($this->linesGenerator($fileName, $autoClose) as $k => $line) {
281 1
            if ($k === $lineNumber) {
282 1
                $spl->rewind();
283 1
                return $line;
284
            }
285
        }
286 1
        return null;
287
    }
288
289
    /**
290
     * 遍历字节的生成器
291
     *
292
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
293
     *
294
     * @param string $fileName
295
     * @param boolean $autoClose 是否自动关闭文件,默认 false
296
     * @param integer|null $buffer 每次读取的字节数,默认 null,值等于初始化时的 buffer 选项
297
     *
298
     * @return \Generator
299
     */
300 1
    public function dataGenerator($fileName, $autoClose = false, $buffer = null)
301
    {
302 1
        $bufferSize = 0;
303 1
        null === $buffer && $buffer = $this->_c['buffer'];
304
        try {
305 1
            $spl = $this->spl($fileName, 'rb');
306 1
            $size = $this->getFilesize($fileName);
307 1
            while (!$spl->eof() && $size > $bufferSize) {
308 1
                $bufferSize += $buffer;
309 1
                yield $spl->fread($bufferSize);
310
            }
311 1
        } finally {
312 1
            true === $autoClose && $this->close($fileName);
313
        }
314 1
    }
315
316
    /**
317
     * @ignore
318
     */
319
    public function getATime($fileName)
320
    {
321
        return $this->spl($fileName)->getATime();
322
    }
323
324
    /**
325
     * @ignore
326
     */
327
    public function getBasename($file, $suffix = null)
328
    {
329
        return parent::getBasename($this->__file($file), $suffix);
330
    }
331
332
    /**
333
     * @ignore
334
     */
335
    public function getCTime($fileName)
336
    {
337
        return $this->spl($fileName)->getCTime();
338
    }
339
340
    /**
341
     * @ignore
342
     */
343
    public function getExtension($fileName)
344
    {
345
        return $this->spl($fileName)->getExtension();
346
    }
347
348
    /**
349
     * @ignore
350
     */
351
    public function getFilename($fileName)
352
    {
353
        return $this->spl($fileName)->getFilename();
354
    }
355
356
    /**
357
     * @ignore
358
     */
359
    public function getMtime($fileName)
360
    {
361
        return $this->spl($fileName)->getMTime();
362
    }
363
364
    /**
365
     * @ignore
366
     */
367
    public function getDirname($path)
368
    {
369
        return parent::getDirname($this->__file($path));
370
    }
371
372
    /**
373
     * @ignore
374
     */
375
    public function getPerms($path)
376
    {
377
        return $this->spl($path)->getPerms();
378
    }
379
380
    /**
381
     * @ignore
382
     */
383 2
    public function getFilesize($fileName)
384
    {
385 2
        return (int) $this->attribute($fileName, 'fileSize');
386
    }
387
388
    /**
389
     * @ignore
390
     */
391
    public function getType($path)
392
    {
393
        return $this->spl($path)->getType();
394
    }
395
396
    /**
397
     * @ignore
398
     */
399 9
    public function isDir($dir)
400
    {
401 9
        return $this->spl($dir)->isDir();
402
    }
403
404
    /**
405
     * @ignore
406
     */
407
    public function isDot($dir)
408
    {
409
        return in_array($this->getBasename($dir), ['.', '..']);
410
    }
411
412
    /**
413
     * @ignore
414
     */
415 9
    public function isFile($file)
416
    {
417 9
        return (bool) $this->attribute($file, 'isExists');
418
    }
419
420
    /**
421
     * @ignore
422
     */
423
    public function isLink($link)
424
    {
425
        return $this->spl($link)->isLink();
426
    }
427
428
    /**
429
     * @ignore
430
     */
431
    public function isReadable($path)
432
    {
433
        return $this->spl($path)->isReadable();
434
    }
435
436
    /**
437
     * @ignore
438
     */
439
    public function isWritable($path)
440
    {
441
        return $this->spl($path)->isWritable();
442
    }
443
444
    /**
445
     * @ignore
446
     */
447
    public function getCommandResult($command)
448
    {
449
        return Console::exec($command);
0 ignored issues
show
Bug Best Practice introduced by
The expression return icy2003\php\ihelp...Console::exec($command) also could return the type false which is incompatible with the return type mandated by icy2003\php\icomponents\...ase::getCommandResult() of string.
Loading history...
450
    }
451
452
    /**
453
     * @ignore
454
     */
455 24
    public function getRealpath($path)
456
    {
457 24
        $realPath = realpath($path);
458 24
        if (false === $realPath) {
459 24
            $realPath = $path;
460
        }
461 24
        return $realPath;
462
    }
463
464
    /**
465
     * @ignore
466
     */
467
    public function getLists($dir = null, $flags = FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE_DISABLED)
468
    {
469
        null === $dir && $dir = $this->getRealpath('./');
470
        $dir = rtrim($this->__file($dir), '/') . '/';
471
        $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS);
472
        if (I::hasFlag($flags, FileConstants::RECURSIVE)) {
473
            $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
474
        }
475
        $files = [];
476
        /**
477
         * @var \RecursiveDirectoryIterator $file
478
         */
479
        foreach ($iterator as $file) {
480
            if (I::hasFlag($flags, FileConstants::COMPLETE_PATH)) {
481
                $files[] = $file->getPathname();
482
            } else {
483
                $files[] = $file->getFilename();
484
            }
485
        }
486
        return $files;
487
    }
488
489
    /**
490
     * @ignore
491
     */
492 1
    public function getFileContent($file)
493
    {
494 1
        return file_get_contents($this->__file($file));
495
    }
496
497
    /**
498
     * @ignore
499
     */
500
    public function putFileContent($file, $string, $mode = 0777)
501
    {
502
        $this->createDir($this->getDirname($file), $mode);
503
        $isCreated = false !== file_put_contents($this->__file($file), $string);
504
        $this->chmod($file, $mode, FileConstants::RECURSIVE_DISABLED);
505
        return $isCreated;
506
    }
507
508
    /**
509
     * @ignore
510
     */
511 1
    public function deleteFile($file)
512
    {
513 1
        if ($this->isFile($file)) {
514 1
            $this->close($file);
515 1
            return unlink($this->__file($file));
516
        }
517
        return true;
518
    }
519
520
    /**
521
     * @ignore
522
     */
523
    public function uploadFile($fileMap, $overwrite = true)
524
    {
525
        return false;
526
    }
527
528
    /**
529
     * - 从远程下载文件到本地
530
     * @param callback|null $callback 执行中的回调([当前下载的字节], [总字节])
531
     * @param callback|null $finishCallback 执行完成后的回调([本地文件 \SplFileObject])
532
     *
533
     * @ignore
534
     */
535
    public function downloadFile($fileMap, $overwrite = true, $callback = null, $finishCallback = null)
536
    {
537
        set_time_limit(0);
538
        list($fromFile, $toFile) = $this->fileMap($fileMap);
539
        if ($this->isFile($toFile) && false === $overwrite) {
540
            return true;
541
        }
542
        $fromSpl = $this->spl($fromFile, 'rb');
543
        $toSpl = $this->spl($toFile, 'wb');
544
        $size = 0;
545
        $total = $this->getFilesize($fromFile);
546
        while (false === $fromSpl->eof()) {
547
            $out = $fromSpl->fread($this->_c['buffer']);
548
            $toSpl->fwrite($out);
549
            $size += Strings::byteLength($out);
550
            I::call($callback, [$size, $total]);
551
        }
552
        $this->close([$fromFile, $toFile]);
553
        I::call($finishCallback, [$toSpl]);
554
    }
555
556
    /**
557
     * download() 配置名:ip
558
     */
559
    const C_DOWNLOAD_IP = 'ip';
560
    /**
561
     * download() 配置名:speed
562
     */
563
    const C_DOWNLOAD_SPEED = 'speed';
564
    /**
565
     * download() 配置名:xSendFile
566
     */
567
    const C_DOWNLOAD_X_SEND_FILE = 'xSendFile';
568
    /**
569
     * download() 配置名:xSendFileRoot
570
     */
571
    const C_DOWNLOAD_X_SEND_FILE_ROOT = 'xSendFileRoot';
572
573
    /**
574
     * 服务端给客户端提供下载请求
575
     *
576
     * @param string|array $fileName self::fileMap()
577
     * @param null|array $config 配置项
578
     *      - ip:限特定 IP 访问,数组或逗号字符串,默认为 *,即对所有 IP 不限制
579
     *      - speed:限速,默认不限速(读取速度为1024 * [buffer]),单位 kb/s
580
     *      - xSendFile:是否使用 X-Sendfile 进行下载,默认 false,即不使用。X-Sendfile 缓解了 PHP 的压力,但同时 PHP 将失去对资源的控制权,因为 PHP 并不知道资源发完了没
581
     *      - xSendFileRoot:文件根路径,默认为 /protected/。此时 Nginx 可作如下配置,更多 @link https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
582
     *      ```nginx.conf
583
     *      location /protected/ {
584
     *          internal; # 表示这个路径只能在 Nginx 内部访问,不能用浏览器直接访问防止未授权的下载
585
     *          alias   /usr/share/nginx/html/protected/; # 别名
586
     *          # root    /usr/share/nginx/html; # 根目录
587
     *      }
588
     *      ```
589
     * @param callback $callback 下载完成后的回调,参数列表:文件属性数组
590
     *
591
     * @return void
592
     * @throws Exception
593
     */
594
    public function download($fileName, $config = null, $callback = null)
595
    {
596
        Header::xPoweredBy();
597
        set_time_limit(0);
598
        list($originName, $downloadName) = $this->fileMap($fileName);
599
        $originName = $this->__file($originName);
600
        try {
601
            $ip = I::get($config, self::C_DOWNLOAD_IP, '*');
602
            if ('*' !== $ip) {
603
                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

603
                C::assertTrue(Arrays::in((new Request())->getUserIP(), Strings::toArray(/** @scrutinizer ignore-type */ $ip)), 'http/1.1 403.6 此 IP 禁止访问');
Loading history...
604
            }
605
            if ($this->isFile($originName)) {
606
                $fileSize = $this->getFilesize($originName);
607
                header('Content-type:application/octet-stream');
608
                header('Accept-Ranges:bytes');
609
                header('Content-Length:' . $fileSize);
610
                header('Content-Disposition: attachment; filename=' . $downloadName);
611
                $speed = I::get($config, self::C_DOWNLOAD_SPEED, 0);
612
                $xSendFile = I::get($config, self::C_DOWNLOAD_X_SEND_FILE, false);
613
                $xSendFileRoot = I::get($config, self::C_DOWNLOAD_X_SEND_FILE_ROOT, '/protected/');
614
                if (true === $xSendFile) {
615
                    $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

615
                    $path = rtrim(/** @scrutinizer ignore-type */ $xSendFileRoot, '/') . '/' . $this->getBasename($originName);
Loading history...
616
                    header('X-Accel-Redirect: ' . $path); // Nginx、Cherokee 实现了该头
617
                    header('X-Sendfile: ' . $path); // Apache、Lighttpd v1.5、Cherokee 实现了该头
618
                    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

618
                    header('X-LIGHTTPD-send-file: ', /** @scrutinizer ignore-type */ $path); // Lighttpd v1.4 实现了该头
Loading history...
619
                    if ($speed) {
620
                        header('X-Accel-Limit-Rate: ' . $speed); // 单位 kb/s
621
                    }
622
                } else {
623
                    flush();
624
                    foreach ($this->dataGenerator($originName, true, ($speed ? $speed : $this->_c['buffer'] * 1024)) as $data) {
0 ignored issues
show
Bug introduced by
It seems like $speed ? $speed : $this->_c['buffer'] * 1024 can also be of type string; however, parameter $buffer of icy2003\php\icomponents\...alFile::dataGenerator() does only seem to accept integer|null, 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

624
                    foreach ($this->dataGenerator($originName, true, /** @scrutinizer ignore-type */ ($speed ? $speed : $this->_c['buffer'] * 1024)) as $data) {
Loading history...
625
                        echo $data;
626
                        flush();
627
                        $speed > 0 && sleep(1);
628
                    }
629
                }
630
            }
631
        } catch (Exception $e) {
632
            header($e->getMessage());
633
        } finally {
634
            I::call($callback, [$this->_attributes]);
635
            // 必须要终止掉,防止发送其他数据导致错误
636
            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...
637
        }
638
    }
639
640
    /**
641
     * @ignore
642
     */
643
    public function chown($file, $user, $flags = FileConstants::RECURSIVE_DISABLED)
644
    {
645
        $file = $this->__file($file);
646
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
647
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
648
            foreach ($files as $subFile) {
649
                @chown($subFile, $user);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chown(). 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

649
                /** @scrutinizer ignore-unhandled */ @chown($subFile, $user);

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...
650
            }
651
        } elseif ($this->isFile($file)) {
652
            return @chown($file, $user);
653
        } else {
654
            return false;
655
        }
656
    }
657
658
    /**
659
     * @ignore
660
     */
661
    public function chgrp($file, $group, $flags = FileConstants::RECURSIVE_DISABLED)
662
    {
663
        $file = $this->__file($file);
664
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
665
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
666
            foreach ($files as $subFile) {
667
                @chgrp($subFile, $group);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chgrp(). 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

667
                /** @scrutinizer ignore-unhandled */ @chgrp($subFile, $group);

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...
668
            }
669
        } elseif ($this->isFile($file)) {
670
            return @chgrp($file, $group);
671
        } else {
672
            return false;
673
        }
674
    }
675
676
    /**
677
     * @ignore
678
     */
679 9
    public function chmod($file, $mode = 0777, $flags = FileConstants::RECURSIVE_DISABLED)
680
    {
681 9
        $file = $this->__file($file);
682 9
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
683
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
684
            foreach ($files as $subFile) {
685
                @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

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