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 (0ed8da)
by t
03:24
created

LocalFile::getDirname()   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\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
                if (is_resource($curl)) {
140 1
                    curl_setopt($curl, CURLOPT_NOBODY, true);
141 1
                    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
142 1
                    curl_setopt($curl, CURLOPT_HEADER, true);
143
                    // 公用名(Common Name)一般来讲就是填写你将要申请SSL证书的域名 (domain)或子域名(sub domain)
144
                    // - 设置为 1 是检查服务器SSL证书中是否存在一个公用名(common name)
145
                    // - 设置成 2,会检查公用名是否存在,并且是否与提供的主机名匹配
146
                    // - 0 为不检查名称。 在生产环境中,这个值应该是 2(默认值)
147 1
                    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
148
                    // 禁止 cURL 验证对等证书(peer's certificate)。要验证的交换证书可以在 CURLOPT_CAINFO 选项中设置
149 1
                    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
150 1
                    $result = curl_exec($curl);
151 1
                    if ($result && $info = curl_getinfo($curl)) {
152 1
                        if (200 == $info['http_code']) {
153 1
                            $this->_attributes[$hashName]['isExists'] = true;
154 1
                            $this->_attributes[$hashName]['fileSize'] = (int) $info['download_content_length'];
155
                        }
156
                    }
157 1
                    curl_close($curl);
158
                }
159 1
                return $this;
160
            }
161 1
            if ('fsockopen' === $this->_c['loader']) {
162 1
                $url = parse_url($fileName);
163 1
                $host = $url['host'];
164 1
                $path = (string) I::get($url, 'path', '/');
165 1
                $port = (int) I::get($url, 'port', 80);
166 1
                $fp = fsockopen($host, $port);
167 1
                if (is_resource($fp)) {
168 1
                    fputs($fp, "GET {$path} HTTP/1.1\r\n");
169 1
                    fputs($fp, "Host: {$host}:{$port}\r\n");
170 1
                    fputs($fp, "Connection: Close\r\n\r\n");
171 1
                    while (!feof($fp)) {
172 1
                        $line = fgets($fp);
173 1
                        preg_match('/HTTP.*(\s\d{3}\s)/', $line, $arr) && $this->_attributes[$hashName]['isExists'] = true;
174 1
                        preg_match('/Content-Length:(.*)/si', $line, $arr) && $this->_attributes[$hashName]['fileSize'] = (int) trim($arr[1]);
175
                    }
176 1
                    fclose($fp);
177
                }
178 1
                return $this;
179
            }
180 1
            if ('fopen' === $this->_c['loader'] && (bool) ini_get('allow_url_fopen')) {
181 1
                $headArray = (array) get_headers($fileName, 1);
182 1
                if (preg_match('/200/', $headArray[0])) {
183 1
                    $this->_attributes[$hashName]['isExists'] = true;
184 1
                    $this->_attributes[$hashName]['fileSize'] = (int) $headArray['Content-Length'];
185
                }
186 1
                return $this;
187
            }
188
        } else {
189 9
            $this->_attributes[$hashName]['isLocal'] = true;
190 9
            $this->_attributes[$hashName]['isExists'] = file_exists($fileName);
191 9
            if ($this->_attributes[$hashName]['isExists']) {
192 8
                $this->_attributes[$hashName]['fileSize'] = filesize($fileName);
193
            }
194 9
            $this->chmod($fileName, 0777, FileConstants::RECURSIVE_DISABLED);
195
        }
196 8
        return $this;
197
    }
198
199
    /**
200
     * 获取文件的属性
201
     *
202
     * @param string $fileName
203
     * @param string $name
204
     *
205
     * @return mixed
206
     */
207 9
    public function attribute($fileName, $name)
208
    {
209 9
        $this->_load($fileName);
210 9
        return I::get($this->_attributes, $this->__hash($fileName) . '.' . $name);
211
    }
212
213
    /**
214
     * 获取文件对象
215
     *
216
     * @param string $fileName
217
     * @param string $mode 读写的模式,默认 rb
218
     *
219
     * @return \SplFileObject|null
220
     */
221 9
    public function spl($fileName, $mode = 'rb')
222
    {
223 9
        $this->_c['mode'] = $mode;
224 9
        $spl = $this->attribute($fileName, 'spl');
225 9
        C::assertTrue($spl instanceof \SplFileObject, '文件打开失败:' . $fileName);
226 8
        return $spl;
227
    }
228
229
    /**
230
     * 获取文件信息对象
231
     *
232
     * @param string $fileName
233
     *
234
     * @return \SplFileInfo|null
235
     */
236 1
    public function splInfo($fileName)
