psalm
psalm copied to clipboard
Notice in php 8.1 autoloading causes psalm to fatal error
If any autoloaded files (in libraries) fire the notice:
Return type of Class:method() should either be compatible with Class::method: OtherClass, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in FILE:LINE in /app/vendor/vimeo/psalm/src/Psalm/Internal/ErrorHandler.php:66
then psalm fails to run any analysis at all. Obviously, libraries should update their code, but psalm should still be usable.
Psalm is installed via composer at version 4.20.0
Hey @withinboredom, can you reproduce the issue on https://psalm.dev ?
Hey @withinboredom, can you reproduce the issue on https://psalm.dev/ ?
No, because the file isn't autoloaded before analysis.
Here's the full error with an attempt at using the phar instead of composer.
Fatal error: During inheritance of IteratorAggregate: Uncaught RuntimeException: PHP Error: Return type of Aura\Accept\AbstractNegotiator::getIterator() should either be compatible with IteratorAggregate::getIterator(): Traversable, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /app/vendor/aura/accept/src/AbstractNegotiator.php:221 in phar:///app/psalm.phar/src/Psalm/Internal/ErrorHandler.php:53
Stack trace:
#0 /app/vendor/aura/accept/src/AbstractNegotiator.php(21): Psalm\Internal\ErrorHandler::Psalm\Internal\{closure}(8192, 'Return type of ...', '/app/vendor/aur...', 221)
#1 phar:///app/psalm.phar/.box/vendor/composer/ClassLoader.php(444): include('/app/vendor/aur...')
#2 phar:///app/psalm.phar/.box/vendor/composer/ClassLoader.php(322): Composer\Autoload\includeFile('/app/vendor/com...')
#3 /app/vendor/aura/accept/src/Charset/CharsetNegotiator.php(22): Composer\Autoload\ClassLoader->loadClass('Aura\\Accept\\Abs...')
#4 phar:///app/psalm.phar/.box/vendor/composer/ClassLoader.php(444): include('/app/vendor/aur...')
#5 phar:///app/psalm.phar/.box/vendor/composer/ClassLoader.php(322): Composer\Autoload\includeFile('/app/vendor/com...')
#6 /app/vendor/aura/accept/src/AcceptFactory.php(89): Composer\Autoload\ClassLoader->loadClass('Aura\\Accept\\Cha...')
#7 /app/vendor/aura/accept/src/AcceptFactory.php(73): Aura\Accept\AcceptFactory->newCharsetNegotiator()
#8 /app/src/bootstrap.php(93): Aura\Accept\AcceptFactory->newInstance()
#9 /app/src/bootstrap.php(111): bindTranslator()
#10 /app/vendor/composer/autoload_real.php(78): require('/app/src/bootst...')
#11 /app/vendor/composer/autoload_real.php(61): composerRequire92c4ff1620ad40cef72cc26ed4e4adbd('6d5b854d6f00538...', '/app/vendor/com...')
#12 /app/vendor/autoload.php(7): ComposerAutoloaderInit92c4ff1620ad40cef72cc26ed4e4adbd::getLoader()
#13 phar:///app/psalm.phar/src/Psalm/Internal/CliUtils.php(100): require_once('/app/vendor/aut...')
#14 phar:///app/psalm.phar/src/Psalm/Internal/Cli/Psalm.php(188): Psalm\Internal\CliUtils::requireAutoloaders('/app/', false, 'vendor')
#15 phar:///app/psalm.phar/src/Psalm/Internal/IncludeCollector.php(31): Psalm\Internal\Cli\Psalm::Psalm\Internal\Cli\{closure}()
#16 phar:///app/psalm.phar/src/Psalm/Internal/Cli/Psalm.php(189): Psalm\Internal\IncludeCollector->runAndCollect(Object(Closure))
#17 phar:///app/psalm.phar/psalm(7): Psalm\Internal\Cli\Psalm::run(Array)
#18 /app/psalm.phar(14): require('phar:///app/psa...')
#19 {main} in /app/vendor/aura/accept/src/AbstractNegotiator.php on line 21
I ended up working around it by manually requiring the files and adding them to psalm as stubs.
When Psalm runs, it treats any notice as an error and crash. While it's okay for Psalm own code, when Psalm has to use a custom autoloader or runs code that has notices or warnings, it's sometimes an issue.
I guess we could declare an error handler each time we have to call the autoloader and then reverts back to the standard error handler.
Are you up for a PR?
I have similar issue with deprecations:
Uncaught Exception: RuntimeException PHP Error: User implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in User.php:27
Emitted in vendor/vimeo/psalm/src/Psalm/Internal/ErrorHandler.php:69
Yeah, that's expected given the explanation above. Feel free to push a fix or to ask for more details as to how it could be fixed!
One addition here: when I run psalm with vendor/bin/psalm ... --threads=$(nproc)
within a github action, the fatal errors will be shown, the run continued and the command will return a 0
return code (indicating github actions that the run was successful) which is not what I would have expected.
If anyone can give me a hint on where the multi thread code lives, I can look into a small fix here.
@tolry "threading" is actually done by forking, grep for pcntl_fork
. I think src/Psalm/Internal/Fork/Pool.php is where you'll need to look.
Same error here because of 'PHP deprecated' warnings in old PHP autoloaded librairies. I must disable ErrorHandler::install($argv);
in vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php
in to make it work. Is there a workaround for this? (can't use threads as a workaround)
Is there a workaround for this? (can't use threads as a workaround)
None known. It's likely more code needs to use ErrorHandler::runWithExceptionsSuppressed()
, but we'd have to see the backtrace to identify where it needs to be added.