Giter Club home page Giter Club logo

psalm-plugin-laravel's Introduction

Psalm plugin for Laravel

The package is seeking maintainers

⚠️ This is a perfect opportunity to learn Laravel very deep and collaborate with other high-skilled developers. At this moment, the package is maintained almost solely by @alies-dev, and he is looking for developers to build a team who can constantly improve this package and the whole Psalm ecosystem.

Some ideas to implement:

  • Fully support custom Model Query Builders (medium)
  • Add an option to rely on Model @property declarations only
  • Get rid of barryvdh/laravel-ide-helper dependency and be more accurate with attribute types
  • Support .sql files for migrations (to find information about attributes and their types)

Packagist version Packagist downloads Type coverage Tests Tests

Overview

This Psalm plugin brings static analysis and type support to projects using Laravel. Our goal is to find as many type-related bugs as possible, therefore increasing developer productivity and application health. Find bugs without the overhead of writing tests!

Screenshot

Versions & Dependencies

Maintained versions:

Laravel Psalm Plugin PHP Laravel Psalm
master ^8.1 10, 11 5
2.9.x ^8.0 9, 10, 11 4, 5
1.6.x >=7.3 6, 8 4
1.4.x >=7.2 6, 7, 8 3, 4

See releases for more details about supported PHP, Laravel and Psalm versions.

Quickstart

Step 1: Install

composer require --dev psalm/plugin-laravel

Step 2: Configure

If you didn't use Psalm on the project before, you need to create a Psalm config:

./vendor/bin/psalm --init

Step 3: enable the plugin:

./vendor/bin/psalm-plugin enable psalm/plugin-laravel

Step 4: Run 🚀

Run your usual Psalm command:

./vendor/bin/psalm

You can customize Psalm configuration using XML config and/or cli parameters.

Recommendation: use baseline file and increase errorLevel at least to 4: this way you can catch more issues. Step by step set errorLevel to 1 and use Psalm and this plugin at full power 🚀.

How it works

Under the hood it just runs https://github.com/barryvdh/laravel-ide-helper and feeds the resultant stubs into Psalm, which can read PhpStorm meta stubs.

It also parses any database migrations it can find to try to understand property types in your database models.

Psalm-Laravel-Plugin or Larastan?

Both! It's fine to use both tools at the same project: they use different approaches to analyze code, and thus you can find more bugs! Psalm and PHPStan use almost same the syntax annotations, so you should not have any conflicts.

psalm-plugin-laravel's People

Contributors

actions-user avatar alies-dev avatar asbiin avatar brendt avatar caugner avatar crynobone avatar ctf0 avatar danog avatar dependabot[bot] avatar dpash avatar eithed avatar ekvedaras avatar github-actions[bot] avatar hendrikheil avatar legion112 avatar lptn avatar masterfermin02 avatar mr-feek avatar muglug avatar mzur avatar netcode avatar nielsvanpach avatar pro2s avatar pthiers avatar ronb-lendesk avatar siebeve avatar tjmmm avatar tm1000 avatar twistor avatar yaegassy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

psalm-plugin-laravel's Issues

Psalm crashes: Could not get class storage for

Describe the bug
When running psalm, an error occurs with the message: Could not get class storage for. This is happening in the ClassLikeStorageProvider, here: https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php#L41-L48. This method is getting called from here: https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php#L339-L341

It seems that the $fq_classlike_name parameter of the get method is null. I've dumped $class_storage and it represents \Illuminate\Database\Eloquent\Builder, that's why I'm creating the issue here and not in the main psalm repository.

When running vendor/bin/psalm --debug-by-line this piece of code, especially the query bit pops up:

return Cart::query()
  ->where('user_uuid', $this->user->getUuid())
  ->where('uuid', $this->session->get('cart_uuid'))
  ->exists();

Cart is just a regular Laravel model nothin special there.

Impacted Versions
barryvdh/laravel-debugbar v3.4.1 PHP Debugbar int...
barryvdh/laravel-ide-helper v2.8.0 Laravel IDE Help...
beyondcode/laravel-dump-server 1.4.0 Symfony Var-Dump...
fruitcake/laravel-cors v1.0.6 Adds CORS (Cross...
inertiajs/inertia-laravel v0.2.5 The Laravel adap...
laravel/framework v7.25.0 The Laravel Fram...
laravel/horizon v4.3.3 Dashboard and co...
laravel/scout v8.2.1 Laravel Scout pr...
laravel/tinker v2.4.2 Powerful REPL fo...
propaganistas/laravel-phone 4.2.4 Adds phone numbe...
psalm/plugin-laravel dev-master 95761f9 A Laravel plugin...
spatie/laravel-backup 6.11.1 A Laravel packag...
spatie/laravel-db-snapshots 1.6.1 Quickly dump and...
spatie/laravel-event-sourcing 4.2.0 The easiest way ...
spatie/laravel-log-dumper 1.3.1 A function to du...
spatie/laravel-model-states 1.6.3
spatie/laravel-multitenancy 1.6.4 Make your Larave...
spatie/laravel-navigation dev-master 90dc440 Manage menus, br...
spatie/laravel-query-builder 2.8.2 Easily build Elo...
spatie/laravel-schemaless-attributes 1.7.1 Add schemaless a...
spatie/laravel-settings dev-master 1725356 Store your appli...
spatie/laravel-sluggable 2.5.0 Generate slugs w...
spatie/laravel-stubs 1.1.0 Opinionated Lara...
spatie/laravel-tail 4.2.1 Easily tail appl...
spatie/laravel-typescript-transformer dev-master 7a73107 Transform your P...
spatie/laravel-view-models 1.3.0 View models in L...
vimeo/psalm dev-master 9822043 A static analysi...

Any idea what the issue could be?

Support for Storage Facade

Is your feature request related to a problem? Please describe.

$disk = Storage::disk('resources');
 
$path = 'definitions/features/';
foreach ($disk->files($path) as $file) {
    //
}

Undefined property: Psalm\Type\Atomic\TNonEmptyList::$type_params

Hi, I'm trying to use this plugin in conjunction with Psalm on Laravel v5.8.19 but after following the instructions I receive the following error when running Psalm:

ErrorException  : Undefined property: Psalm\Type\Atomic\TNonEmptyList::$type_params

  at /home/me/project/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php:345
    341|                                 if ($array_atomic_type instanceof Type\Atomic\ObjectLike) {
    342|                                     return $array_atomic_type->getGenericValueType();
    343|                                 }
    344|
  > 345|                                 return clone $array_atomic_type->type_params[1];
    346|                             }
    347|                         }
    348|
    349|                         if (!$statements_analyzer instanceof StatementsAnalyzer) {

  Exception trace:

  1   Illuminate\Foundation\Bootstrap\HandleExceptions::handleError("Undefined property: Psalm\Type\Atomic\TNonEmptyList::$type_params", "/home/me/project/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php")
      /home/me/project/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php:345

  2   Psalm\Internal\Scanner\PhpStormMetaScanner::Psalm\Internal\Scanner\{closure}(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), "last", Object(Psalm\Context), Object(Psalm\CodeLocation))
      /home/me/project/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php:114

