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
Push — master ( e3ed33...e324fa )
by t
01:57
created

LocalFile::attribute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
c 1
b 1
f 0
nc 1
nop 2
dl 0
loc 4
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\I;
13
use icy2003\php\icomponents\file\FileInterface;
14
use icy2003\php\ihelpers\Charset;
15
use icy2003\php\ihelpers\Header;
16
17
/**
18
 * 本地文件
19
 *
20
 * - 支持本地文件操作
21
 * - 支持网络文件:文件是否存在、文件大小
22
 */
23
class LocalFile extends Base implements FileInterface
24
{
25
26
    /**
27
     * 加载文件用的函数
28
     *
29
     * @var callback
30
     */
31
    protected $_functions = [
32
        'loader' => null,
33
    ];
34
35
    /**
36
     * 文件属性
37
     *
38
     * - 文件名为键,属性为值
39
     *
40
     * @var array
41
     */
42
    protected $_attributes = [];
43
44
    /**
45
     * 初始化
46
     *
47
     * @param mixed $locale 地区信息
48
     */
49
    public function __construct($locale = 'zh_CN.UTF-8')
50
    {
51
        setlocale(LC_ALL, $locale);
52
        clearstatcache();
53
        $this->_functions['loader'] = function ($fileName) {
54
            $hashName = $this->__hash($fileName);
55
            $this->_attributes[$hashName] = I::get($this->_attributes, $hashName, [
56
                'isCached' => false,
57
                'isLocal' => true,
58
                'file' => $this->__file($fileName),
59
                // 以下属性需要重新设置
60
                'isExists' => false,
61
                'fileSize' => 0,
62
            ]);
63
            // 如果已经被缓存了,直接返回
64
            if (true === $this->_attributes[$hashName]['isCached']) {
65
                return;
66
            }
67
            $this->_attributes[$hashName]['isCached'] = true;
68
            if (preg_match('/^https?:\/\//', $fileName)) {
69
                $this->_attributes[$hashName]['isLocal'] = false;
70
                // 加载网络文件
71
                if (extension_loaded('curl')) {
72
                    $curl = curl_init($fileName);
73
                    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

73
                    curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_NOBODY, true);
Loading history...
74
                    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
75
                    curl_setopt($curl, CURLOPT_HEADER, true);
76
                    $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

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

77
                    if ($result && $info = curl_getinfo(/** @scrutinizer ignore-type */ $curl)) {
Loading history...
78
                        if (200 == $info['http_code']) {
79
                            $this->_attributes[$hashName]['isExists'] = true;
80
                            $this->_attributes[$hashName]['fileSize'] = $info['download_content_length'];
81
                        }
82
                    }
83
                    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

83
                    curl_close(/** @scrutinizer ignore-type */ $curl);
Loading history...
84
                } elseif ((bool) ini_get('allow_url_fopen')) {
85
                    $headArray = (array) get_headers($fileName, 1);
86
                    if (preg_match('/200/', $headArray[0])) {
87
                        $this->_attributes[$hashName]['isExists'] = true;
88
                        $this->_attributes[$hashName]['fileSize'] = $headArray['Content-Length'];
89
                    }
90
                } else {
91
                    $url = parse_url($fileName);
92
                    $host = $url['host'];
93
                    $path = I::get($url, 'path', '/');
94
                    $port = I::get($url, 'port', 80);
95
                    $fp = fsockopen($host, $port);
96
                    if (is_resource($fp)) {
97
                        $header = [
98
                            'GET ' . $path . ' HTTP/1.0',
99
                            'HOST: ' . $host . ':' . $port,
100
                            'Connection: Close',
101
                        ];
102
                        fwrite($fp, implode('\r\n', $header) . '\r\n\r\n');
103
                        while (!feof($fp)) {
104
                            $line = fgets($fp);
105
                            if ('' == trim($line)) {
106
                                break;
107
                            } else {
108
                                preg_match('/HTTP.*(\s\d{3}\s)/', $line, $arr) && $this->_attributes[$hashName]['isExists'] = true;
109
                                preg_match('/Content-Length:(.*)/si', $line, $arr) && $this->_attributes[$hashName]['fileSize'] = trim($arr[1]);
110
                            }
111
                        }
112
                    }
113
                    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

113
                    fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
114
                }
115
            } else {
116
                $this->_attributes[$hashName]['isLocal'] = true;
117
                $this->_attributes[$hashName]['isExists'] = file_exists($this->_attributes[$hashName]['file']);
118
                $this->_attributes[$hashName]['fileSize'] = filesize($this->_attributes[$hashName]['file']);
119
                $this->_attributes[$hashName]['spl'] = new \SplFileObject($this->_attributes[$hashName]['file']);
120
                $this->_attributes[$hashName]['splInfo'] = new \SplFileInfo($this->_attributes[$hashName]['file']);
121
            }
122
        };
123
    }