237
    {
238 1
        $splInfo = $this->attribute($fileName, 'splInfo');
239 1
        C::assertTrue($splInfo instanceof \SplFileInfo, '文件打开失败:' . $fileName);
240 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|null.
Loading history...
241
    }
242
243
    /**
244
     * 遍历行的生成器
245
     *
246
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
247
     *
248
     * @param string $fileName
249
     * @param boolean $autoClose 是否自动关闭文件,默认 false
250
     *
251
     * @return \Generator
252
     */
253 2
    public function linesGenerator($fileName, $autoClose = false)
254
    {
255
        try {
256 2
            $spl = $this->spl($fileName, 'r');
257 2
            while (false === $spl->eof() && ($line = $spl->fgets())) {
258 2
                true === $this->_c['rtrim'] && $line = rtrim($line);
259 2
                yield $line;
260
            }
261 2
        } finally {
262 2
            true === $autoClose && $this->close($fileName);
263
        }
264 2
    }
265
266
    /**
267
     * 返回文本的某行
268
     *
269
     * - 每取一行,文件指针会回到初始位置,如果需要大量的行,请直接使用 linesGenerator
270
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
271
     *
272
     * @param string $fileName
273
     * @param integer $lineNumber 行号
274
     * @param boolean $autoClose 是否自动关闭文件,默认 false
275
     *
276
     * @return string|null
277
     */
278 1
    public function line($fileName, $lineNumber = 0, $autoClose = false)
279
    {
280 1
        $spl = $this->spl($fileName, 'r');
281 1
        $lineNumber = (int) $lineNumber;
282 1
        foreach ($this->linesGenerator($fileName, $autoClose) as $k => $line) {
283 1
            if ($k === $lineNumber) {
284 1
                $spl->rewind();
285 1
                return $line;
286
            }
287
        }
288 1
        return null;
289
    }
290
291
    /**
292
     * 遍历字节的生成器
293
     *
294
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
295
     *
296
     * @param string $fileName
297
     * @param boolean $autoClose 是否自动关闭文件,默认 false
298
     * @param integer|null $buffer 每次读取的字节数,默认 null,值等于初始化时的 buffer 选项
299
     *
300
     * @return \Generator
301
     */
302 1
    public function dataGenerator($fileName, $autoClose = false, $buffer = null)
303
    {
304 1
        $bufferSize = 0;
305 1
        null === $buffer && $buffer = $this->_c['buffer'];
306
        try {
307 1
            $spl = $this->spl($fileName, 'rb');
308 1
            $size = $this->getFilesize($fileName);
309 1
            while (!$spl->eof() && $size > $bufferSize) {
310 1
                $bufferSize += $buffer;
311 1
                yield $spl->fread($bufferSize);
312
            }
313 1
        } finally {
314 1
            true === $autoClose && $this->close($fileName);
315
        }
316 1
    }
317
318
    /**
319
     * @ignore
320
     */
321
    public function getATime($fileName)
322
    {
323
        return $this->spl($fileName)->getATime();
324
    }
325
326
    /**
327
     * @ignore
328
     */
329
    public function getBasename($file, $suffix = null)
330
    {
331
        return parent::getBasename($this->__file($file), $suffix);
332
    }
333
334
    /**
335
     * @ignore
336
     */
337
    public function getCTime($fileName)
338
    {
339
        return $this->spl($fileName)->getCTime();
340
    }
341
342
    /**
343
     * @ignore
344
     */
345
    public function getExtension($fileName)
346
    {
347
        return $this->spl($fileName)->getExtension();
348
    }
349
350
    /**
351
     * @ignore
352
     */
353
    public function getFilename($fileName)
354
    {
355
        return $this->spl($fileName)->getFilename();
356
    }
357
358
    /**
359
     * @ignore
360
     */
361
    public function getMtime($fileName)
362
    {
363
        return $this->spl($fileName)->getMTime();
364
    }
365
366
    /**
367
     * @ignore
368
     */
369
    public function getDirname($path)
370
    {
371
        return parent::getDirname($this->__file($path));
372
    }
373
374
    /**
375
     * @ignore
376
     */
377
    public function getPerms($path)
378
    {
379
        return $this->spl($path)->getPerms();
380
    }
381
382
    /**
383
     * @ignore
384
     */
385 2
    public function getFilesize($fileName)
386
    {
387 2
        return (int) $this->attribute($fileName, 'fileSize');
388
    }
389
390
    /**
391
     * @ignore
392
     */
393
    public function getType($path)
394
    {
395
        return $this->spl($path)->getType();
396
    }
397
398
    /**
399
     * @ignore
400
     */
401 9
    public function isDir($dir)
402
    {
403 9
        return $this->spl($dir)->isDir();
404
    }