ERROR: InvalidStaticInvocation - RouteServiceProvider - Method Router::__construct is not static, but is called statically

I'm getting the following unexpected error on the RouteServiceProvider that comes with Laravel and hasn't been modified by me:

ERROR: InvalidStaticInvocation - app\Providers\RouteServiceProvider.php:12:36 - Method Illuminate\Routing\Router::__construct is not static, but is called statically
class RouteServiceProvider extends ServiceProvider

Unfortunately I cannot suppress it using @psalm-suppress InvalidStaticInvocation on the RouteServiceProvider class either.

RouteServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * This namespace is applied to your controller routes.
     *
     * In addition, it is set as the URL generator's root namespace.
     *
     * @var string
     */
    protected $namespace = 'App\Http\Controllers';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        //

        parent::boot();
    }

    /**
     * Define the routes for the application.
     *
     * @return void
     */
    public function map()
    {
        $this->mapApiRoutes();

        $this->mapWebRoutes();

        //
    }

    /**
     * Define the "web" routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     *
     * @return void
     */
    protected function mapWebRoutes()
    {
        Route::middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
    }

    /**
     * Define the "api" routes for the application.
     *
     * These routes are typically stateless.
     *
     * @return void
     */
    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }
}

Support for Auth Facade

ERROR: MixedMethodCall - app/Http/Middleware/RedirectIfAuthenticated.php:21:34 - Cannot determine the type of the object on the left hand side of this expression (see https://psalm.dev/015)
        if (Auth::guard($guard)->check()) {

on a fresh laravel install

memory leak

Describe the bug
When using psalm with Laravel plugin I have, as I think, memory leak.
At least, when I run it with plugin, I have

psalm --no-cache
Scanning files...
PHP Fatal error:  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/laravel/framework/src/Illuminate/Support/Arr.php on line 300
PHP Fatal error:  Allowed memory size of 8589934592 bytes exhausted (tried to allocate 65536 bytes) in /var/www/vendor/sentry/sentry/src/Integration/FrameContextifierIntegration.php on line 56

when I run without plugin, it works fine

Impacted Versions
laravel/framework 5.8
vimeo/psalm 3.9
psalm/plugin-laravel 1.2.1
Can't give more info due to NDA.

Additional context
Add any other context about the problem here.
I can provide some other info (logs all dependencies) but only in private DM

Incompatible with Psalm 3.7?

I have Psalm 3.7 installed. When I run

composer require --dev psalm/plugin-laravel

Composer tells me:

Your requirements could not be resolved to an installable set of packages.

Problem 1
- Installation request for psalm/plugin-laravel ^0.7.0 -> satisfiable by psalm/plugin-laravel[0.7].
- psalm/plugin-laravel 0.7 requires vimeo/psalm 3.2.|3.3.|3.4.|3.5.|3.6.* -> satisfiable by vimeo/psalm[3.2, 3.2.10, 3.2.11, 3.2.12, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8, 3.2.9, 3.3.0, 3.3.1, 3.3.2, 3.4.0, 3.4.1, 3.4.10, 3.4.11, 3.4.12, 3.4.2, 3.4.3, 3.4.4, 3.4.5, 3.4.6, 3.4.7, 3.4.8, 3.4.9, 3.5.0, 3.5.1, 3.5.2, 3.5.3, 3.6.0, 3.6.1, 3.6.2, 3.6.3, 3.6.4, 3.6.5, 3.6.6] but these conflict with your requirements or minimum-stability.

Handling of variadic functions

Laravel makes use of the variadic function pattern quite a bit. (func_get_args is used 110 times in the Framework code.) Even a Laravel skeleton project starts out with this error:

ERROR: TooManyArguments - app/Http/Controllers/Auth/VerificationController.php:34:44 - Too many arguments for method Illuminate\Routing\ControllerMiddlewareOptions::only - expecting 1 but saw 2
    $this->middleware('throttle:6,1')->only('verify', 'resend');

vimeo/psalm#605 suggested plugin-based stubbing, which seems like it might be appropriate here.

Interfaces for Models not being recognized

Problem:
I am implementing an interface but psalm is not recognizing that it is an instance of that interface.

Example:

# User.php
<?php
namespace App\Models\Acl;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable;

class User extends Model implements Authenticable {
   /** stuff */
}

# Controller.php
<?php

namespace App\Http\Controllers;

use Auth;
use App\Models\Acl\User;

class SomeController {
  public function login()
  {
    $user = User::first();
    Auth::login($user); //  **Psalm Error** 
  }
}

/*

**Psalm Error:**
Argument 1 of Auth::login expects Illuminate\Contracts\Auth\Authenticatable, App\Models\Acl\User provided

So that was pretty weird,

then I looked into vendor/psalm/plugin-laravel/src/cache/models.php

and saw the following entry:
namespace App\Models\Acl{
/**
 * App\Models\Acl\User
 *
 * @property int $id
 * @property string $name
 * @property string $email
 * @property string $password
 * @property string $remember_token
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @property int|null $role_id
 * @property \Illuminate\Support\Carbon|null $last_active
 * @property \Illuminate\Support\Carbon|null $deleted_at
 * @property string|null $surname
 * @property string|null $telephone
 * @property int|null $reference_id
 * @property bool $login_allowed
 * @property int|null $salutation_id
 * @method static bool|null forceDelete()
 * @method static \Illuminate\Database\Query\Builder|\App\Models\Acl\User onlyTrashed()
 * @method static bool|null restore()
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereDeletedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereEmail($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereLastActive($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereLoginAllowed($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereName($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User wherePassword($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereReferenceId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereRememberToken($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereRoleId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereSalutationId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereSurname($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereTelephone($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Acl\User whereUpdatedAt($value)
 * @method static \Illuminate\Database\Query\Builder|\App\Models\Acl\User withTrashed()
 * @method static \Illuminate\Database\Query\Builder|\App\Models\Acl\User withoutTrashed()
 */
	class User extends \Eloquent {}
}

so I changed the following line:

	class User extends \Eloquent {}

to

	class User extends \Eloquent implements \Illuminate\Contracts\Auth\Authenticatable {}

So I think that when we generate the stub we will also need to include the interfaces for these models.

Also note that if I disable the laravel plugin psalm doesn't find any errors with the file in question :/

If someone could give me a rough idea of what the solution could look like I don't mind diving into it,

If you'd like me to setup a demo repo I also don't mind doing that- let me know how I can help!

Application::makeWith does not exist

ERROR: UndefinedInterfaceMethod - tests/Feature/Tax/TaxServiceTest.php:1031:30 - Method Illuminate\Contracts\Foundation\Application::makewith does not exist (see https://psalm.dev/181)
        $taxService = app()->makeWith(TaxService::class, ['taxVerification' => $taxVerification]);

We can probably figure out the app instance to use and return that, rather than the application interface which is missing some methods.

#57 will be a step in the right direction for helping this

Method Illuminate\Foundation\Application::environment does not exist

Describe the bug

if (app()->environment('production')) {
    // do something
}

Impacted Versions

barryvdh/laravel-ide-helper           v2.7.0             Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.
bugsnag/bugsnag-laravel               v2.19.0            Official Bugsnag notifier for Laravel applications.
laravel/dusk                          v5.11.0            Laravel Dusk provides simple end-to-end testing and browser automation.
laravel/framework                     v7.19.1            The Laravel Framework.
laravel/helpers                       v1.2.0             Provides backwards compatibility for helpers in the latest Laravel release.
laravel/tinker                        v2.4.0             Powerful REPL for the Laravel framework.
laravel/ui                            v2.0.3             Laravel UI utilities and presets.
psalm/plugin-laravel                  v1.3.1             A Laravel plugin for Psalm
spatie/laravel-newsletter             4.8.0              Manage newsletters in Laravel
vimeo/psalm                           3.12.2             A static analysis tool for finding errors in PHP applications

Additional context
N/A

Laravel 7: Failed to load plugin Psalm\LaravelPlugin\Plugin

I'm getting the following error after having upgraded to Laravel 7:

$ composer info | grep -P "(laravel|psalm)"
barryvdh/laravel-ide-helper           v2.6.7   Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.
beyondcode/laravel-dump-server        1.4.0    Symfony Var-Dump Server for Laravel
laravel/framework                     v7.5.2   The Laravel Framework.
laravel/tinker                        v2.4.0   Powerful REPL for the Laravel framework.
psalm/plugin-laravel                  1.1.1    A Laravel plugin for Psalm
vimeo/psalm                           3.10.1   A static analysis tool for finding errors in PHP applications

$ vendor/bin/psalm
Scanning files...

   Psalm\Exception\ConfigException

  Failed to load plugin Psalm\LaravelPlugin\Plugin

  at D:\path\to\project\vendor\vimeo\psalm\src\Psalm\Config.php:1173
    1169|                  */
    1170|                 $plugin_object = new $plugin_class_name;
    1171|                 $plugin_object($socket, $plugin_config);
    1172|             } catch (\Throwable $e) {
  > 1173|                 throw new ConfigException('Failed to load plugin ' . $plugin_class_name, 0, $e);
    1174|             }
    1175|
    1176|             $project_analyzer->progress->debug('Loaded plugin ' . $plugin_class_name . ' successfully'. PHP_EOL);
    1177|         }

  1   D:\path\to\project\vendor\laravel\framework\src\Illuminate\Auth\SessionGuard.php:105
      TypeError::("Argument 2 passed to Illuminate\Auth\SessionGuard::__construct() must be an instance of Illuminate\Contracts\Auth\UserProvider, null given, called in D:\path\to\project\vendor\laravel\framework\src\Illuminate\Auth\AuthManager.php on line 125")

  2   D:\path\to\project\vendor\laravel\framework\src\Illuminate\Auth\AuthManager.php:125
      Illuminate\Auth\SessionGuard::__construct("web", Object(Illuminate\Session\Store))