124
125
    /**
126
     * 获取 Hash 值
127
     *
128
     * @param string $fileName
129
     *
130
     * @return string
131
     */
132
    private function __hash($fileName)
133
    {
134
        return md5($fileName);
135
    }
136
137
    /**
138
     * 返回路径别名
139
     *
140
     * @param string $file
141
     *
142
     * @return string
143
     */
144
    private function __file($file)
145
    {
146
        return (string) I::getAlias($file);
147
    }
148
149
    /**
150
     * 获取网络文件的属性
151
     *
152
     * @param string $fileName
153
     * @param string $name
154
     *
155
     * @return mixed
156
     */
157
    public function attribute($fileName, $name)
158
    {
159
        I::trigger($this->_functions['loader'], [$fileName]);
160
        return I::get($this->_attributes, $this->__hash($fileName) . '.' . $name);
161
    }
162
163
    /**
164
     * 获取文件对象
165
     *
166
     * @param string $fileName
167
     *
168
     * @return \SplFileObject
169
     */
170
    public function spl($fileName)
171
    {
172
        return $this->attribute($fileName, 'spl');
173
    }
174
175
    /**
176
     * 获取文件信息对象
177
     *
178
     * @param string $fileName
179
     *
180
     * @return \SplFileInfo
181
     */
182
    public function splInfo($fileName)
183
    {
184
        return $this->attribute($fileName, 'splInfo');
185
    }
186
187
    /**
188
     * 遍历行的生成器
189
     *
190
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
191
     *
192
     * @param string $fileName
193
     * @param boolean $autoClose 是否自动关闭文件,默认 false
194
     *
195
     * @return \Generator
196
     */
197
    public function linesGenerator($fileName, $autoClose = false)
198
    {
199
        try {
200
            $spl = $this->spl($fileName);
201
            while ($line = $spl->fgets()) {
202
                yield $line;
203
            }
204
        } finally {
205
            true === $autoClose && $this->close($fileName);
206
        }
207
    }
208
209
    /**
210
     * 返回文本的某行
211
     *
212
     * - 每取一行,文件指针会回到初始位置,如果需要大量的行,请直接使用 linesGenerator
213
     * - 自动关闭后再次调用需要重新读取文件,不建议自动关闭
214
     *
215
     * @param string $fileName
216
     * @param integer $num 行号
217
     * @param boolean $autoClose 是否自动关闭文件,默认 false
218
     *
219
     * @return string
220
     */
221
    public function line($fileName, $num = 0, $autoClose = false)
222
    {
223
        foreach ($this->linesGenerator($fileName, $autoClose) as $k => $line) {
224
            if ($k == $num) {
225
                $this->spl($fileName)->rewind();
226
                return $line;
227
            }
228
        }
229
    }
230
231
    /**
232
     * 遍历字节的生成器
233
     *
234
     * @param string $fileName
235
     * @param boolean $autoClose 是否自动关闭文件,默认 false
236
     * @param integer $buffer 缓冲区长度,默认 1024
237
     *
238
     * @return \Generator
239
     */
240
    public function dataGenerator($fileName, $autoClose = false, $buffer = 1024)
241
    {
242
        $bufferSize = 0;
243
        try {
244
            while (!$this->spl($fileName)->eof() && $this->splInfo($fileName)->getSize() > $bufferSize) {
245
                $bufferSize += $buffer;
246
                yield $this->spl($fileName)->fread($bufferSize);
247
            }
248
        } finally {
249
            true === $autoClose && $this->close($fileName);
250
        }
251
    }
252
253
    /**
254
     * @ignore
255
     */
256
    public function getATime($fileName)
257
    {
258
        return fileatime($this->__file($fileName));
259
    }
260
261
    /**
262
     * @ignore
263
     */
264
    public function getBasename($path, $suffix = null)
265
    {
266
        return parent::getBasename($this->__file($path), $suffix);
267
    }
268
269
    /**
270
     * @ignore
271
     */
272
    public function getCTime($fileName)
