1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Imanghafoori\LaravelMicroscope; |
4
|
|
|
|
5
|
|
|
use Illuminate\Support\Composer; |
6
|
|
|
use Illuminate\Support\Str; |
7
|
|
|
use Imanghafoori\LaravelMicroscope\Analyzers\ComposerJson; |
8
|
|
|
use Imanghafoori\LaravelMicroscope\Analyzers\FileManipulator; |
9
|
|
|
use Imanghafoori\LaravelMicroscope\Analyzers\GetClassProperties; |
10
|
|
|
use Imanghafoori\LaravelMicroscope\Analyzers\NamespaceCorrector; |
11
|
|
|
use Imanghafoori\LaravelMicroscope\Analyzers\ParseUseStatement; |
12
|
|
|
use Imanghafoori\LaravelMicroscope\ErrorReporters\ErrorPrinter; |
13
|
|
|
|
14
|
|
|
class CheckClassReferencesAreValid |
15
|
|
|
{ |
16
|
|
|
public static function check($tokens, $absFilePath) |
17
|
|
|
{ |
18
|
|
|
try { |
19
|
|
|
self::checkReferences($tokens, $absFilePath); |
20
|
|
|
} catch (\ErrorException $e) { |
|
|
|
|
21
|
|
|
// In case a file is moved or deleted, |
22
|
|
|
// composer will need a dump autoload. |
23
|
|
|
if (! Str::endsWith($e->getFile(), 'vendor\composer\ClassLoader.php')) { |
24
|
|
|
throw $e; |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
self::warnDumping($e->getMessage()); |
28
|
|
|
resolve(Composer::class)->dumpAutoloads(); |
29
|
|
|
} |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
public static function warnDumping($msg) |
33
|
|
|
{ |
34
|
|
|
$p = resolve(ErrorPrinter::class)->printer; |
35
|
|
|
$p->writeln('It seems composer has some trouble with autoload...'); |
36
|
|
|
$p->writeln($msg); |
37
|
|
|
$p->writeln('Running "composer dump-autoload" command... \(*_*)\ '); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
private static function checkReferences($tokens, $absFilePath) |
41
|
|
|
{ |
42
|
|
|
// If file is empty or does not begin with <?php |
43
|
|
|
if (($tokens[0][0] ?? null) !== T_OPEN_TAG) { |
44
|
|
|
return; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
[ |
48
|
|
|
$currentNamespace, |
|
|
|
|
49
|
|
|
$class, |
|
|
|
|
50
|
|
|
$type, |
|
|
|
|
51
|
|
|
$parent, |
|
|
|
|
52
|
|
|
$interfaces, |
|
|
|
|
53
|
|
|
] = GetClassProperties::readClassDefinition($tokens); |
54
|
|
|
|
55
|
|
|
// It means that, there is no class/trait definition found in the file. |
56
|
|
|
if (! $class) { |
57
|
|
|
return; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
event('laravel_microscope.checking_file', [$absFilePath]); |
61
|
|
|
// @todo better to do it an event listener. |
62
|
|
|
|
63
|
|
|
self::checkAtSignStrings($tokens, $absFilePath); |
64
|
|
|
|
65
|
|
|
self::checkNotImportedClasses($tokens, $absFilePath); |
66
|
|
|
|
67
|
|
|
self::checkImportedClasses($currentNamespace, $class, $absFilePath); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
private static function checkImportedClassesExist($imports, $absFilePath) |
71
|
|
|
{ |
72
|
|
|
$printer = app(ErrorPrinter::class); |
73
|
|
|
|
74
|
|
|
foreach ($imports as $i => $import) { |
75
|
|
|
[$class, $line] = $import; |
|
|
|
|
76
|
|
|
|
77
|
|
|
if (! self::isAbsent($class)) { |
78
|
|
|
continue; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
if (\is_dir(base_path(NamespaceCorrector::getRelativePathFromNamespace($class)))) { |
82
|
|
|
continue; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
$isInUserSpace = Str::startsWith($class, array_keys(ComposerJson::readAutoload())); |
86
|
|
|
|
87
|
|
|
[$isCorrected, $corrects] = FileManipulator::fixReference($absFilePath, $class, $line, '', true); |
|
|
|
|
88
|
|
|
|
89
|
|
|
if ($isInUserSpace && $isCorrected) { |
90
|
|
|
$printer->printFixation($absFilePath, $class, $line, $corrects); |
91
|
|
|
} else { |
92
|
|
|
$printer->wrongImport($absFilePath, $class, $line); |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
public static function isAbsent($class) |
98
|
|
|
{ |
99
|
|
|
return ! \class_exists($class) && ! \interface_exists($class) && ! \trait_exists($class); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
protected static function fullNamespace($currentNamespace, $class) |
103
|
|
|
{ |
104
|
|
|
return $currentNamespace ? $currentNamespace.'\\'.$class : $class; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
public static function checkAtSignStrings($tokens, $absFilePath, $onlyAbsClassPath = false) |
108
|
|
|
{ |
109
|
|
|
$printer = app(ErrorPrinter::class); |
110
|
|
|
|
111
|
|
|
foreach ($tokens as $token) { |
112
|
|
|
// If it is a string containing a single '@' |
113
|
|
|
if ($token[0] != T_CONSTANT_ENCAPSED_STRING || \substr_count($token[1], '@') != 1) { |
114
|
|
|
continue; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
$trimmed = \trim($token[1], '\'\"'); |
118
|
|
|
|
119
|
|
|
if ($onlyAbsClassPath && $trimmed[0] !== '\\') { |
120
|
|
|
continue; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
[$class, $method] = \explode('@', $trimmed); |
|
|
|
|
124
|
|
|
|
125
|
|
|
if (\substr_count($class, '\\') <= 0) { |
126
|
|
|
continue; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
if (Str::contains($trimmed, ['-', '/', '[', '*', '+', '.', '(', '$', '^'])) { |
130
|
|
|
continue; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
if (! \class_exists($class)) { |
134
|
|
|
$isInUserSpace = Str::startsWith($class, \array_keys(ComposerJson::readAutoload())); |
135
|
|
|
$result = FileManipulator::fixReference($absFilePath, $class, $token[2]); |
136
|
|
|
if ($isInUserSpace && $result[0]) { |
137
|
|
|
$printer->printFixation($absFilePath, $class, $token[2], $result[1]); |
138
|
|
|
} else { |
139
|
|
|
$printer->wrongUsedClassError($absFilePath, $token[1], $token[2]); |
140
|
|
|
} |
141
|
|
|
} elseif (! \method_exists($class, $method)) { |
142
|
|
|
$printer->wrongMethodError($absFilePath, $trimmed, $token[2]); |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
private static function checkImportedClasses($currentNamespace, $class, $absPath) |
148
|
|
|
{ |
149
|
|
|
$namespacedClassName = self::fullNamespace($currentNamespace, $class); |
150
|
|
|
|
151
|
|
|
$imports = ParseUseStatement::getUseStatementsByPath($namespacedClassName, $absPath); |
152
|
|
|
|
153
|
|
|
self::checkImportedClassesExist($imports, $absPath); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
private static function fix($absFilePath, $class, $line, $namespace) |
157
|
|
|
{ |
158
|
|
|
$baseClassName = \str_replace($namespace.'\\', '', $class); |
159
|
|
|
|
160
|
|
|
$result = FileManipulator::fixReference($absFilePath, $baseClassName, $line, '\\'); |
161
|
|
|
|
162
|
|
|
if ($result[0]) { |
163
|
|
|
return $result; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
return $result = FileManipulator::fixReference($absFilePath, $class, $line); |
|
|
|
|
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
private static function checkNotImportedClasses($tokens, $absFilePath) |
170
|
|
|
{ |
171
|
|
|
[$nonImportedClasses, $namespace] = ParseUseStatement::findClassReferences($tokens, $absFilePath); |
|
|
|
|
172
|
|
|
|
173
|
|
|
$printer = app(ErrorPrinter::class); |
174
|
|
|
|
175
|
|
|
loopStart: |
176
|
|
|
foreach ($nonImportedClasses as $nonImportedClass) { |
177
|
|
|
$class = $nonImportedClass['class']; |
178
|
|
|
$line = $nonImportedClass['line']; |
179
|
|
|
|
180
|
|
|
if (! self::isAbsent($class) || \function_exists($class)) { |
181
|
|
|
continue; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
if (! ComposerJson::isInAppSpace($class)) { |
185
|
|
|
$printer->doesNotExist($class, $absFilePath, $line, 'wrongReference', 'Wrong Reference:'); |
186
|
|
|
continue; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
[$isFixed, $corrections] = self::fix($absFilePath, $class, $line, $namespace); |
|
|
|
|
190
|
|
|
|
191
|
|
|
$method = $isFixed ? 'printFixation' : 'wrongImportPossibleFixes'; |
192
|
|
|
$printer->$method($absFilePath, $class, $line, $corrections); |
193
|
|
|
if ($isFixed) { |
194
|
|
|
$tokens = token_get_all(file_get_contents($absFilePath)); |
195
|
|
|
([$nonImportedClasses, $namespace] = ParseUseStatement::findClassReferences($tokens, $absFilePath)); |
196
|
|
|
goto loopStart; |
197
|
|
|
} |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
|
Scrutinizer analyzes your
composer.json
/composer.lock
file if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.