models.stubphp out of sync

There are small differences between a project generated models file, compared to psalm cached version:

This is an excerpt from models.stubphp

 * @method static \Illuminate\Database\Eloquent\Builder|\App\Context\Product\Models\EventSession whereEndsAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Context\Product\Models\EventSession whereEventUuid($value)

While this is the project-level equivalent:

 * @method static \Illuminate\Database\Eloquent\Builder|\App\Context\Product\Models\EventSession whereEndsAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Context\Product\Models\EventSession whereEvent(\App\Context\Product\Models\Event $event)
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Context\Product\Models\EventSession whereEventUuid($value)

You can see that whereEvent misses from the psalm version. The scope certainly exists btw. I've regenerated both the cached psalm file and the project file, they stay out of sync.

As a sidenote: how can I tell psalm to use my project-level ide helper file, instead of generating a new one?

Plugin triggers "ErrorException : Undefined index: unguarded" in StaticPropertyFetchAnalyzer

Describe the bug
Adding this plugin to psalm v4.0.0 or v4.0.1 triggers "ErrorException : Undefined index: unguarded"

It wouldn't surprise me if there is some kind of clash with this class inheriting from our class named 'Model', which does a lot of Eloquent-y stuff. If I prune enough code it also seems to inhibit loading this plugin for some reason.