273
    {
274
        return filectime($this->__file($fileName));
275
    }
276
277
    /**
278
     * @ignore
279
     */
280
    public function getExtension($fileName)
281
    {
282
        return pathinfo($this->__file($fileName), PATHINFO_EXTENSION);
283
    }
284
285
    /**
286
     * @ignore
287
     */
288
    public function getFilename($fileName)
289
    {
290
        return pathinfo($this->__file($fileName), PATHINFO_FILENAME);
291
    }
292
293
    /**
294
     * @ignore
295
     */
296
    public function getMtime($fileName)
297
    {
298
        return filemtime($this->__file($fileName));
299
    }
300
301
    /**
302
     * @ignore
303
     */
304
    public function getDirname($path)
305
    {
306
        return parent::getDirname($this->__file($path));
307
    }
308
309
    /**
310
     * @ignore
311
     */
312
    public function getPerms($path)
313
    {
314
        return fileperms($this->__file($path));
315
    }
316
317
    /**
318
     * @ignore
319
     */
320
    public function getFilesize($fileName)
321
    {
322
        return $this->attribute($fileName, 'fileSize');
323
    }
324
325
    /**
326
     * @ignore
327
     */
328
    public function getType($path)
329
    {
330
        return filetype($this->__file($path));
331
    }
332
333
    /**
334
     * @ignore
335
     */
336
    public function isDir($dir)
337
    {
338
        return is_dir($this->__file($dir));
339
    }
340
341
    /**
342
     * @ignore
343
     */
344
    public function isDot($dir)
345
    {
346
        return in_array($this->getBasename($dir), ['.', '..']);
347
    }
348
349
    /**
350
     * @ignore
351
     */
352
    public function isFile($file)
353
    {
354
        return $this->attribute($file, 'isExists');
355
    }
356
357
    /**
358
     * @ignore
359
     */
360
    public function isLink($link)
361
    {
362
        return is_link($this->__file($link));
363
    }
364
365
    /**
366
     * @ignore
367
     */
368
    public function isReadable($path)
369
    {
370
        return is_readable($this->__file($path));
371
    }
372
373
    /**
374
     * @ignore
375
     */
376
    public function isWritable($path)
377
    {
378
        return is_writable($this->__file($path));
379
    }
380
381
    /**
382
     * @ignore
383
     */
384
    public function getCommandResult($command)
385
    {
386
        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...
387
    }
388
389
    /**
390
     * @ignore
391
     */
392
    public function getRealpath($path)
393
    {
394
        return realpath($this->__file($path));
395
    }
396
397
    /**
398
     * @ignore
399
     */
400
    public function getLists($dir = null, $flags = FileConstants::COMPLETE_PATH)
401
    {
402
        null === $dir && $dir = $this->getRealpath('./');
403
        $dir = $this->__file(rtrim($dir, '/') . '/');
404
        $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS);
405
        if (I::hasFlag($flags, FileConstants::RECURSIVE)) {
406
            $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
407
        }
408
        $files = [];
409
        /**
410
         * @var \RecursiveDirectoryIterator $file
411
         */
412
        foreach ($iterator as $file) {
413
            if (I::hasFlag($flags, FileConstants::COMPLETE_PATH)) {
414
                $files[] = $file->getPathname();
415
            } else {
416
                $files[] = $file->getFilename();
417
            }
418
        }
419
        return $files;
420
    }
421
422
    /**
423
     * @ignore
424
     */
425
    public function getFileContent($file)
426
    {
427
        return file_get_contents($this->__file($file));
428
    }
429
430
    /**
431
     * @ignore
432
     */
433
    public function putFileContent($file, $string, $mode = 0777)
434
    {
435
        $file = $this->__file($file);
436
        $this->createDir($this->getDirname($file), $mode);
437
        $isCreated = false !== file_put_contents($file, $string);
438
        $this->chmod($file, $mode, FileConstants::RECURSIVE_DISABLED);
439
        return $isCreated;
440
    }
441
442
    /**
443
     * @ignore
444
     */
445
    public function deleteFile($file)
446
    {
447
        $file = $this->__file($file);
448
        if ($this->isFile($file)) {
449
            return unlink($file);
450
        }
451
        return true;
452
    }
453
454
    /**
455
     * @ignore
456
     */
457
    public function uploadFile($toFile, $fromFile = null, $overwrite = true)
458
    {
459
        return false;
460
    }
