diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index df2ca12db6..11504b4448 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -128,6 +128,10 @@ jobs: cd e2e/bug-14514 composer install ../../bin/phpstan analyze bug-14515.php + - script: | + cd e2e/bug-14988 + composer install + ../../bin/phpstan analyse - script: | cd e2e/bug-14724 composer install diff --git a/e2e/bug-14988/.gitignore b/e2e/bug-14988/.gitignore new file mode 100644 index 0000000000..61ead86667 --- /dev/null +++ b/e2e/bug-14988/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/e2e/bug-14988/classes/gadget.php b/e2e/bug-14988/classes/gadget.php new file mode 100644 index 0000000000..5ff2deec60 --- /dev/null +++ b/e2e/bug-14988/classes/gadget.php @@ -0,0 +1,10 @@ +size; + } + +} diff --git a/src/Reflection/BetterReflection/SourceLocator/AutoloadFunctionsSourceLocator.php b/src/Reflection/BetterReflection/SourceLocator/AutoloadFunctionsSourceLocator.php index 3da44c64bf..750c0a175b 100644 --- a/src/Reflection/BetterReflection/SourceLocator/AutoloadFunctionsSourceLocator.php +++ b/src/Reflection/BetterReflection/SourceLocator/AutoloadFunctionsSourceLocator.php @@ -9,6 +9,7 @@ use PHPStan\BetterReflection\Reflector\Reflector; use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator; use function class_exists; +use function function_exists; use function interface_exists; use function PHPStan\autoloadFunctions; use function trait_exists; @@ -35,6 +36,18 @@ public function locateIdentifier(Reflector $reflector, Identifier $identifier): return null; } + // If the name is already a defined function, this locator must not run the bootstrap + // autoloaders for it: a catch-all autoloader (e.g. PHP_CodeSniffer's, which falls back to + // Composer's findFile()) would resolve the name to the function's own file and plain-include + // it a second time - it was loaded once already, e.g. by a package that ships one function + // per PSR-4 path and requires it from its bootstrap - fatally redeclaring the function. + // Returning null only declines this locator; a class and a function may share a name in PHP, + // and a class that genuinely exists under this name in another file is still located by the + // later source locators in the chain. See https://github.com/phpstan/phpstan/issues/14988 + if (function_exists($className)) { + return null; + } + $autoloadFunctions = autoloadFunctions(); foreach ($autoloadFunctions as $autoloadFunction) { $autoloadFunction($className);