Memory usage was also to the order of 24GB 😅 when analyzing code it doesn't crash on. Instead of ~870MB without this plugin.

$ vendor/bin/psalm --debug --alter --issues="InvalidFalsableReturnType" Product/Module/Models/Group.php

[..]

Getting Product/Module/Models/Group.php
Analyzing Product/Module/Models/Group.php

   ErrorException  : Undefined index: unguarded

  at vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php:317
    313|             }
    314|         }
    315|
    316|         $class_storage = $codebase->classlike_storage_provider->get($declaring_property_class);
  > 317|         $property = $class_storage->properties[$prop_name];
    318|
    319|         if ($var_id) {
    320|             if ($property->type) {
    321|                 $context->vars_in_scope[$var_id] = \Psalm\Internal\Type\TypeExpander::expandUnion(

  Exception trace:

  1   Illuminate\Foundation\Bootstrap\HandleExceptions::handleError("Undefined index: unguarded", "vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php")
      vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php:317

  2   Psalm\Internal\Analyzer\Statements\Expression\Fetch\StaticPropertyFetchAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticPropertyFetch), Object(Psalm\Context))
     vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php:212

  Please use the argument -v to see more details.

Impacted Versions

$ composer show | grep -E 'psalm|laravel'
barryvdh/laravel-ide-helper           v2.8.1             Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.
cviebrock/laravel-elasticsearch       4.2.2              An easy way to use the official PHP ElasticSearch client in your Laravel applications.
laravel/framework                     v6.19.1            The Laravel Framework.
laravel/passport                      v9.3.2             Laravel Passport provides OAuth2 server support to Laravel.
laravel/tinker                        v2.4.2             Powerful REPL for the Laravel framework.
psalm/plugin-laravel                  v1.4.1             A Laravel plugin for Psalm
supliu/laravel-query-monitor          1.0.1              Laravel Query Monitor
vimeo/psalm                           4.0.1              A static analysis tool for finding errors in PHP applications

Additional context

Minimized Product/Module/Models/Group.php:

<?php

namespace Apnvpn\Models;

use Product\Ldap\Model;

class Group extends Model
{

}
$ vendor/bin/psalm --version
Psalm 4.0.1@b1e2e30026936ef8d5bf6a354d1c3959b6231f44

Support for model factories

We need to template model factories and create a return type provider to decide if a collection or model instance will be returned

[enhancement] Template the container

we need to add support for the likes of

  • app() and resolve() global helpers to return instances of Application with no args, or else proxy the method call to Container::make
  • Container::make needs support to take a classname as first parameter, and returns an instance of that classname

_ide_helper_models.php isn't loaded in github actions

I'm getting these kinds of errors when running psalm in a github action:

ERROR: UndefinedMagicPropertyFetch - app/Context/Balance/Projectors/BalanceProjector.php:35:9 - Magic instance property Illuminate\Database\Eloquent\Model::$amount is not defined (see https://psalm.dev/218)
        $balance->amount += $event->amount;

Running psalm locally doesn't yield these errors. I'm sure _ide_helper_models.php is available within the github action, since it's committed in the repository.

Phpstorm complains about multiple class definitions because of ide-helper

Since we are generating ide-helper files, phpstorm complains about multiple class definitions.

Perhaps we should consider moving ide-helper to a suggested peer dependency, as I assume a lot of laravel developers will already have it installed and the files generated. Then the developer can configure the path to their phpstorm stubs file if they would like.

image

Psalm not working with Laravel Aliases for Facades

> ./vendor/bin/psalm --show-info=false
Scanning files...

   Psalm\Exception\ConfigException 

  Failed to load plugin Psalm\LaravelPlugin\Plugin

  at vendor/vimeo/psalm/src/Psalm/Config.php:1216
    1212|                  */
    1213|                 $plugin_object = new $plugin_class_name;
    1214|                 $plugin_object($socket, $plugin_config);
    1215|             } catch (\Throwable $e) {
  > 1216|                 throw new ConfigException('Failed to load plugin ' . $plugin_class_name, 0, $e);
    1217|             }
    1218| 
    1219|             $project_analyzer->progress->debug('Loaded plugin ' . $plugin_class_name . ' successfully' . PHP_EOL);
    1220|         }

I'm using these packages:

"php": "7.3.*"
"laravel/framework": "^7.24"
"psalm/plugin-laravel": "1.4.0"
"vimeo/psalm": "3.16"

Laravel 8.3+: optional() in RouteServiceProvider causes MixedPropertyFetch + MixedArgument errors

Describe the bug
As of Laravel 8.3.0 (this commit), the RouteServiceProvider uses the helper optional(), which causes the following errors:

