Skip to content

Commit f896750

Browse files
authored
Consult bootstrap-registered custom autoloaders only after the static source locators (#6069)
1 parent 8e2efc0 commit f896750

23 files changed

Lines changed: 365 additions & 38 deletions

.github/workflows/e2e-tests.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ jobs:
132132
cd e2e/bug-14988
133133
composer install
134134
../../bin/phpstan analyse
135+
- script: |
136+
cd e2e/bug-12972b
137+
composer install
138+
../../bin/phpstan analyze
139+
- script: |
140+
cd e2e/bug-12972c
141+
composer install
142+
../../bin/phpstan analyze
135143
- script: |
136144
cd e2e/bug-14724
137145
composer install

bin/phpstan

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -91,31 +91,15 @@ use Symfony\Component\Console\Helper\ProgressBar;
9191
$autoloadFunctionsAfter = spl_autoload_functions();
9292

9393
if ($autoloadFunctionsBefore !== false && $autoloadFunctionsAfter !== false) {
94-
$newAutoloadFunctions = [];
95-
foreach ($autoloadFunctionsAfter as $after) {
96-
if (
97-
is_array($after)
98-
&& count($after) > 0
99-
) {
100-
if (is_object($after[0])
101-
&& get_class($after[0]) === \Composer\Autoload\ClassLoader::class
102-
) {
103-
continue;
104-
}
105-
if ($after[0] === 'PHPStan\\PharAutoloader') {
106-
continue;
107-
}
108-
}
109-
foreach ($autoloadFunctionsBefore as $before) {
110-
if ($after === $before) {
111-
continue 2;
112-
}
113-
}
114-
115-
$newAutoloadFunctions[] = $after;
116-
}
117-
118-
$GLOBALS['__phpstanAutoloadFunctions'] = $newAutoloadFunctions;
94+
$collectedAutoloadFunctions = \PHPStan\collectNewAutoloadFunctions($autoloadFunctionsBefore, $autoloadFunctionsAfter);
95+
$GLOBALS['__phpstanAutoloadFunctions'] = array_merge(
96+
$GLOBALS['__phpstanAutoloadFunctions'] ?? [],
97+
$collectedAutoloadFunctions['appended'],
98+
);
99+
$GLOBALS['__phpstanAutoloadFunctionsPrependedToComposer'] = array_merge(
100+
$GLOBALS['__phpstanAutoloadFunctionsPrependedToComposer'] ?? [],
101+
$collectedAutoloadFunctions['prepended'],
102+
);
119103
}
120104

121105
$devOrPharLoader->register(true);

compiler/build/scoper.inc.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ function (string $filePath, string $prefix, string $content): string {
141141
function (string $filePath, string $prefix, string $content): string {
142142
if (!in_array($filePath, [
143143
'bin/phpstan',
144+
'src/autoloadFunctions.php',
144145
'src/Testing/TestCaseSourceLocatorFactory.php',
145146
'src/Testing/PHPStanTestCase.php',
146147
'vendor/ondrejmirtes/better-reflection/src/SourceLocator/Type/ComposerSourceLocator.php',

e2e/bug-12972b/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor/
2+
composer.lock

e2e/bug-12972b/autoloader.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
require_once __DIR__ . '/vendor/autoload.php';
4+
5+
// Registered *after* Composer's class loader: the third argument (prepend)
6+
// defaults to false, so this ends up at the back of the spl_autoload queue.
7+
//
8+
// \other12972\MyClass is part of Composer's class map (see composer.json), so at
9+
// runtime Composer's class loader resolves it first and this autoloader is never
10+
// invoked for it - running `php real-world.php` therefore never throws.
11+
//
12+
// PHPStan must mirror that order: the Composer class map source locator has to
13+
// resolve the class before this bootstrap autoloader is consulted. Analysing the
14+
// project used to invoke this autoloader first, hitting the LogicException and
15+
// crashing with an internal error that cannot happen at runtime.
16+
spl_autoload_register(function($class) {
17+
if ($class === \other12972\MyClass::class) {
18+
throw new LogicException('this should not happen');
19+
}
20+
});

e2e/bug-12972b/composer.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"autoload": {
3+
"classmap": [
4+
"src/"
5+
]
6+
}
7+
}

e2e/bug-12972b/phpstan.dist.neon

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
parameters:
2+
level: 9
3+
4+
paths:
5+
- src
6+
7+
bootstrapFiles:
8+
- autoloader.php

e2e/bug-12972b/real-world.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
require 'autoloader.php';
4+
5+
$root = new \Foo12972\MyRoot();
6+
$root->doBar(new \other12972\MyClass());
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Foo12972;
4+
5+
use other12972\MyClass;
6+
7+
class MyRoot {
8+
function doBar(MyClass $myClass):void {}
9+
}

e2e/bug-12972b/src/other/file.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace other12972;
4+
5+
class MyClass {
6+
public function doSomething(): int
7+
{
8+
return 1;
9+
}
10+
}

0 commit comments

Comments
 (0)