405
406
    /**
407
     * @ignore
408
     */
409
    public function isDot($dir)
410
    {
411
        return in_array($this->getBasename($dir), ['.', '..']);
412
    }
413
414
    /**
415
     * @ignore
416
     */
417 9
    public function isFile($file)
418
    {
419 9
        return (bool) $this->attribute($file, 'isExists');
420
    }
421
422
    /**
423
     * @ignore
424
     */
425
    public function isLink($link)
426
    {
427
        return $this->spl($link)->isLink();
428
    }
429
430
    /**
431
     * @ignore
432
     */
433
    public function isReadable($path)
434
    {
435
        return $this->spl($path)->isReadable();
436
    }
437
438
    /**
439
     * @ignore
440
     */
441
    public function isWritable($path)
442
    {
443
        return $this->spl($path)->isWritable();
444
    }
445
446
    /**
447
     * @ignore
448
     */
449
    public function getCommandResult($command)
450
    {
451
        return Console::exec($command);
452
    }
453
454
    /**
455
     * @ignore
456
     */
457 24
    public function getRealpath($path)
458
    {
459 24
        $realPath = realpath($path);
460 24
        if (false === $realPath) {
461 24
            $realPath = $path;
462
        }
463 24
        return $realPath;
464
    }
465
466
    /**
467
     * @ignore
468
     */
469
    public function getLists($dir = null, $flags = FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE_DISABLED)
470
    {
471
        null === $dir && $dir = $this->getRealpath('./');
472
        $dir = rtrim($this->__file($dir), '/') . '/';
473
        $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS);
474
        if (I::hasFlag($flags, FileConstants::RECURSIVE)) {
475
            $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
476
        }
477
        $files = [];
478
        /**
479
         * @var \RecursiveDirectoryIterator $file
480
         */
481
        foreach ($iterator as $file) {
482
            if (I::hasFlag($flags, FileConstants::COMPLETE_PATH)) {
483
                $files[] = $file->getPathname();
484
            } else {
485
                $files[] = $file->getFilename();
486
            }
487
        }
488
        return $files;
489
    }
490
491
    /**
492
     * @ignore
493
     */
494 1
    public function getFileContent($file)
495
    {
496 1
        return file_get_contents($this->__file($file));
497
    }
498
499
    /**
500
     * @ignore
501
     */
502
    public function putFileContent($file, $string, $mode = 0777)
503
    {
504
        $this->createDir($this->getDirname($file), $mode);
505
        $isCreated = false !== file_put_contents($this->__file($file), $string);
506
        $this->chmod($file, $mode, FileConstants::RECURSIVE_DISABLED);
507
        return $isCreated;
508
    }
509
510
    /**
511
     * @ignore
512
     */
513 1
    public function deleteFile($file)
514
    {
515 1
        if ($this->isFile($file)) {
516 1
            $this->close($file);
517 1
            return unlink($this->__file($file));
518
        }
519
        return true;
520
    }
521
522
    /**
523
     * @ignore
524
     */
525
    public function uploadFile($fileMap, $overwrite = true)
526
    {
527
        return false;
528
    }
529
530
    /**
531
     * - 从远程下载文件到本地
532
     * @param callback|null $callback 执行中的回调([当前下载的字节], [总字节])
533
     * @param callback|null $finishCallback 执行完成后的回调([本地文件 \SplFileObject])
534
     *
535
     * @ignore
536
     */
537
    public function downloadFile($fileMap, $overwrite = true, $callback = null, $finishCallback = null)
538
    {
539
        set_time_limit(0);
540
        list($fromFile, $toFile) = $this->fileMap($fileMap);
541
        if ($this->isFile($toFile) && false === $overwrite) {
542
            return true;
543
        }
544
        $fromSpl = $this->spl($fromFile, 'rb');
545
        $toSpl = $this->spl($toFile, 'wb');
546
        $size = 0;
547
        $total = $this->getFilesize($fromFile);
548
        while (false === $fromSpl->eof()) {
549
            $out = $fromSpl->fread($this->_c['buffer']);
550
            $toSpl->fwrite($out);
551
            $size += Strings::byteLength($out);
552
            I::call($callback, [$size, $total]);
553
        }
554
        $this->close([$fromFile, $toFile]);
555
        I::call($finishCallback, [$toSpl]);
556
    }
557
558
    /**
559
     * download() 配置名:ip
560
     */
561
    const C_DOWNLOAD_IP = 'ip';
562
    /**
563
     * download() 配置名:speed
564
     */
565
    const C_DOWNLOAD_SPEED = 'speed';
566
    /**
567
     * download() 配置名:xSendFile
568
     */
569
    const C_DOWNLOAD_X_SEND_FILE = 'xSendFile';