ERROR: MixedPropertyFetch - app/Providers/RouteServiceProvider.php:60:45 - Cannot fetch property on mixed var  (see https://psalm.dev/034)
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
ERROR: MixedArgument - app/Providers/RouteServiceProvider.php:60:45 - Argument 1 of Illuminate\Cache\RateLimiting\Limit::by cannot be non-empty-mixed|null|string, expecting string (see https://psalm.dev/030)
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
ERROR: PossiblyNullArgument - app/Providers/RouteServiceProvider.php:60:45 - Argument 1 of Illuminate\Cache\RateLimiting\Limit::by cannot be null, possibly null value provided (see https://psalm.dev/078)
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());

Impacted Versions
Affects Laravel 8.3.0 and later.

Additional context
I guess this could be fixed by adding stubs for both the optional() function and the Optional class:

  1. optional() returns an Optional object if it's second $callback parameter is omitted.
  2. Optional::__get() returns null unless the optional value is an object.

However, I'm not sure if it is possible to declare that Optional::__get() returns the value of the corresponding property (magic or not) on the value.

As for the PossiblyNullArgument, this would probably require to update the Limit::by() signature to accept string|null, since Request::ip() can return null.

PS: A workaround is to modify RouteServiceProvider::configureRateLimiting() as follows:

    /**
     * Configure the rate limiters for the application.
     *
     * @return void
     */
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            /** @var User|null $user */
            $user = $request->user();

            /** @var \Illuminate\Support\Optional $optional */
            $optional = optional($user);

            /** @var string $key */
            $key = $optional->id ?: $request->ip();
            return Limit::perMinute(60)->by($key);
        });
    }

models.stubphp out of sync

Psalm reported following error:

ERROR: InvalidArgument - app/Context/Product/Actions/UpdateEventGeneralCardAction.php:16:41 - Argument 2 of App\Context\Product\Actions\UpdateEventGeneralCardAction::saveProductDetail expects App\Context\Product\Models\ProductDetail, Illuminate\Database\Eloquent\Relations\BelongsTo provided (see https://psalm.dev/004)
        $this->saveProductDetail($data, $event->productDetail);

It seems like it didn't recognise productDetail as the relation.

So I did some digging in _ide_helper_models.php, where the property seemed to exist:

* @property-read \App\Context\Product\Models\ProductDetail $productDetail

Looking at models.stubphp in vendor/psalm/plugin-laravel/src/cache/models.stubphp though, the property wasn't present. It seemed like this file was an outdated version.

I've tried reinstalling psalm/plugin-laravel, but the same stub file, missing the property gets generated.

Any ideas?

Undefined index: wrap at Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php:318

Describe the bug

I have some code that Psalm okays when psalm/plugin-laravel is disabled. When psalm/plugin-laravel is enabled, I get an Undefined index: wrap error. Full repro at https://github.com/AlbinoDrought/repro-psalm-laravel-undefined-index

I have this resource:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
 * @property-read \App\User $resource
 */
class User extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->resource->id,
            'name' => $this->resource->name,
        ];
    }
}

I have this controller:

<?php

namespace App\Http\Controllers;

use App\Http\Resources\User as UserResource;
use App\User;

class UserController extends Controller
{
    public function __construct()
    {
        UserResource::$wrap = 'items';
    }

    public function show(User $user)
    {
        return new UserResource($user);
    }
}

I see

