psalm icon indicating copy to clipboard operation
psalm copied to clipboard

Notice in php 8.1 autoloading causes psalm to fatal error

Open withinboredom opened this issue 3 years ago • 9 comments

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

withinboredom avatar Feb 18 '22 08:02 withinboredom

Hey @withinboredom, can you reproduce the issue on https://psalm.dev ?

psalm-github-bot[bot] avatar Feb 18 '22 08:02 psalm-github-bot[bot]

Hey @withinboredom, can you reproduce the issue on https://psalm.dev/ ?

No, because the file isn't autoloaded before analysis.

withinboredom avatar Feb 18 '22 08:02 withinboredom

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

withinboredom avatar Feb 18 '22 08:02 withinboredom

I ended up working around it by manually requiring the files and adding them to psalm as stubs.

withinboredom avatar Feb 18 '22 10:02 withinboredom

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?

orklah avatar Feb 18 '22 11:02 orklah

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

jmalinens avatar Mar 01 '22 14:03 jmalinens

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!

orklah avatar Mar 01 '22 18:03 orklah

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 avatar Sep 09 '22 07:09 tolry

@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.

AndrolGenhald avatar Sep 09 '22 16:09 AndrolGenhald

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)

COil avatar Aug 01 '23 12:08 COil

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.

weirdan avatar Aug 01 '23 19:08 weirdan