570
    /**
571
     * download() 配置名:xSendFileRoot
572
     */
573
    const C_DOWNLOAD_X_SEND_FILE_ROOT = 'xSendFileRoot';
574
575
    /**
576
     * 服务端给客户端提供下载请求
577
     *
578
     * @param string|array $fileName self::fileMap()
579
     * @param null|array $config 配置项
580
     *      - ip:限特定 IP 访问,数组或逗号字符串,默认为 *,即对所有 IP 不限制
581
     *      - speed:限速,默认不限速(读取速度为1024 * [buffer]),单位 kb/s
582
     *      - xSendFile:是否使用 X-Sendfile 进行下载,默认 false,即不使用。X-Sendfile 缓解了 PHP 的压力,但同时 PHP 将失去对资源的控制权,因为 PHP 并不知道资源发完了没
583
     *      - xSendFileRoot:文件根路径,默认为 /protected/。此时 Nginx 可作如下配置,更多 @link https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
584
     *      ```nginx.conf
585
     *      location /protected/ {
586
     *          internal; # 表示这个路径只能在 Nginx 内部访问,不能用浏览器直接访问防止未授权的下载
587
     *          alias   /usr/share/nginx/html/protected/; # 别名
588
     *          # root    /usr/share/nginx/html; # 根目录
589
     *      }
590
     *      ```
591
     * @param callback $callback 下载完成后的回调,参数列表:文件属性数组
592
     *
593
     * @return void
594
     * @throws Exception
595
     */
596
    public function download($fileName, $config = null, $callback = null)
597
    {
598
        Header::xPoweredBy();
599
        set_time_limit(0);
600
        list($originName, $downloadName) = $this->fileMap($fileName);
601
        $originName = $this->__file($originName);
602
        try {
603
            $ip = (string) I::get($config, self::C_DOWNLOAD_IP, '*');
604
            if ('*' !== $ip) {
605
                C::assertTrue(Arrays::in((new Request())->getUserIP(), Strings::toArray($ip)), 'http/1.1 403.6 此 IP 禁止访问');
606
            }
607
            if ($this->isFile($originName)) {
608
                $fileSize = $this->getFilesize($originName);
609
                header('Content-type:application/octet-stream');
610
                header('Accept-Ranges:bytes');
611
                header('Content-Length:' . $fileSize);
612
                header('Content-Disposition: attachment; filename=' . $downloadName);
613
                $speed = (int) I::get($config, self::C_DOWNLOAD_SPEED, 0);
614
                $xSendFile = I::get($config, self::C_DOWNLOAD_X_SEND_FILE, false);
615
                $xSendFileRoot = (string) I::get($config, self::C_DOWNLOAD_X_SEND_FILE_ROOT, '/protected/');
616
                if (true === $xSendFile) {
617
                    $path = rtrim($xSendFileRoot, '/') . '/' . $this->getBasename($originName);
618
                    header('X-Accel-Redirect: ' . $path); // Nginx、Cherokee 实现了该头
619
                    header('X-Sendfile: ' . $path); // Apache、Lighttpd v1.5、Cherokee 实现了该头
620
                    header('X-LIGHTTPD-send-file: ' . $path); // Lighttpd v1.4 实现了该头
621
                    if ($speed) {
622
                        header('X-Accel-Limit-Rate: ' . $speed); // 单位 kb/s
623
                    }
624
                } else {
625
                    flush();
626
                    foreach ($this->dataGenerator($originName, true, ($speed ? $speed : $this->_c['buffer'] * 1024)) as $data) {
627
                        echo $data;
628
                        flush();
629
                        $speed > 0 && sleep(1);
630
                    }
631
                }
632
            }
633
        } catch (Exception $e) {
634
            header($e->getMessage());
635
        } finally {
636
            I::call($callback, [$this->_attributes]);
637
            // 必须要终止掉,防止发送其他数据导致错误
638
            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...
639
        }
640
    }
641
642
    /**
643
     * @ignore
644
     */
645
    public function chown($file, $user, $flags = FileConstants::RECURSIVE_DISABLED)
646
    {
647
        $file = $this->__file($file);
648
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
649
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
650
            foreach ($files as $subFile) {
651
                @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

651
                /** @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...
652
            }
653
        } elseif ($this->isFile($file)) {
654
            return @chown($file, $user);
655
        } else {
656
            return false;
657
        }
658
    }
659
660
    /**
661
     * @ignore
662
     */
663
    public function chgrp($file, $group, $flags = FileConstants::RECURSIVE_DISABLED)
664
    {
665
        $file = $this->__file($file);
666
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
667
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
668
            foreach ($files as $subFile) {
669
                @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

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

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