2121use function array_filter ;
2222use function array_key_exists ;
2323use function array_keys ;
24- use function array_merge ;
2524use function explode ;
2625use function file_get_contents ;
2726use function get_declared_classes ;
@@ -81,19 +80,12 @@ class Analyser
8180 private $ classmap = [];
8281
8382 /**
84- * package name => is dev dependency
83+ * package or ext-* => is dev dependency
8584 *
8685 * @var array<string, bool>
8786 */
8887 private $ composerJsonDependencies ;
8988
90- /**
91- * ext-* => is dev dependency
92- *
93- * @var array<string, bool>
94- */
95- private $ composerJsonExtensions ;
96-
9789 /**
9890 * symbol name => true
9991 *
@@ -117,21 +109,18 @@ class Analyser
117109
118110 /**
119111 * @param array<string, ClassLoader> $classLoaders vendorDir => ClassLoader (e.g. result of \Composer\Autoload\ClassLoader::getRegisteredLoaders())
120- * @param array<string, bool> $composerJsonDependencies package name => is dev dependency
121- * @param array<string, bool> $composerJsonExtensions ext-* => is dev dependency
112+ * @param array<string, bool> $composerJsonDependencies package or ext-* => is dev dependency
122113 */
123114 public function __construct (
124115 Stopwatch $ stopwatch ,
125116 array $ classLoaders ,
126117 Configuration $ config ,
127- array $ composerJsonDependencies ,
128- array $ composerJsonExtensions
118+ array $ composerJsonDependencies
129119 )
130120 {
131121 $ this ->stopwatch = $ stopwatch ;
132122 $ this ->config = $ config ;
133123 $ this ->composerJsonDependencies = $ composerJsonDependencies ;
134- $ this ->composerJsonExtensions = $ composerJsonExtensions ;
135124
136125 $ this ->initExistingSymbols ();
137126
@@ -150,14 +139,12 @@ public function run(): AnalysisResult
150139 $ scannedFilesCount = 0 ;
151140 $ unknownClassErrors = [];
152141 $ unknownFunctionErrors = [];
153- $ missingExtensions = [];
154142 $ shadowErrors = [];
155143 $ devInProdErrors = [];
156144 $ prodOnlyInDevErrors = [];
157145 $ unusedErrors = [];
158146
159- $ usedPackages = [];
160- $ usedExtensions = [];
147+ $ usedDependencies = [];
161148 $ prodPackagesUsedInProdPath = [];
162149
163150 $ usages = [];
@@ -176,72 +163,64 @@ public function run(): AnalysisResult
176163 }
177164
178165 if (isset ($ this ->extensionSymbols [$ kind ][$ usedSymbol ])) {
179- $ neededExtension = $ this ->extensionSymbols [$ kind ][$ usedSymbol ];
180- $ usedExtensions [$ neededExtension ] = true ;
181-
182- if (!isset ($ this ->composerJsonExtensions [$ neededExtension ])) {
183- foreach ($ lineNumbers as $ lineNumber ) {
184- $ missingExtensions [$ neededExtension ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
185- }
186- }
187-
188- continue ;
189- }
166+ $ dependencyName = $ this ->extensionSymbols [$ kind ][$ usedSymbol ];
190167
191- $ symbolPath = $ this ->getSymbolPath ($ usedSymbol , $ kind );
168+ } else {
169+ $ symbolPath = $ this ->getSymbolPath ($ usedSymbol , $ kind );
192170
193- if ($ symbolPath === null ) {
194- if ($ kind === SymbolKind::CLASSLIKE && !$ ignoreList ->shouldIgnoreUnknownClass ($ usedSymbol , $ filePath )) {
195- foreach ($ lineNumbers as $ lineNumber ) {
196- $ unknownClassErrors [$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
171+ if ($ symbolPath === null ) {
172+ if ($ kind === SymbolKind::CLASSLIKE && !$ ignoreList ->shouldIgnoreUnknownClass ($ usedSymbol , $ filePath )) {
173+ foreach ($ lineNumbers as $ lineNumber ) {
174+ $ unknownClassErrors [$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
175+ }
197176 }
198- }
199177
200- if ($ kind === SymbolKind::FUNCTION && !$ ignoreList ->shouldIgnoreUnknownFunction ($ usedSymbol , $ filePath )) {
201- foreach ($ lineNumbers as $ lineNumber ) {
202- $ unknownFunctionErrors [$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
178+ if ($ kind === SymbolKind::FUNCTION && !$ ignoreList ->shouldIgnoreUnknownFunction ($ usedSymbol , $ filePath )) {
179+ foreach ($ lineNumbers as $ lineNumber ) {
180+ $ unknownFunctionErrors [$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
181+ }
203182 }
183+
184+ continue ;
204185 }
205186
206- continue ;
207- }
187+ if (!$ this ->isVendorPath ($ symbolPath )) {
188+ continue ; // local class
189+ }
208190
209- if (!$ this ->isVendorPath ($ symbolPath )) {
210- continue ; // local class
191+ $ dependencyName = $ this ->getPackageNameFromVendorPath ($ symbolPath );
211192 }
212193
213- $ packageName = $ this ->getPackageNameFromVendorPath ($ symbolPath );
214-
215194 if (
216- $ this ->isShadowDependency ($ packageName )
217- && !$ ignoreList ->shouldIgnoreError (ErrorType::SHADOW_DEPENDENCY , $ filePath , $ packageName )
195+ $ this ->isShadowDependency ($ dependencyName )
196+ && !$ ignoreList ->shouldIgnoreError (ErrorType::SHADOW_DEPENDENCY , $ filePath , $ dependencyName )
218197 ) {
219198 foreach ($ lineNumbers as $ lineNumber ) {
220- $ shadowErrors [$ packageName ][$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
199+ $ shadowErrors [$ dependencyName ][$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
221200 }
222201 }
223202
224203 if (
225204 !$ isDevFilePath
226- && $ this ->isDevDependency ($ packageName )
227- && !$ ignoreList ->shouldIgnoreError (ErrorType::DEV_DEPENDENCY_IN_PROD , $ filePath , $ packageName )
205+ && $ this ->isDevDependency ($ dependencyName )
206+ && !$ ignoreList ->shouldIgnoreError (ErrorType::DEV_DEPENDENCY_IN_PROD , $ filePath , $ dependencyName )
228207 ) {
229208 foreach ($ lineNumbers as $ lineNumber ) {
230- $ devInProdErrors [$ packageName ][$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
209+ $ devInProdErrors [$ dependencyName ][$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
231210 }
232211 }
233212
234213 if (
235214 !$ isDevFilePath
236- && !$ this ->isDevDependency ($ packageName )
215+ && !$ this ->isDevDependency ($ dependencyName )
237216 ) {
238- $ prodPackagesUsedInProdPath [$ packageName ] = true ;
217+ $ prodPackagesUsedInProdPath [$ dependencyName ] = true ;
239218 }
240219
241- $ usedPackages [ $ packageName ] = true ;
220+ $ usedDependencies [ $ dependencyName ] = true ;
242221
243222 foreach ($ lineNumbers as $ lineNumber ) {
244- $ usages [$ packageName ][$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
223+ $ usages [$ dependencyName ][$ usedSymbol ][] = new SymbolUsage ($ filePath , $ lineNumber , $ kind );
245224 }
246225 }
247226 }
@@ -254,36 +233,40 @@ public function run(): AnalysisResult
254233 continue ;
255234 }
256235
257- $ symbolPath = $ this ->getSymbolPath ($ forceUsedSymbol , null );
236+ if (
237+ isset ($ this ->extensionSymbols [SymbolKind::FUNCTION ][$ forceUsedSymbol ])
238+ || isset ($ this ->extensionSymbols [SymbolKind::CONSTANT ][$ forceUsedSymbol ])
239+ || isset ($ this ->extensionSymbols [SymbolKind::CLASSLIKE ][$ forceUsedSymbol ])
240+ ) {
241+ $ forceUsedDependency = $ this ->extensionSymbols [SymbolKind::FUNCTION ][$ forceUsedSymbol ]
242+ ?? $ this ->extensionSymbols [SymbolKind::CONSTANT ][$ forceUsedSymbol ]
243+ ?? $ this ->extensionSymbols [SymbolKind::CLASSLIKE ][$ forceUsedSymbol ];
244+ } else {
245+ $ symbolPath = $ this ->getSymbolPath ($ forceUsedSymbol , null );
246+
247+ if ($ symbolPath === null || !$ this ->isVendorPath ($ symbolPath )) {
248+ continue ;
249+ }
258250
259- if ($ symbolPath === null || !$ this ->isVendorPath ($ symbolPath )) {
260- continue ;
251+ $ forceUsedDependency = $ this ->getPackageNameFromVendorPath ($ symbolPath );
261252 }
262253
263- $ forceUsedPackage = $ this ->getPackageNameFromVendorPath ($ symbolPath );
264- $ usedPackages [$ forceUsedPackage ] = true ;
265- $ forceUsedPackages [$ forceUsedPackage ] = true ;
254+ $ usedDependencies [$ forceUsedDependency ] = true ;
255+ $ forceUsedPackages [$ forceUsedDependency ] = true ;
266256 }
267257
268258 if ($ this ->config ->shouldReportUnusedDevDependencies ()) {
269- $ dependenciesForUnusedAnalysis = array_merge (
270- array_keys ($ this ->composerJsonDependencies ),
271- array_keys ($ this ->composerJsonExtensions )
272- );
259+ $ dependenciesForUnusedAnalysis = array_keys ($ this ->composerJsonDependencies );
273260
274261 } else {
275- $ dependenciesForUnusedAnalysis = array_merge (
276- array_keys (array_filter ($ this ->composerJsonDependencies , static function (bool $ devDependency ) {
277- return !$ devDependency ; // dev deps are typically used only in CI
278- })),
279- array_keys ($ this ->composerJsonExtensions )
280- );
262+ $ dependenciesForUnusedAnalysis = array_keys (array_filter ($ this ->composerJsonDependencies , static function (bool $ devDependency ) {
263+ return !$ devDependency ; // dev deps are typically used only in CI
264+ }));
281265 }
282266
283267 $ unusedDependencies = array_diff (
284268 $ dependenciesForUnusedAnalysis ,
285- array_keys ($ usedPackages ),
286- array_keys ($ usedExtensions ),
269+ array_keys ($ usedDependencies ),
287270 self ::CORE_EXTENSIONS
288271 );
289272
@@ -300,7 +283,8 @@ public function run(): AnalysisResult
300283 $ prodDependencies ,
301284 array_keys ($ prodPackagesUsedInProdPath ),
302285 array_keys ($ forceUsedPackages ), // we dont know where are those used, lets not report them
303- $ unusedDependencies
286+ $ unusedDependencies ,
287+ self ::CORE_EXTENSIONS
304288 );
305289
306290 foreach ($ prodPackagesUsedOnlyInDev as $ prodPackageUsedOnlyInDev ) {
@@ -313,7 +297,6 @@ public function run(): AnalysisResult
313297 $ scannedFilesCount ,
314298 $ this ->stopwatch ->stop (),
315299 $ usages ,
316- $ missingExtensions ,
317300 $ unknownClassErrors ,
318301 $ unknownFunctionErrors ,
319302 $ shadowErrors ,
0 commit comments