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.
Completed
Pull Request — master (#22)
by joseph
10:55 queued 08:32
created

Psr4Validator::getActualNamespace()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\PHPQA;
4
5
class Psr4Validator
6
{
7
8
    /**
9
     * @var string
10
     */
11
    protected $pathToProjectRoot;
12
    /**
13
     * @var array
14
     */
15
    protected $decodedComposerJson;
16
    private $parseErrors  = [];
17
    private $psr4Errors   = [];
18
    private $ignoreRegexPatterns;
19
    private $ignoredFiles = [];
20
    private $missingPaths = [];
21
22
    /**
23
     * Psr4Validator constructor.
24
     *
25
     * @param array  $ignoreRegexPatterns Set of regex patterns used to exclude files or directories
26
     * @param string $pathToProjectRoot
27
     * @param array  $decodedComposerJson
28
     */
29 3
    public function __construct(array $ignoreRegexPatterns, string $pathToProjectRoot, array $decodedComposerJson)
30
    {
31 3
        $this->ignoreRegexPatterns = $ignoreRegexPatterns;
32 3
        $this->pathToProjectRoot   = $pathToProjectRoot;
33 3
        $this->decodedComposerJson = $decodedComposerJson;
34 3
    }
35
36
    /**
37
     * @throws \Exception
38
     */
39 3
    public function main(): array
40
    {
41 3
        $this->loop();
42 3
        $errors = [];
43
        //Actual Errors
44 3
        if (!empty($this->psr4Errors)) {
45 1
            $errors['PSR-4 Errors:'] = $this->psr4Errors;
46
        }
47 3
        if (!empty($this->parseErrors)) {
48 1
            $errors['Parse Errors:'] = $this->parseErrors;
49
        }
50 3
        if (!empty($this->missingPaths)) {
51 1
            $errors['Missing Paths:'] = $this->missingPaths;
52
        }
53 3
        if ([] === $errors) {
54 2
            return $errors;
55
        }
56
        //Debug Info
57 1
        if (!empty($this->ignoredFiles)) {
58 1
            $errors['Ignored Files:'] = $this->ignoredFiles;
59
        }
60
61 1
        return $errors;
62
    }
63
64
    /**
65
     * @throws \Exception
66
     */
67 3
    private function loop()
68
    {
69 3
        foreach ($this->yieldPhpFilesToCheck() as list($absPathRoot, $namespaceRoot, $fileInfo)) {
70 3
            $this->check($absPathRoot, $namespaceRoot, $fileInfo);
71
        }
72 3
    }
73
74 3
    private function check(string $absPathRoot, string $namespaceRoot, \SplFileInfo $fileInfo)
75
    {
76 3
        $actualNamespace = $this->getActualNamespace($fileInfo);
77 3
        if ('' === $actualNamespace) {
78 1
            return;
79
        }
80 3
        $expectedNamespace = $this->expectedFileNamespace($absPathRoot, $namespaceRoot, $fileInfo);
81 3
        if ($actualNamespace !== $expectedNamespace) {
82 1
            $this->psr4Errors[$namespaceRoot][] =
83
                [
84 1
                    'fileInfo'          => $fileInfo->getRealPath(),
85 1
                    'expectedNamespace' => $expectedNamespace,
86 1
                    'actualNamespace'   => $actualNamespace,
87
                ];
88
        }
89 3
    }
90
91 3
    private function expectedFileNamespace(string $absPathRoot, string $namespaceRoot, \SplFileInfo $fileInfo): string
92
    {
93 3
        $relativePath = \substr($fileInfo->getPathname(), \strlen($absPathRoot));
94 3
        $relativeDir  = \dirname($relativePath);
95 3
        $relativeNs   = '';
96 3
        if ($relativeDir !== '.') {
97 3
            $relativeNs = \str_replace(
98 3
                '/',
99 3
                '\\',
100 3
                \ltrim($relativeDir, '/')
101
            );
102
        }
103
104 3
        return rtrim($namespaceRoot.$relativeNs, '\\');
105
    }
106
107 3
    private function getActualNamespace(\SplFileInfo $fileInfo): string
108
    {
109 3
        $contents = \file_get_contents($fileInfo->getPathname());
110 3
        \preg_match('%namespace\s+?([^;]+)%', $contents, $matches);
111 3
        if (empty($matches) || !isset($matches[1])) {
112 1
            $this->parseErrors[] = $fileInfo->getRealPath();
113
114 1
            return '';
115
        }
116
117 3
        return $matches[1];
118
    }
119
120
    /**
121
     * @param string $realPath
122
     *
123
     * @return \SplHeap|\SplFileInfo[]
124
     */
125 3
    private function getDirectoryIterator(string $realPath)
126
    {
127 3
        $directoryIterator = new \RecursiveDirectoryIterator(
128 3
            $realPath,
129 3
            \RecursiveDirectoryIterator::SKIP_DOTS
130
        );
131 3
        $iterator          = new \RecursiveIteratorIterator(
132 3
            $directoryIterator,
133 3
            \RecursiveIteratorIterator::SELF_FIRST
134
        );
135
136
        return new class($iterator) extends \SplHeap
137
        {
138 3
            public function __construct(\RecursiveIteratorIterator $iterator)
139
            {
140 3
                foreach ($iterator as $item) {
141 3
                    $this->insert($item);
142
                }
143 3
            }
144
145 3
            public function compare($item1, $item2)
146
            {
147 3
                return strcmp($item2->getRealpath(), $item1->getRealpath());
148
            }
149
        };
150
    }
151
152 1
    private function addMissingPathError(string $path, string $namespaceRoot, string $absPathRoot)
153
    {
154 1
        $invalidPathMessage = "Namespace root '$namespaceRoot'".
155 1
                              "\ncontains a path '$path''".
156 1
                              "\nwhich doesn't exist\n";
157 1
        if (stripos($absPathRoot, "Magento") !== false) {
158
            $invalidPathMessage .= 'Magento\'s composer includes this by default, '
159 1
                                   .'it should be removed from the psr-4 section';
160
        }
161 1
        $this->missingPaths[$path] = $invalidPathMessage;
162 1
    }
163
164
    /**
165
     * @return \Generator
166
     * @throws \Exception
167
     * @SuppressWarnings(PHPMD.StaticAccess)
168
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
169
     */
170 3
    private function yieldPhpFilesToCheck(): \Generator
171
    {
172 3
        $json = $this->decodedComposerJson;
173 3
        foreach (['autoload', 'autoload-dev'] as $autoload) {
174 3
            if (!isset($json[$autoload]['psr-4'])) {
175 1
                continue;
176
            }
177 3
            $psr4 = $json[$autoload]['psr-4'];
178 3
            foreach ($psr4 as $namespaceRoot => $paths) {
179 3
                if (!\is_array($paths)) {
180 1
                    $paths = [$paths];
181
                }
182 3
                foreach ($paths as $path) {
183 3
                    $absPathRoot     = $this->pathToProjectRoot.'/'.$path;
184 3
                    $realAbsPathRoot = \realpath($absPathRoot);
185 3
                    if (false === $realAbsPathRoot) {
186 1
                        $this->addMissingPathError($path, $namespaceRoot, $absPathRoot);
187 1
                        continue;
188
                    }
189 3
                    $iterator = $this->getDirectoryIterator($absPathRoot);
190 3
                    foreach ($iterator as $fileInfo) {
191 3
                        if ('php' !== $fileInfo->getExtension()) {
192 3
                            continue;
193
                        }
194 3
                        foreach ($this->ignoreRegexPatterns as $pattern) {
195 1
                            if (\preg_match($pattern, $fileInfo->getRealPath())) {
196 1
                                $this->ignoredFiles[] = $fileInfo->getRealPath();
197 1
                                continue 2;
198
                            }
199
                        }
200
                        yield [
201 3
                            $absPathRoot,
202 3
                            $namespaceRoot,
203 3
                            $fileInfo,
204
                        ];
205
                    }
206
                }
207
            }
208
        }
209 3
    }
210
}
211