Getting /app/app/Http/Controllers/UserController.php
Analyzing /app/app/Http/Controllers/UserController.php

   ErrorException 

  Undefined index: wrap

  at vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php:318
    314|             }
    315|         }
    316| 
    317|         $class_storage = $codebase->classlike_storage_provider->get($declaring_property_class);
  > 318|         $property = $class_storage->properties[$prop_name];
    319| 
    320|         if ($var_id) {
    321|             if ($property->type) {
    322|                 $context->vars_in_scope[$var_id] = \Psalm\Internal\Type\TypeExpander::expandUnion(

The Undefined index: $wrap error is triggered by UserResource::$wrap = 'items';.

I have a full repro repository at https://github.com/AlbinoDrought/repro-psalm-laravel-undefined-index

Impacted Versions

barryvdh/laravel-ide-helper           v2.7.0    Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.
fruitcake/laravel-cors                v1.0.6    Adds CORS (Cross-Origin Resource Sharing) headers support in your Laravel application
laravel/framework                     v7.19.1   The Laravel Framework.
laravel/tinker                        v2.4.1    Powerful REPL for the Laravel framework.
psalm/plugin-laravel                  v1.3.1    A Laravel plugin for Psalm
vimeo/psalm                           3.12.2    A static analysis tool for finding errors in PHP applications

Additional context

For what it's worth, PHPStorm detects the $wrap property:

image

EDIT: Still occurs with psalm/plugin-laravel:1.4.2 and vimeo/psalm:4.4.1, updated repro and added Github Actions https://github.com/AlbinoDrought/repro-psalm-laravel-undefined-index/actions

Run tests on actual laravel project

Is your feature request related to a problem? Please describe.
Let's clone our supported laravel releases during our CI pipeline and run this plugin and an accompanying baseline file on them. That will help to ensure that we don't have any regressions.

Describe the solution you'd like
Larastan and psalm itself both do something similar -- let's use them for inspiration

Could not get class storage for

Describe the bug
After updating to 1.4.0 version, psalm was crashed by following error

░░░░░░[2020-08-21 19:05:05] development.ERROR: Could not get class storage for 
Stack trace in the forked worker:
#0 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php(340): Psalm\Internal\Provider\ClassLikeStorageProvider->get()
#1 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(173): Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalyzer::analyze()
#2 /myproject/vendor/psalm/plugin-laravel/src/ReturnTypeProvider/ModelReturnTypeProvider.php(86): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze()
#3 /myproject/vendor/psalm/plugin-laravel/src/ReturnTypeProvider/ModelReturnTypeProvider.php(49): Psalm\LaravelPlugin\ReturnTypeProvider\ModelReturnTypeProvider::executeFakeCall()
#4 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php(108): Psalm\LaravelPlugin\ReturnTypeProvider\ModelReturnTypeProvider::getMethodReturnType()
#5 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(985): Psalm\Internal\Provider\MethodReturnTypeProvider->getReturnType()
#6 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(154): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze()
#7 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(45): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression()
#8 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php(50): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze()
#9 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(150): Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer::analyze()
#10 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(45): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression()
#11 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php(164): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze()
#12 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(131): Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer::analyze()
#13 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(45): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression()
#14 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(500): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze()
#15 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(169): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement()
#16 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(570): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze()
#17 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1921): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze()
#18 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(748): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod()
#19 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(221): Psalm\Internal\Analyzer\ClassAnalyzer->analyze()
#20 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(349): Psalm\Internal\Analyzer\FileAnalyzer->analyze()
#21 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php(184): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}()
#22 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(415): Psalm\Internal\Fork\Pool->__construct()
#23 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(275): Psalm\Internal\Codebase\Analyzer->doAnalysis()
#24 /myproject/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(632): Psalm\Internal\Codebase\Analyzer->analyzeFiles()
#25 /myproject/vendor/vimeo/psalm/src/psalm.php(680): Psalm\Internal\Analyzer\ProjectAnalyzer->check()
#26 /myproject/vendor/vimeo/psalm/psalm(2): require_once('/home/user/mypr...')
#27 {main}

  at vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php:348
    344|                             if ($this->task_done_closure !== null) {
    345|                                 ($this->task_done_closure)($message->data);
    346|                             }
    347|                         } elseif ($message instanceof ForkProcessErrorMessage) {
  > 348|                             throw new \Exception($message->message);
    349|                         } else {
    350|                             error_log('Child should return ForkMessage - response type=' . gettype($message));
    351|                             $this->did_have_error = true;
    352|                         }


Impacted Versions

barryvdh/laravel-ide-helper v2.8.0
laravel/framework v7.25.0
vimeo/psalm 3.14.1
psalm/plugin-laravel v1.4.0

Failure to install due to dependencies conflict

Describe the bug
Initial installation on Laravel 6.0 is not possible due to dependencies conflict.

Impacted Versions

barryvdh/laravel-debugbar             v3.2.9       PHP Debugbar integration for Laravel
fruitcake/laravel-cors                v1.0.4       Adds CORS (Cross-Origin Resource Sharing) headers support in y...
laravel/framework                     v6.0.0       The Laravel Framework.
laravel/tinker                        v1.0.10      Powerful REPL for the Laravel framework.
vimeo/psalm                           3.12.0       A static analysis tool for finding errors in PHP applications

Additional context

Using version ^1.3 for psalm/plugin-laravel
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for composer/semver (locked at 3.0.0) -> satisfiable by composer/semver[3.0.0].
    - psalm/plugin-laravel 1.x-dev requires barryvdh/laravel-ide-helper ^2.7 -> satisfiable by barryvdh/laravel-ide-helper[v2.7.0].
    - psalm/plugin-laravel v1.3.0 requires barryvdh/laravel-ide-helper ^2.7 -> satisfiable by barryvdh/laravel-ide-helper[v2.7.0].
    - Conclusion: don't install barryvdh/laravel-ide-helper v2.7.0
    - Installation request for psalm/plugin-laravel ^1.3 -> satisfiable by psalm/plugin-laravel[1.x-dev, v1.3.0].

Support for response helper

Describe the bug

ERROR: InvalidReturnStatement - app/Http/Controllers/Api/V1/UserController.php:58:16 - The inferred type 'Illuminate\Contracts\Routing\ResponseFactory|Illuminate\Http\Response' does not match the declared return type 'Illuminate\Http\Response' for App\Http\Controllers\Api\V1\UserController::storeCoupon (see https://psalm.dev/128)
        return response('OK');

Impacted Versions

barryvdh/laravel-ide-helper               dev-master a233d5b              Laravel IDE Helper, generates 
laravel/framework                         v5.8.37                         The Laravel Framework.
psalm/plugin-laravel                      dev-eloquent-collection c2e30af A Laravel plugin for Psalm
vimeo/psalm                               3.11.2                          A static analysis tool for finding er...

Additional context
We just need to write a simple stub for the response helper

Version incompatibility with Laravel 6.0

Hi,

This package depends on orchestra/testbench:^3.5, but none of the releases with that constraint are compatible with Laravel 6.

It looks like orchestra/testbench needs to be updated to ^4.0.

Contents of models.stubphp are incomplete

Describe the bug
Contents of models.stubphp are incomplete. I get errors about missing model class properties. E.g. id. This was not an issue in 1.4.0 version. I don't get this issue with _ide_helper_models.php generated by barryvdh/laravel-ide-helper.

Impacted Versions
1.4.1 and 1.4.2

AppInterfaceProvider not compatible with MethodReturnTypeProviderInterface

After installing psalm, the Laravel plug-in and enabling the plug-in, I get the following error message:

PHP Fatal error:  Declaration of Psalm\LaravelPlugin\AppInterfaceProvider::getMethodReturnType(Psalm\StatementsSource $statements_source, string $fq_classlike_name, string $method_name_lowercase, array $call_args, Psalm\Context $context, ?Psalm\CodeLocation $code_location = NULL, ?array $template_type_parameters = NULL) must be compatible with Psalm\Plugin\Hook\MethodReturnTypeProviderInterface::getMethodReturnType(Psalm\StatementsSource $source, string $fq_classlike_name, string $method_name_lowercase, array $call_args, Psalm\Context $context, Psalm\CodeLocation $code_location, ?array $template_type_parameters = NULL, ?string $called_fq_classlike_name = NULL, ?string $called_method_name_lowercase = NULL) in /Users/wgriffioen/project/vendor/psalm/plugin-laravel/src/AppInterfaceProvider.php on line 11

After I've altered the method to be compatible with the interface, I've been able to run Psalm successfully.

Failed to load plugin Psalm\LaravelPlugin\Plugin

Hi, thanks for this psalm plugin.

I tried this plugin, but failed with the following error

 Psalm\Exception\ConfigException  : Failed to load plugin Psalm\LaravelPlugin\Plugin

  at /home/me/project/vendor/vimeo/psalm/src/Psalm/Config.php:886
    882|                  */
    883|                 $plugin_object = new $plugin_class_name;
    884|                 $plugin_object($socket, $plugin_config);
    885|             } catch (\Throwable $e) {
  > 886|                 throw new ConfigException('Failed to load plugin ' . $plugin_class_name, 0, $e);
    887|             }
    888|         }
    889| 
    890|         foreach ($this->filetype_scanner_paths as $extension => $path) {

  Exception trace:

  1   Error::("Class 'Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory' not found")
      /home/me/project/vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php:128

  2   Illuminate\Routing\RoutingServiceProvider::Illuminate\Routing\{closure}(Object(Illuminate\Foundation\Application), [])
      /home/me/project/vendor/laravel/framework/src/Illuminate/Container/Container.php:776

Support for path helpers

Currently, the likes of base_path() emits an UnresolvableInclude.

These are all of the path helpers to add support for

/**
     * Get the base path of the Laravel installation.
     *
     * @return string
     */
    public function basePath();

    /**
     * Get the path to the bootstrap directory.
     *
     * @param  string  $path Optionally, a path to append to the bootstrap path
     * @return string
     */
    public function bootstrapPath($path = '');

    /**
     * Get the path to the application configuration files.
     *
     * @param  string  $path Optionally, a path to append to the config path
     * @return string
     */
    public function configPath($path = '');

    /**
     * Get the path to the database directory.
     *
     * @param  string  $path Optionally, a path to append to the database path
     * @return string
     */
    public function databasePath($path = '');

    /**
     * Get the path to the environment file directory.
     *
     * @return string
     */
    public function environmentPath();

    /**
     * Get the path to the resources directory.
     *
     * @param  string  $path
     * @return string
     */
    public function resourcePath($path = '');

    /**
     * Get the path to the storage directory.
     *
     * @return string
     */
    public function storagePath();

How to Supress PossiblyInvalidArgument for Request Objects?

Laravel has Form Request classes that can look like the following:

<?php

namespace App\Modules\Accounts\Requests;

use App\Http\Requests\FormRequest;

class SaveAccountRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'string|max:128',
            'email' => 'required|string|email',
        ];
    }

    public function email(): string
    {
        return $this->input('email');
    }
}

