psalm-plugin-laravel
psalm-plugin-laravel copied to clipboard
Endless memory consumption and aborting
Describe the bug I have an issue similar to @vladyslavstartsev’s #90. On the project I currently want to refactor this plugin fails with endless memory consumption on one file every time. I limited the Psalm config with the directory containing this file only, but this didn’t help. Without the plugin Psalm runs okay but the results are unusable as they have many Laravel-specific issues.
Impacted Versions
$ composer show | grep -E 'psalm|laravel'
barryvdh/laravel-ide-helper v2.8.1 Laravel IDE Helper, generates correct PHPDocs for all F...
beyondcode/laravel-websockets 1.9.0 An easy to use WebSocket server
emadadly/laravel-uuid v1.3.2 laravel uuid a simple, automatic UUID generator for any...
fruitcake/laravel-cors v1.0.6 Adds CORS (Cross-Origin Resource Sharing) headers suppo...
laravel/framework v8.17.2 The Laravel Framework.
laravel/socialite v5.1.2 Laravel wrapper around OAuth 1 & OAuth 2 libraries.
laravel/tinker v2.5.0 Powerful REPL for the Laravel framework.
laravel/ui v2.3.0 Laravel UI utilities and presets.
laravelcollective/html v6.2.0 HTML and Form Builders for the Laravel Framework
orklah/psalm-insane-comparison v1.0.0 Detects possible insane comparison ("string" == 0) to h...
psalm/plugin-laravel v1.4.1 A Laravel plugin for Psalm
sentry/sentry-laravel 1.9.0 Laravel SDK for Sentry (https://sentry.io)
vimeo/psalm 4.3.1 A static analysis tool for finding errors in PHP applic...
Additional context I can send you the whole project archive (in private only). Specific file which aborts the analysis is a hell of a mess, but in order to fix this it would be perfect to have some handy Psalm recomendations.
I have a feeling this is related to a cyclic dependency in the container perhaps. We result to some "dynamic analysis" in some parts of laravel, such as using the container to resolve dependencies.
We use https://github.com/barryvdh/laravel-ide-helper under the hood. Curious, are you able to run php artisan ide-helper:generate
manually without running out of memory?
@mr-feek, I generate IDE helpers on every composer update and install. It goes fine.
Not sure I can help any more without the source code. Feel free to share it with me (fiachra @ squareup . com) if you are comfortable
@mr-feek, sent.
I'm experiencing similar symptoms, memory consumption on a smallish project ramping up and eventually throwing and OutOfMemory Error. Psalm works fine without the plugin.
Interestingly I also have sentry installed like OP so potentially something to do with that?
@CharlieTap feel free to share some code to reproduce if you can!
@mr-feek Unfortunately I'm not allowed to share the source, I did try a build today without sentry and it I still had the same issue. Interestingly this time with errors, I ran it several times and encountered different errors but all of which reside in the same area.
These are my deps:
"require": {
"php": ">=7.4",
"ext-json": "*",
"barryvdh/laravel-dompdf": "^0.8.6",
"facade/ignition": "^2.0",
"fideloper/proxy": "^4.2",
"fruitcake/laravel-cors": "^1.0",
"goldspecdigital/laravel-eloquent-uuid": "^7.0",
"guzzlehttp/guzzle": "^6.0",
"intervention/image": "^2.4",
"laravel/framework": "^7.0",
"laravel/helpers": "^1.2",
"laravel/passport": "^8.4.1",
"laravel/tinker": "^2.0",
"laravel/ui": "^2.0",
"league/csv": "^9.6",
"nunomaduro/collision": "^4.1",
"phenx/php-font-lib": "^0.5.2",
"phenx/php-svg-lib": "^0.3.3",
"predis/predis": "^1.1",
"fzaninotto/faker": "^1.9.1",
"mockery/mockery": "^1.3.1"
},
"require-dev": {
"oscarafdev/migrations-generator": "^2.0",
"phpunit/phpunit": "^8.5",
"psalm/plugin-laravel": "^1.4",
"vimeo/psalm": "^4.3"
},
And the below contains a few stack traces from the issue
/var/www # ./vendor/bin/psalm Scanning files... Analyzing files...
░░E░PHP Fatal error: Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/vimeo/psalm/src/Psalm/Type/Atomic.php on line 529
Symfony\Component\ErrorHandler\Error\FatalError
Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes)
at vendor/vimeo/psalm/src/Psalm/Type/Atomic.php:529
PHP Fatal error: Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/nunomaduro/collision/src/Highlighter.php on line 134
/var/www # ./vendor/bin/psalm
Scanning files...
Analyzing files...
░░E░PHP Fatal error: Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes) in /var/www/vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php on line 36
Symfony\Component\ErrorHandler\Error\FatalError
Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)
at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
32| if ($this instanceof TNamedObject && $this->extra_types) {
33| $extra_types = '&' . implode('&', $this->extra_types);
34| }
35|
> 36| return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
37| }
38|
39| public function getId(bool $nested = false): string
40| {
Whoops\Exception\ErrorException
Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)
at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
32| if ($this instanceof TNamedObject && $this->extra_types) {
33| $extra_types = '&' . implode('&', $this->extra_types);
34| }
35|
> 36| return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
37| }
38|
39| public function getId(bool $nested = false): string
40| {
+1 vendor frames
2 [internal]:0
Whoops\Run::handleShutdown()
/var/www # ./vendor/bin/psalm
Scanning files...
Analyzing files...
░░E░PHP Fatal error: Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes) in /var/www/vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php on line 36
Symfony\Component\ErrorHandler\Error\FatalError
Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)
at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
32| if ($this instanceof TNamedObject && $this->extra_types) {
33| $extra_types = '&' . implode('&', $this->extra_types);
34| }
35|
> 36| return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
37| }
38|
39| public function getId(bool $nested = false): string
40| {
Whoops\Exception\ErrorException
Allowed memory size of 8589934592 bytes exhausted (tried to allocate 13520000 bytes)
at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
32| if ($this instanceof TNamedObject && $this->extra_types) {
33| $extra_types = '&' . implode('&', $this->extra_types);
34| }
35|
> 36| return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
37| }
38|
39| public function getId(bool $nested = false): string
40| {
+1 vendor frames
2 [internal]:0
Whoops\Run::handleShutdown()
Sorry, accidentally closed the issue.
Any news on this?
I'm disconnecting over the holidays, I'll take a look again next week
It also hangs for me for chained eloquent queries:
Model.where(..._)
->join(...)
->join(...)
->join(...)
->join(...)
->get();
Hangs for me. If I remove one of the joins it works again. Looks like it have a max of 5 chained queries. If you have more than that it will just hang
@mr-feek, just wondering if you had time to look into this issue?
@maximal sorry, but no. I don't really use laravel at work anymore at the moment, so I haven't been working at resolving any of these bugs. Happy to merge any PRs that resolve issues though
@maximal @CharlieTap @luisgrases Can you report if the problem still occurs with latest vimeo/psalm
(4.8.1) and psalm/plugin-laravel
(1.4.9)? If it does, please share your current output of: composer show | grep -E 'psalm|laravel'
@caugner, on my test project the problem persists. On my real project I changed the queries’ structure to pass Psalm’s checks.
$ composer show | grep -E 'psalm|laravel'
barryvdh/laravel-ide-helper v2.9.1 Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.
beyondcode/laravel-websockets 1.12.0 An easy to use WebSocket server
emadadly/laravel-uuid v1.3.2 laravel uuid a simple, automatic UUID generator for any model based on Laravel.
fruitcake/laravel-cors v1.0.6 Adds CORS (Cross-Origin Resource Sharing) headers support in your Laravel application
laravel/framework v8.50.0 The Laravel Framework.
laravel/socialite v5.2.3 Laravel wrapper around OAuth 1 & OAuth 2 libraries.
laravel/tinker v2.6.1 Powerful REPL for the Laravel framework.
laravel/ui v2.3.0 Laravel UI utilities and presets.
laravelcollective/html v6.2.1 HTML and Form Builders for the Laravel Framework
orklah/psalm-insane-comparison v1.0.3 Detects possible insane comparison ("string" == 0) to help migrate to PHP8
psalm/plugin-laravel v1.4.9 A Laravel plugin for Psalm
sentry/sentry-laravel 1.9.0 Laravel SDK for Sentry (https://sentry.io)
vimeo/psalm 4.8.1 A static analysis tool for finding errors in PHP applications
@caugner, I could share the test project with you if you wish.
I've got the same issue as the others now
It always comes from the same file as the others:
at vendor/vimeo/psalm/src/Psalm/Type/Atomic/GenericTrait.php:36
32| if ($this instanceof TNamedObject && $this->extra_types) {
33| $extra_types = '&' . implode('&', $this->extra_types);
34| }
35|
> 36| return $this->value . '<' . substr($s, 0, -2) . '>' . $extra_types;
37| }
38|
39| public function getId(bool $nested = false): string
40| {
Is it an issue with query joins?
This fully resolved for me once I declared my model properties (https://github.com/barryvdh/laravel-ide-helper#automatic-phpdocs-for-models) and additionally set throwExceptionOnError="true"
and fixed all errors that were thrown.
Then I was able to remove throwExceptionOnError="true"
and all is good
Im thinking the loop is mainly in the models. Once I declared the properties then Psalm doesnt have to try to infer anything.
I've gone through five projects now where typing out the models (like above) fixed this in every case.
Same problem for us with the models folder. @tm1000 please could you explain better how you solved it ? What do you mean by "typing out" ? Can you tell us how you solved it?
@borgogelli read my comment right before my last comment as I fully detailed how to fix it. You need to declare phpdoc model properties. As of today at my company this still isn't an issue as long as we keep the phpdoc properties up to date.
The endless memory consumption is this plugins attempt at trying to infer types.
FYI this just happened again in a coworker's instance of Laravel. It was because he typehinted Illuminate\Database\Eloquent\Model
on a property. In laravel 8 and lower there is a lot of usage of magic methods. This causes Psalm (utilizing this plugin) to go into a spiral trying to infer types against everything and then run out of memory.
Good news is that you can do a couple of things:
- Use
psalm --debug-by-line
to identify the line that Psalm get's stuck on - In that area try to more specifically define your types. In this situation adding
/** @var SpecificModel1|SpecificModel2 **/
solved the issue - Use ide helper to generate model properties. See https://github.com/barryvdh/laravel-ide-helper#automatic-phpdocs-for-models (use the -W option as well to write them directly into your models)
@tm1000 @maximal @borgogelli @luisgrases
Good news everyone: in release v2.5.0 we located and fixed one important memory consumption issue. Additionally, we have replaced some custom code by correct subs. There is even more potential to optimize memory consumption, but probably these steps already solved your issues guys, can you please test using latest releases (2.5+)?
If the problem persists, Ideally we need to have a small snippet that causes it - so we can add it to a testing suite and check our optimization results. Volunteers are very welcome
YAY!! :tada:
@lptn, seems like it works fine now (given the codebase where previous-version plugin hangs).
I think we can close the issue now.
Thank you guys!
@maximal Great! Please feel free to file more issues here, especially if you think it may affect other people.