461
462
    /**
463
     * @ignore
464
     */
465
    public function downloadFile($fromFile, $toFile = null, $overwrite = true)
466
    {
467
        return false;
468
    }
469
470
    /**
471
     * 客户端向服务端发起下载请求
472
     *
473
     * @param string $fileName
474
     *
475
     * @return void
476
     * @throws Exception
477
     */
478
    public function download($fileName)
479
    {
480
        $fileName = $this->__file($fileName);
481
        try {
482
            if ($this->isFile($fileName)) {
483
                header('Content-type:application/octet-stream');
484
                header('Accept-Ranges:bytes');
485
                header('Accept-Length:' . $this->getFilesize($fileName));
486
                header('Content-Disposition: attachment; filename=' . Charset::toCn($this->getBasename($fileName)));
487
                foreach ($this->dataGenerator($fileName) as $data) {
488
                    echo $data;
489
                }
490
            }
491
        } catch (Exception $e) {
492
            Header::notFound();
493
            throw $e;
494
        }
495
    }
496
497
    /**
498
     * @ignore
499
     */
500
    public function chown($file, $user, $flags = FileConstants::RECURSIVE_DISABLED)
501
    {
502
        $file = $this->__file($file);
503
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
504
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
505
            foreach ($files as $subFile) {
506
                chown($subFile, $user);
507
            }
508
        }
509
        return chown($file, $user);
510
    }
511
512
    /**
513
     * @ignore
514
     */
515
    public function chgrp($file, $group, $flags = FileConstants::RECURSIVE_DISABLED)
516
    {
517
        $file = $this->__file($file);
518
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
519
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
520
            foreach ($files as $subFile) {
521
                chgrp($subFile, $group);
522
            }
523
        }
524
        return chgrp($file, $group);
525
    }
526
527
    /**
528
     * @ignore
529
     */
530
    public function chmod($file, $mode = 0777, $flags = FileConstants::RECURSIVE_DISABLED)
531
    {
532
        $file = $this->__file($file);
533
        if ($this->isDir($file) && I::hasFlag($flags, FileConstants::RECURSIVE)) {
534
            $files = $this->getLists($file, FileConstants::COMPLETE_PATH | FileConstants::RECURSIVE);
535
            foreach ($files as $subFile) {
536
                chmod($subFile, $mode);
537
            }
538
        }
539
        return (bool) chmod($file, $mode);
540
    }
541
542
    /**
543
     * @ignore
544
     */
545
    public function symlink($from, $to)
546
    {
547
        $from = $this->__file($from);
548
        $to = $this->__file($to);
549
        return symlink($from, $to);
550
    }
551
552
    /**
553
     * @ignore
554
     */
555
    public function close($fileName = null)
556
    {
557
        $fileName = $this->__file($fileName);
558
        if (is_string($fileName)) {
0 ignored issues
show
introduced by
The condition is_string($fileName) is always true.
Loading history...
559
            $fileName = [$this->__hash($fileName)];
560
        } elseif (is_array($fileName)) {
561
            foreach ($fileName as $k => $name) {
562
                $fileName[$k] = $this->__hash($name);
563
            }
564
        }
565
        foreach ($this->_attributes as $hashName => /** @scrutinizer ignore-unused */$attribute) {
566
            if (null === $fileName || is_array($fileName) && in_array($hashName, $fileName)) {
567
                unset($this->_attributes[$hashName]);
568
            }
569
        }
570
        return true;
571
    }
572
573
    /**
574
     * @ignore
575
     */
576
    protected function _copy($fromFile, $toFile)
577
    {
578
        $fromFile = $this->__file($fromFile);
579
        $toFile = $this->__file($toFile);
580
        return copy($fromFile, $toFile);
581
    }
582
583
    /**
584
     * @ignore
585
     */
586
    protected function _move($fromFile, $toFile)
587
    {
588
        $fromFile = $this->__file($fromFile);
589
        $toFile = $this->__file($toFile);
590
        return rename($fromFile, $toFile);
591
    }
592
593
    /**
594
     * @ignore
595
     */
596
    protected function _mkdir($dir, $mode = 0777)
597
    {
598
        $dir = $this->__file($dir);
599
        return mkdir($dir, $mode);
600
    }
601
602
    /**
603
     * @ignore
604
     */
605
    protected function _rmdir($dir)
606
    {
607
        $dir = $this->__file($dir);
608
        return rmdir($dir);
609
    }
610
611
}
612