The input() method has a return signature declared as * @return string|array|null. For that reason, Psalm end up giving PossiblyInvalidArgument [...] possibly different type array<array-key, mixed>|string|null provided.

I tried the following:

    <issueHandlers>
        <PossiblyInvalidArgument>
            <errorLevel type="suppress">
                <file name="*Request.php" />
            </errorLevel>
        </PossiblyInvalidArgument>
    </issueHandlers>

but that configuration leads to

/app # vendor/bin/psalm
Could not resolve config path to /app//*Request.php

What can I do to suppress this problem but only for Request objects or for any interaction with the input method of a Form Request class?

Allow more precise Eloquent relationship specification

Is your feature request related to a problem? Please describe.

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    /**
     * @psalm-return \Illuminate\Database\Eloquent\Relations\HasMany<\App\Models\User>
     */
    public function users()
    {
        return $this->hasMany(User::class);
    }

    public function something()
    {
        return $this->users()->first();
    }
}

The return type of something() type should be known and it should be \App\Models\User|null.

Describe the solution you'd like
The solution would be to support template in all of the relationship calls like in the above example.

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.

Support for polymorphic relations

Describe the bug

ERROR: TooManyTemplateParams - app/Context/Product/Models/FieldOfStudy.php:26:16 - Illuminate\Database\Eloquent\Relations\MorphToMany<App\Context\Product\Types\Event\Models\Event> has too many template params, expecting 0 (see https://psalm.dev/184)
        return $this->morphedByMany(Event::class, 'product');


ERROR: TooManyTemplateParams - app/Context/Product/Models/ProductAttribute.php:23:16 - Illuminate\Database\Eloquent\Relations\MorphToMany<App\Context\Product\Types\Event\Models\Event> has too many template params, expecting 0 (see https://psalm.dev/184)
        return $this->morphedByMany(
            Event::class,
            'assigned_attribute',
            'product_assigned_attributes'
        );


ERROR: TooManyTemplateParams - app/Context/Product/Types/Event/Models/Event.php:107:16 - Illuminate\Database\Eloquent\Relations\MorphToMany<App\Context\Product\Models\FieldOfStudy> has too many template params, expecting 0 (see https://psalm.dev/184)
        return $this->morphToMany(
            FieldOfStudy::class,
            'product',
            'product_field_of_study',
            'product_uuid'
        )->withPivot('credit_hours');

Impacted Versions
dev-master

Additional context
@brendt reported here: #62 (comment)

Getting UndefinedMagicMethod on query scopes

Model scope:

public function scopeAuthoredBy($query, User $user)
{
    return $query->whereHas('author', function ($subQuery) use ($user) {
        $subQuery->where('id', $user->id);
    });
}

Implementation:

/**
 * @return \Illuminate\Http\JsonResponse
 */
public function recentlyAuthored()
{
    $reports = Report::with('categories', 'customer')
        ->authoredBy(Auth::user())
        ->latest()
        ->get()
        ->take(5);

    return response()->json(ReportResource::collection($reports));
}

Psalm's result:

ERROR: UndefinedMagicMethod - app\Http\Controllers\AdminDashboardController.php:18:15 - Magic method Illuminate\Database\Eloquent\Builder::authoredby does not exist
            ->authoredBy(Auth::user())

Could not get class storage for eloquent

Describe the bug
When I try to execute psalm receiving next error

In ClassLikeStorageProvider.php line 45:

Could not get class storage for eloquent

Impacted Versions
barryvdh/laravel-debugbar v3.3.3 PHP Debugbar integ...
barryvdh/laravel-ide-helper v2.7.0 Laravel IDE Helper...
barryvdh/laravel-snappy v0.4.7 Snappy PDF/Image f...
bugsnag/bugsnag-laravel v2.17.1 Official Bugsnag n...
fruitcake/laravel-cors v0.11.4 Adds CORS (Cross-O...
laravel/browser-kit-testing v5.1.3 Provides backwards...
laravel/framework v5.8.38 The Laravel Framew...
laravel/passport v7.5.1 Laravel Passport p...
laravel/slack-notification-channel v2.0.2 Slack Notification...
laravel/socialite v4.3.2 Laravel wrapper ar...
laravel/telescope v2.1.7 An elegant debug a...
laravel/tinker v1.0.10 Powerful REPL for ...
laravelcollective/html v5.8.1 HTML and Form Buil...
m50/psalm-json-to-junit dev-master 840343b Converts psalm's j...
psalm/plugin-laravel v1.3.0 A Laravel plugin f...
spatie/laravel-webhook-server 1.4.0 Send webhooks in L...
vimeo/psalm 3.12.2 A static analysis ...

Additional context
[2020-07-07 10:58:08] local.ERROR: Could not get class storage for eloquent ["[object] (InvalidArgumentException(code: 0): Could not get class storage for eloquent at /app/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php:45)\n[stacktrace]\n#0 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php(93): Psalm\Internal\Provider\ClassLikeStorageProvider->get('Eloquent')\n#1 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(151): Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context))\n#2 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(45): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context), false, NULL, false)\n#3 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php(246): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\StaticCall), Object(Psalm\Context))\n#4 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(128): Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Variable), Object(PhpParser\Node\Expr\StaticCall), NULL, Object(Psalm\Context), NULL)\n#5 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php(45): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::handleExpression(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, NULL, true)\n#6 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(439): Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Expr\Assign), Object(Psalm\Context), false, NULL, true)\n#7 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(163): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\Expression), Object(Psalm\Context), NULL)\n#8 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfAnalyzer.php(795): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context))\n#9 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfAnalyzer.php(348): Psalm\Internal\Analyzer\Statements\Block\IfAnalyzer::analyzeIfBlock(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\If_), Object(Psalm\Internal\Scope\IfScope), Object(Psalm\Context), Object(Psalm\Context), Object(Psalm\Context), Array)\n#10 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(387): Psalm\Internal\Analyzer\Statements\Block\IfAnalyzer::analyze(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\If_), Object(Psalm\Context))\n#11 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php(163): Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement(Object(Psalm\Internal\Analyzer\StatementsAnalyzer), Object(PhpParser\Node\Stmt\If_), Object(Psalm\Context), Object(Psalm\Context))\n#12 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php(547): Psalm\Internal\Analyzer\StatementsAnalyzer->analyze(Array, Object(Psalm\Context), Object(Psalm\Context), true)\n#13 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(1803): Psalm\Internal\Analyzer\FunctionLikeAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Internal\Provider\NodeDataProvider), Object(Psalm\Context))\n#14 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php(754): Psalm\Internal\Analyzer\ClassAnalyzer->analyzeClassMethod(Object(PhpParser\Node\Stmt\ClassMethod), Object(Psalm\Storage\ClassLikeStorage), Object(Psalm\Internal\Analyzer\ClassAnalyzer), Object(Psalm\Context), Object(Psalm\Context))\n#15 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php(217): Psalm\Internal\Analyzer\ClassAnalyzer->analyze(Object(Psalm\Context), Object(Psalm\Context))\n#16 /app/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(340): Psalm\Internal\Analyzer\FileAnalyzer->analyze(NULL)\n#17 /app/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(576): Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure}(2, '/app/app/Models...')\n#18 /app/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php(266): Psalm\Internal\Codebase\Analyzer->doAnalysis(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1)\n#19 /app/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php(1203): Psalm\Internal\Codebase\Analyzer->analyzeFiles(Object(Psalm\Internal\Analyzer\ProjectAnalyzer), 1, false, false)\n#20 /app/vendor/vimeo/psalm/src/psalm.php(604): Psalm\Internal\Analyzer\ProjectAnalyzer->checkPaths(Array)\n#21 /app/vendor/vimeo/psalm/psalm(2): require_once('/app/vendor/vim...')\n#22 {main}\n"]

"Could not get class storage for illuminate\support\facades\broadcast" when specifying a file path

If I run psalm with a file specified, the following error is thrown:

psalm app/Context/Product/Actions/UpdateProductComplianceCardAction.php
Scanning files...

   InvalidArgumentException

  Could not get class storage for illuminate\support\facades\broadcast

  at vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php:45
    41|     public function get($fq_classlike_name)
    42|     {
    43|         $fq_classlike_name_lc = strtolower($fq_classlike_name);
    44|         if (!isset(self::$storage[$fq_classlike_name_lc])) {
  > 45|             throw new \InvalidArgumentException('Could not get class storage for ' . $fq_classlike_name_lc);
    46|         }
    47|
    48|         return self::$storage[$fq_classlike_name_lc];
    49|     }

Running psalm on the whole codebase succeeds.

`first` method of BelongsToMany has wrong return type

Describe the bug
the first method of BelongsToMany has wrong return type. It can return model or null, but described only model. Also the method has some code in body.
And on checking I get that error:

INFO: RedundantConditionGivenDocblockType - app\User.php:83:16 - Docblock-defined type TRelatedModel can never contain null (see https://psalm.dev/156)
            && null !== $this->roles()->where('name', $role)->first();

Impacted Versions
barryvdh/laravel-ide-helper v2.8.0 Laravel IDE Helper, generates correct PHPDocs for all Facad...
laravel/framework v5.8.38 The Laravel Framework.
laravel/tinker v1.0.10 Powerful REPL for the Laravel framework.
matt-allan/laravel-code-style 0.5.1 Code formatting for Laravel projects
psalm/plugin-laravel v1.4.0 A Laravel plugin for Psalm
vimeo/psalm 3.17.2 A static analysis tool for finding errors in PHP applications

Additional context
I think problem is here:

* @psalm-return TRelatedModel

It should be

     * @psalm-return TRelatedModel|null

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.