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.

GlobIterator::count()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Gielfeldt\Iterators;
4
5
/**
6
 * Glob iterator with double wildcard (**) and recursive capabilities.
7
 */
8
class GlobIterator extends TraversableIterator implements \Countable
9
{
10
    const GLOB_NOSORT = 2048;
11
    const GLOB_ONLYDIR = 16384;
12
13
    protected $flags;
14
    protected $path;
15 2
16
    public function getPath()
17 2
    {
18
        return $this->path;
19
    }
20
21
    /**
22
     * Constructor.
23
     *
24
     * @param string  $globPattern
25
     *   Glob pattern.
26
     * @param integer $flags
27
     *   FilesystemIterator flags.
28 6
     */
29
    public function __construct(string $globPattern, int $flags = self::GLOB_NOSORT | \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO)
30 6
    {
31 6
        $this->flags = $flags;
32 6
        list($path, $maxDepth) = self::extractPathAndMaxDepth($globPattern);
33
        $regexPattern = self::globToRegex($globPattern);
34 6
35
        $this->path = $path;
36 6
37 6
        $realPath = $path ? $path : './';
38
        $realPath = rtrim($realPath, '/') . '/';
39 6
40 6
        $flags = $flags & ~ \FilesystemIterator::CURRENT_AS_PATHNAME;
41 6
        $flags |= \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS;
42
        $iterator = new \RecursiveDirectoryIterator($realPath, $flags);
43
44 6
        // Sort if necessary.
45
        $sIterator = $this->flags & self::GLOB_NOSORT ? $iterator : new RecursiveSortIterator(
46 1
            $iterator,
47 1
            RecursiveSortIterator::SORT_ASC,
48 6
            0,
49
            [$this, 'sortSplFileInfo']
50
        );
51
52 6
        // Only traverse the depth needed.
53 6
        $rIterator = new \RecursiveIteratorIterator($sIterator, \RecursiveIteratorIterator::SELF_FIRST);
54
        $rIterator->setMaxDepth($maxDepth);
55
56 6
        // Setup file info handler.
57 6
        $iteratorId = spl_object_hash($this);
58
        GlobIteratorFileInfo::setPath($iteratorId, $path, $realPath);
59
60 6
        // Actual glob filtering.
61
        $fIterator = new \CallbackFilterIterator(
62 6
            $rIterator,
63 5
            function (&$current, &$key) use ($iteratorId, $regexPattern) {
64 5
                if (($this->flags & self::GLOB_ONLYDIR) && !$current->isDir()) {
65 5
                    return false;
66 1
                }
67
                GlobIteratorFileInfo::setIteratorId($iteratorId);
68 4
                $fileInfo = $current->getFileInfo(GlobIteratorFileInfo::class);
69
                if ($this->flags & \FilesystemIterator::CURRENT_AS_PATHNAME) {
70
                    $current = $fileInfo->getPathname();
71 5
                } else {
72 1
                    $current = $fileInfo;
73
                }
74 4
75
                if ($this->flags & \FilesystemIterator::KEY_AS_FILENAME) {
76 5
                    $key = $fileInfo->getFilename();
77 6
                } else {
78
                    $key = $fileInfo->getPathname();
79 6
                }
80 6
                return preg_match($regexPattern, $fileInfo->getPathname());
81
            }
82 3
        );
83
        parent::__construct(new CountableIterator($fIterator, CountableIterator::CACHE_COUNT));
84 3
    }
85
86
    public function count()
87 1
    {
88
        return $this->getInnerIterator()->count();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Iterator as the method count() does only exist in the following implementations of said interface: ArrayIterator, CachingIterator, Gielfeldt\Iterators\CachingIterator, Gielfeldt\Iterators\CountableIterator, Gielfeldt\Iterators\CsvFileObject, Gielfeldt\Iterators\GlobIterator, Gielfeldt\Iterators\IndexIterator, Gielfeldt\Iterators\OrderedInterleaveIterator, Gielfeldt\Iterators\RecursiveSortIterator, Gielfeldt\Iterators\RepeatIterator, Gielfeldt\Iterators\ShuffleIterator, Gielfeldt\Iterators\SortIterator, GlobIterator, HttpMessage, HttpRequestPool, Issue523, MongoCursor, MongoGridFSCursor, PHP_CodeSniffer\Files\FileList, PHP_Token_Stream, Phar, PharData, Predis\Response\Iterator\MultiBulk, Predis\Response\Iterator\MultiBulkIterator, Predis\Response\Iterator\MultiBulkTuple, RecursiveArrayIterator, RecursiveCachingIterator, SQLiteResult, SimpleXMLIterator, SplDoublyLinkedList, SplFixedArray, SplHeap, SplMaxHeap, SplMinHeap, SplObjectStorage, SplPriorityQueue, SplQueue, SplStack, TheSeer\Tokenizer\TokenCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
89 1
    }
90
91
    public function sortSplFileInfo($cmpA, $cmpB)
92
    {
93
        return $cmpA->current->getPathname() <=> $cmpB->current->getPathname();
94
    }
95
96
    /**
97
     * Extract the path to start at.
98 6
     *
99
     * @param  string $globPattern
100 6
     * @return string
101 6
     */
102
    public static function extractPathAndMaxDepth(string $globPattern): array
103 6
    {
104 6
        if (strpos($globPattern, '*')) {
105 6
            list($path,) = explode('*', $globPattern, 2);
106 6
        }
107
        $path = isset($path) ? dirname("$path.") . '/' : '';
108
        $subPattern = substr($globPattern, strlen($path));
109
        $maxDepth = strpos($globPattern, '**') !== false ? -1 : substr_count($subPattern, '/');
110
        return [$path, $maxDepth];
111
    }
112
113
    /**
114
     * Convert a glob pattern to a regex pattern.
115 6
     *
116
     * @param  string $globPattern
117 6
     * @return string
118
     */
119 6
    public static function globToRegex(string $globPattern): string
120
    {
121
        $modifiers = '';
122
        $transforms = array(
123
            '\*'    => '[^/]*',
124
            '\*\*'    => '.*',
125
            '\?'    => '[^/]',
126
            '\[\!'    => '[^',
127
            '\['    => '[',
128 6
            '\]'    => ']',
129 6
            '\.'    => '\.',
130 6
        );
131
        $regexPattern = '#^'
132 6
            . strtr(preg_quote($globPattern, '#'), $transforms)
133
            . '$#'
134
            . $modifiers;
135
136
        return $regexPattern;
137
    }
138 1
139
    /**
140 1
     * Use the RecursiveGlobIterator for glob://
141 1
     */
142 1
    public static function registerStreamWrapper($root = null)
143 1
    {
144
        stream_wrapper_unregister('glob');
145
        stream_wrapper_register('glob', GlobStreamWrapper::class);
146
        GlobStreamWrapper::setRoot($root);
147
    }
148 1
149
    /**
150 1
     * Restore the default glob:// stream wrapper.
151 1
     */
152 1
    public static function unRegisterStreamWrapper()
153 1
    {
154
        stream_wrapper_unregister('glob');
155
        stream_wrapper_restore('glob');
156
        GlobStreamWrapper::setRoot(null);
157
    }
158
}
159