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.

Wrapper::dir_readdir()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 0
crap 6
1
<?php
2
/*
3
 * This file is part of the php-vfs package.
4
 *
5
 * (c) Michael Donat <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace VirtualFileSystem;
12
13
use VirtualFileSystem\Exception\FileExistsException;
14
use VirtualFileSystem\Exception\NotDirectoryException;
15
use VirtualFileSystem\Exception\NotFoundException;
16
use VirtualFileSystem\Structure\Directory;
17
use VirtualFileSystem\Structure\File;
18
use VirtualFileSystem\Structure\Link;
19
use VirtualFileSystem\Structure\Root;
20
use VirtualFileSystem\Wrapper\DirectoryHandler;
21
use VirtualFileSystem\Wrapper\FileHandler;
22
23
/**
24
 * Stream wrapper class. This is the class that PHP uses as the stream operations handler.
25
 *
26
 * @see http://php.net/streamwrapper for informal protocol description
27
 *
28
 * @author Michael Donat <[email protected]>
29
 * @package php-vfs
30
 * @api
31
 */
32
class Wrapper
33
{
34
    public $context;
35
36
    /**
37
     * @var FileHandler
38
     */
39
    protected $currentlyOpenedFile;
40
41
    /**
42
     * @var DirectoryHandler
43
     */
44
    protected $currentlyOpenedDir;
45
46
    /**
47
     * Returns default expectation for stat() function.
48
     *
49
     * @see http://php.net/stat
50
     *
51
     * @return array<*,integer>
52
     */
53
    protected function getStatArray()
54
    {
55
        $assoc = array(
56
            'dev' => 0,
57
            'ino' => 0,
58
            'mode' => 0,
59
            'nlink' => 0,
60
            'uid' => 0,
61
            'gid' => 0,
62
            'rdev' => 0,
63
            'size' => 123,
64
            'atime' => 0,
65
            'mtime' => 0,
66
            'ctime' => 0,
67
            'blksize' => -1,
68
            'blocks' => -1
69
        );
70
71
        return array_merge(array_values($assoc), $assoc);
72
    }
73
74
    /**
75
     * Returns path stripped of url scheme (http://, ftp://, test:// etc.)
76
     *
77
     * @param string $path
78
     *
79
     * @return string
80
     */
81
    public function stripScheme($path)
82
    {
83
        $scheme = preg_split('#://#', $path, 2);
84
        $scheme = end($scheme);
85
86
        return '/'.ltrim($scheme, '/');
87
    }
88
89
    /**
90
     * Returns Container object fished form default_context_options by scheme.
91
     *
92
     * @param $path
93
     *
94
     * @return Container
95
     */
96
    public function getContainerFromContext($path)
97
    {
98
        $scheme = current(preg_split('#://#', $path));
99
        $options = stream_context_get_options(stream_context_get_default());
100
101
        return $options[$scheme]['Container'];
102
    }
103
104
    /**
105
     * @see http://php.net/streamwrapper.stream-tell
106
     *
107
     * @return int
108
     */
109
    public function stream_tell()
110
    {
111
        return $this->currentlyOpenedFile->position();
112
    }
113
114
    /**
115
     * @see http://php.net/streamwrapper.stream-cast
116
     *
117
     * @return void
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
118
     */
119
    public function stream_cast() {
120
        return false;
121
    }
122
123
    /**
124
     * @see http://php.net/streamwrapper.stream-close
125
     *
126
     * @return void
127
     */
128
    public function stream_close()
129
    {
130
        $this->currentlyOpenedFile = null;
131
    }
132
133
    /**
134
     * Opens stream in selected mode.
135
     *
136
     * @see http://php.net/streamwrapper.stream-open
137
     *
138
     * @param string $path
139
     * @param int    $mode
140
     * @param int    $options
141
     *
142
     * @param $openedPath
143
     * @throws NotDirectoryException
144
     * @throws NotFoundException
145
     * @return bool
146
     */
147
    public function stream_open($path, $mode, $options, &$openedPath)
148
    {
149
        $container = $this->getContainerFromContext($path);
150
        $path = $this->stripScheme($path);
151
152
        $mode = str_split(str_replace('b', '', $mode));
153
154
        $accessDeniedError = function () use ($path, $options) {
155
            if ($options & STREAM_REPORT_ERRORS) {
156
                trigger_error(sprintf('fopen(%s): failed to open stream: Permission denied', $path), E_USER_WARNING);
157
            }
158
159
            return false;
160
        };
161
162
        $appendMode = in_array('a', $mode);
163
        $readMode = in_array('r', $mode);
164
        $writeMode = in_array('w', $mode);
165
        $extended = in_array('+', $mode);
166
167
        if (!$container->hasNodeAt($path)) {
168
            if ($readMode || !$container->hasNodeAt(dirname($path))) {
169
                if ($options & STREAM_REPORT_ERRORS) {
170
                    trigger_error(sprintf('%s: failed to open stream.', $path), E_USER_WARNING);
171
                }
172
173
                return false;
174
            }
175
            $parent = $container->directoryAt(dirname($path));
176
            $permissionHelper = $container->getPermissionHelper($parent);
177
            if (!$permissionHelper->isWritable()) {
178
                return $accessDeniedError();
179
            }
180
            $parent->addFile($container->factory()->getFile(basename($path)));
181
        }
182
183
        $file = $container->nodeAt($path);
184
185
        if ($file instanceof Link) {
186
            $file = $file->getDestination();
187
        }
188
189
        if (($extended || $writeMode || $appendMode) && $file instanceof Directory) {
190
            if ($options & STREAM_REPORT_ERRORS) {
191
                trigger_error(sprintf('fopen(%s): failed to open stream: Is a directory', $path), E_USER_WARNING);
192
            }
193
194
            return false;
195
        }
196
197
        if ($file instanceof Directory) {
198
            $dir = $file;
199
            $file = $container->factory()->getFile('tmp');
200
            $file->chmod($dir->mode());
201
            $file->chown($dir->user());
202
            $file->chgrp($dir->group());
203
        }
204
205
        $permissionHelper = $container->getPermissionHelper($file);
206
207
        $this->currentlyOpenedFile = new FileHandler();
208
        $this->currentlyOpenedFile->setFile($file);
209
210
        if ($extended) {
211
            if (!$permissionHelper->isReadable() || !$permissionHelper->isWritable()) {
212
                return $accessDeniedError();
213
            }
214
            $this->currentlyOpenedFile->setReadWriteMode();
215
        } elseif ($readMode) {
216
            if (!$permissionHelper->isReadable()) {
217
                return $accessDeniedError();
218
            }
219
            $this->currentlyOpenedFile->setReadOnlyMode();
220
        } else { // a or w are for write only
221
            if (!$permissionHelper->isWritable()) {
222
                return $accessDeniedError();
223
            }
224
            $this->currentlyOpenedFile->setWriteOnlyMode();
225
        }
226
227
        if ($appendMode) {
228
            $this->currentlyOpenedFile->seekToEnd();
229
        } elseif ($writeMode) {
230
            $this->currentlyOpenedFile->truncate();
231
            clearstatcache();
232
        }
233
234
        $openedPath = $file->path();
235
236
        return true;
237
    }
238
239
    /**
240
     * Writes data to stream.
241
     *
242
     * @see http://php.net/streamwrapper.stream-write
243
     *
244
     * @param $data
245
     *
246
     * @return false|integer
247
     */
248
    public function stream_write($data)
249
    {
250
        if (!$this->currentlyOpenedFile->isOpenedForWriting()) {
251
            return false;
252
        }
253
        //file access time changes so stat cache needs to be cleared
254
        $written = $this->currentlyOpenedFile->write($data);
255
        clearstatcache();
256
257
        return $written;
258
    }
259
260
    /**
261
     * Returns stat data for file inclusion.
262
     *
263
     * @see http://php.net/streamwrapper.stream-stat
264
     *
265
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be array|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
266
     */
267
    public function stream_stat()
268
    {
269
270
        try {
271
            $file = $this->currentlyOpenedFile->getFile();
272
273
            return array_merge($this->getStatArray(), array(
274
                'mode' => $file->mode(),
275
                'uid' => $file->user(),
276
                'gid' => $file->group(),
277
                'atime' => $file->atime(),
278
                'mtime' => $file->mtime(),
279
                'ctime' => $file->ctime(),
280
                'size' => $file->size()
281
            ));
282
        } catch (NotFoundException $e) {
283
            return false;
284
        }
285
    }
286
287
    /**
288
     * Returns file stat information
289
     *
290
     * @see http://php.net/stat
291
     *
292
     * @param string $path
293
     *
294
     * @return  array|false
295
     */
296
    public function url_stat($path)
297
    {
298
        try {
299
            $file = $this->getContainerFromContext($path)->nodeAt($this->stripScheme($path));
300
301
            return array_merge($this->getStatArray(), array(
302
                'mode' => $file->mode(),
303
                'uid' => $file->user(),
304
                'gid' => $file->group(),
305
                'atime' => $file->atime(),
306
                'mtime' => $file->mtime(),
307
                'ctime' => $file->ctime(),
308
                'size' => $file->size()
309
            ));
310
        } catch (NotFoundException $e) {
311
            return false;
312
        }
313
    }
314
315
    /**
316
     * Reads and returns $bytes amount of bytes from stream.
317
     *
318
     * @see http://php.net/streamwrapper.stream-read
319
     *
320
     * @param int $bytes
321
     *
322
     * @return string|null
323
     */
324
    public function stream_read($bytes)
325
    {
326
        if (!$this->currentlyOpenedFile->isOpenedForReading()) {
327
            return null;
328
        }
329
        $data = $this->currentlyOpenedFile->read($bytes);
330
        //file access time changes so stat cache needs to be cleared
331
        clearstatcache();
332
333
        return $data;
334
    }
335
336
    /**
337
     * Checks whether pointer has reached EOF.
338
     *
339
     * @see http://php.net/streamwrapper.stream-eof
340
     *
341
     * @return bool
342
     */
343
    public function stream_eof()
344
    {
345
        return $this->currentlyOpenedFile->atEof();
346
    }
347
348
    /**
349
     * Flushes the output. This always returns TRUE, since no buffering takes place in FileHandler.
350
     *
351
     * @see http://php.net/streamwrapper.stream-flush
352
     *
353
     * @return bool
354
     */
355
    public function stream_flush()
356
    {
357
        return true;
358
    }
359
360
    /**
361
     * Called in response to mkdir to create directory.
362
     *
363
     * @see http://php.net/streamwrapper.mkdir
364
     *
365
     * @param string $path
366
     * @param int    $mode
367
     * @param int    $options
368
     *
369
     * @return bool
370
     */
371
    public function mkdir($path, $mode, $options)
372
    {
373
        $container = $this->getContainerFromContext($path);
374
        $path = $this->stripScheme($path);
375
        $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
376
377
        try {
378
            //need to check all parents for permissions
379
            $parentPath = $path;
380
            while ($parentPath = dirname($parentPath)) {
381
                try {
382
                    $parent = $container->nodeAt($parentPath);
383
                    $permissionHelper = $container->getPermissionHelper($parent);
384
                    if (!$permissionHelper->isWritable()) {
385
                        trigger_error(sprintf('mkdir: %s: Permission denied', dirname($path)), E_USER_WARNING);
386
387
                        return false;
388
                    }
389
                    if ($parent instanceof Root) {
390
                        break;
391
                    }
392
                } catch (NotFoundException $e) {
393
                    break; //will sort missing parent below
394
                }
395
            }
396
            $container->createDir($path, $recursive, $mode);
397
        } catch (FileExistsException $e) {
398
            trigger_error($e->getMessage(), E_USER_WARNING);
399
400
            return false;
401
        } catch (NotFoundException $e) {
402
            trigger_error(sprintf('mkdir: %s: No such file or directory', dirname($path)), E_USER_WARNING);
403
404
            return false;
405
        }
406
407
        return true;
408
    }
409
410
    /**
411
     * Called in response to chown/chgrp/touch/chmod etc.
412
     *
413
     * @see http://php.net/streamwrapper.stream-metadata
414
     *
415
     * @param string  $path
416
     * @param int     $option
417
     * @param integer $value
418
     *
419
     * @return bool
420
     */
421
    public function stream_metadata($path, $option, $value)
422
    {
423
        $container = $this->getContainerFromContext($path);
424
        $strippedPath = $this->stripScheme($path);
425
426
        try {
427
428
            if ($option == STREAM_META_TOUCH) {
429
                if (!$container->hasNodeAt($strippedPath)) {
430
                    try {
431
                        $container->createFile($strippedPath);
432
                    } catch (NotFoundException $e) {
433
                        trigger_error(
434
                            sprintf('touch: %s: No such file or directory.', $strippedPath),
435
                            E_USER_WARNING
436
                        );
437
438
                        return false;
439
                    }
440
                }
441
                $file = $container->nodeAt($strippedPath);
442
443
                $permissionHelper = $container->getPermissionHelper($file);
444
445
                if (!$permissionHelper->userIsOwner() && !$permissionHelper->isWritable()) {
446
                    trigger_error(
447
                        sprintf('touch: %s: Permission denied', $strippedPath),
448
                        E_USER_WARNING
449
                    );
450
451
                    return false;
452
                }
453
454
                $time = isset($value[0]) ? $value[0] : time();
455
                $atime = isset($value[1]) ? $value[1] : $time;
456
457
                $file->setChangeTime($time);
458
                $file->setModificationTime($time);
459
                $file->setAccessTime($atime);
460
461
                clearstatcache(true, $path);
462
463
                return true;
464
465
            }
466
467
            $node = $container->nodeAt($strippedPath);
468
            $permissionHelper = $container->getPermissionHelper($node);
469
470
            switch ($option) {
471
                case STREAM_META_ACCESS:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
472
473
                    if ($node instanceof Link) {
474
                        $node = $node->getDestination();
475
                        $permissionHelper = $container->getPermissionHelper($node);
476
                    }
477
478
                    if (!$permissionHelper->userIsOwner()) {
479
                        trigger_error(
480
                            sprintf('chmod: %s: Permission denied', $strippedPath),
481
                            E_USER_WARNING
482
                        );
483
484
                        return false;
485
                    }
486
                    $node->chmod($value);
487
                    $node->setChangeTime(time());
488
                    break;
489
490
                case STREAM_META_OWNER_NAME:
491
                    if (!$permissionHelper->userIsRoot() && !$permissionHelper->userIsOwner()) {
492
                        trigger_error(
493
                            sprintf('chown: %s: Permission denied', $strippedPath),
494
                            E_USER_WARNING
495
                        );
496
497
                        return false;
498
                    }
499
                    $uid = function_exists('posix_getpwnam') ? posix_getpwnam($value)['uid'] : 0;
500
                    $node->chown($uid);
501
                    $node->setChangeTime(time());
502
                    break;
503
504
                case STREAM_META_OWNER:
505
                    if (!$permissionHelper->userIsRoot() && !$permissionHelper->userIsOwner()) {
506
                        trigger_error(
507
                            sprintf('chown: %s: Permission denied', $strippedPath),
508
                            E_USER_WARNING
509
                        );
510
511
                        return false;
512
                    }
513
                    $node->chown($value);
514
                    $node->setChangeTime(time());
515
                    break;
516
517
                case STREAM_META_GROUP_NAME:
518
                    if (!$permissionHelper->userIsRoot() && !$permissionHelper->userIsOwner()) {
519
                        trigger_error(
520
                            sprintf('chgrp: %s: Permission denied', $strippedPath),
521
                            E_USER_WARNING
522
                        );
523
524
                        return false;
525
                    }
526
                    $gid = function_exists('posix_getgrnam') ? posix_getgrnam($value)['gid'] : 0;
527
                    $node->chgrp($gid);
528
                    $node->setChangeTime(time());
529
                    break;
530
531
                case STREAM_META_GROUP:
532
                    if (!$permissionHelper->userIsRoot() && !$permissionHelper->userIsOwner()) {
533
                        trigger_error(
534
                            sprintf('chgrp: %s: Permission denied', $strippedPath),
535
                            E_USER_WARNING
536
                        );
537
538
                        return false;
539
                    }
540
                    $node->chgrp($value);
541
                    $node->setChangeTime(time());
542
                    break;
543
            }
544
        } catch (NotFoundException $e) {
545
            return false;
546
        }
547
548
        clearstatcache(true, $path);
549
550
        return true;
551
    }
552
553
    /**
554
     * Sets file pointer to specified position
555
     *
556
     * @param int $offset
557
     * @param int $whence
558
     *
559
     * @return bool
560
     */
561
    public function stream_seek($offset, $whence = SEEK_SET)
562
    {
563
        switch ($whence) {
564
            case SEEK_SET:
565
                $this->currentlyOpenedFile->position($offset);
566
                break;
567
            case SEEK_CUR:
568
                $this->currentlyOpenedFile->offsetPosition($offset);
569
                break;
570
            case SEEK_END:
571
                $this->currentlyOpenedFile->seekToEnd();
572
                $this->currentlyOpenedFile->offsetPosition($offset);
573
        }
574
575
        return true;
576
    }
577
578
    /**
579
     * Truncates file to given size
580
     *
581
     * @param int $newSize
582
     *
583
     * @return bool
584
     */
585
    public function stream_truncate($newSize)
586
    {
587
        $this->currentlyOpenedFile->truncate($newSize);
588
        clearstatcache();
589
590
        return true;
591
    }
592
593
    /**
594
     * Renames/Moves file
595
     *
596
     * @param string $oldname
597
     * @param string $newname
598
     *
599
     * @return bool
600
     */
601
    public function rename($oldname, $newname)
602
    {
603
        $container = $this->getContainerFromContext($newname);
604
        $oldname = $this->stripScheme($oldname);
605
        $newname = $this->stripScheme($newname);
606
607
        try {
608
            $container->move($oldname, $newname);
609
        } catch (NotFoundException $e) {
610
            trigger_error(
611
                sprintf('mv: rename %s to %s: No such file or directory', $oldname, $newname),
612
                E_USER_WARNING
613
            );
614
615
            return false;
616
        } catch (\RuntimeException $e) {
617
            trigger_error(
618
                sprintf('mv: rename %s to %s: Not a directory', $oldname, $newname),
619
                E_USER_WARNING
620
            );
621
622
            return false;
623
        }
624
625
        return true;
626
    }
627
628
    /**
629
     * Deletes file at given path
630
     *
631
     * @param int $path
632
     *
633
     * @return bool
634
     */
635
    public function unlink($path)
636
    {
637
        $container = $this->getContainerFromContext($path);
638
639
        try {
640
641
            $path = $this->stripScheme($path);
642
643
            $parent = $container->nodeAt(dirname($path));
644
645
            $permissionHelper = $container->getPermissionHelper($parent);
646
            if (!$permissionHelper->isWritable()) {
647
                trigger_error(
648
                    sprintf('rm: %s: Permission denied', $path),
649
                    E_USER_WARNING
650
                );
651
652
                return false;
653
            }
654
655
            $container->remove($path = $this->stripScheme($path));
656
        } catch (NotFoundException $e) {
657
            trigger_error(
658
                sprintf('rm: %s: No such file or directory', $path),
659
                E_USER_WARNING
660
            );
661
662
            return false;
663
        } catch (\RuntimeException $e) {
664
            trigger_error(
665
                sprintf('rm: %s: is a directory', $path),
666
                E_USER_WARNING
667
            );
668
669
            return false;
670
        }
671
672
        return true;
673
    }
674
675
    /**
676
     * Removes directory
677
     *
678
     * @param string $path
679
     *
680
     * @return bool
681
     */
682
    public function rmdir($path)
683
    {
684
        $container = $this->getContainerFromContext($path);
685
        $path = $this->stripScheme($path);
686
687
        try {
688
            $directory = $container->nodeAt($path);
689
690
            if ($directory instanceof File) {
691
                trigger_error(
692
                    sprintf('Warning: rmdir(%s): Not a directory', $path),
693
                    E_USER_WARNING
694
                );
695
696
                return false;
697
            }
698
699
            $permissionHelper = $container->getPermissionHelper($directory);
700
            if (!$permissionHelper->isReadable()) {
701
                trigger_error(
702
                    sprintf('rmdir: %s: Permission denied', $path),
703
                    E_USER_WARNING
704
                );
705
706
                return false;
707
            }
708
709
        } catch (NotFoundException $e) {
710
            trigger_error(
711
                sprintf('Warning: rmdir(%s): No such file or directory', $path),
712
                E_USER_WARNING
713
            );
714
715
            return false;
716
        }
717
718
        if ($directory->size()) {
719
            trigger_error(
720
                sprintf('Warning: rmdir(%s): Directory not empty', $path),
721
                E_USER_WARNING
722
            );
723
724
            return false;
725
        }
726
727
        $container->remove($path, true);
728
729
        return true;
730
    }
731
732
    /**
733
     * Opens directory for iteration
734
     *
735
     * @param string $path
736
     *
737
     * @return bool
738
     */
739
    public function dir_opendir($path)
740
    {
741
        $container = $this->getContainerFromContext($path);
742
743
        $path = $this->stripScheme($path);
744
745
        if (!$container->hasNodeAt($path)) {
746
            trigger_error(sprintf('opendir(%s): failed to open dir: No such file or directory', $path), E_USER_WARNING);
747
748
            return false;
749
        }
750
751
        try {
752
753
            $dir = $container->directoryAt($path);
754
755
        } catch (NotDirectoryException $e) {
756
            trigger_error(sprintf('opendir(%s): failed to open dir: Not a directory', $path), E_USER_WARNING);
757
758
            return false;
759
        }
760
761
        $permissionHelper = $container->getPermissionHelper($dir);
762
763
        if (!$permissionHelper->isReadable()) {
764
            trigger_error(sprintf('opendir(%s): failed to open dir: Permission denied', $path), E_USER_WARNING);
765
766
            return false;
767
        }
768
769
        $this->currentlyOpenedDir = new DirectoryHandler();
770
        $this->currentlyOpenedDir->setDirectory($dir);
771
772
        return true;
773
    }
774
775
    /**
776
     * Closes opened dir
777
     *
778
     * @return bool
779
     */
780
    public function dir_closedir()
781
    {
782
        if ($this->currentlyOpenedDir) {
783
            $this->currentlyOpenedDir = null;
784
785
            return true;
786
        }
787
788
        return false;
789
    }
790
791
    /**
792
     * Returns next file url in directory
793
     *
794
     * @return null
795
     */
796
    public function dir_readdir()
797
    {
798
        $node = $this->currentlyOpenedDir->iterator()->current();
799
        if (!$node) {
800
            return false;
801
        }
802
        $this->currentlyOpenedDir->iterator()->next();
803
804
        return $node->basename();
805
    }
806
807
    /**
808
     * Resets directory iterator
809
     */
810
    public function dir_rewinddir()
811
    {
812
        $this->currentlyOpenedDir->iterator()->rewind();
813
    }
814
815
    public function stream_lock($operation)
816
    {
817
        return $this->currentlyOpenedFile->lock($this, $operation);
818
    }
819
}
820