From cff155b0d3ce8d5bddd97340ed939e8697a4c334 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Sat, 7 Feb 2026 14:32:53 +0100 Subject: [PATCH] Replace psalm with larastan (#1755) * Use larastan instead of psalm * Fix cs * Fix baseline --- .github/workflows/run-static-analysis.yml | 2 +- .gitignore | 2 +- .php-cs-fixer.dist.php | 2 +- composer.json | 7 ++- config/ide-helper.php | 14 ----- phpstan-baseline.neon | 67 +++++++++++++++++++++++ phpstan.neon | 11 ++++ psalm-baseline.xml | 24 -------- psalm.xml | 16 ------ resources/views/helper.php | 10 +--- resources/views/meta.php | 25 +++++---- src/Alias.php | 7 +-- src/Console/MetaCommand.php | 5 -- src/Console/ModelsCommand.php | 3 +- src/Factories.php | 35 ------------ src/Generator.php | 3 +- src/Macro.php | 6 +- src/Method.php | 3 +- tests/Factories/AllTest.php | 18 ------ tests/MacroTest.php | 6 +- tests/MethodTest.php | 16 +++--- 21 files changed, 118 insertions(+), 164 deletions(-) create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon delete mode 100644 psalm-baseline.xml delete mode 100644 psalm.xml delete mode 100644 src/Factories.php delete mode 100644 tests/Factories/AllTest.php diff --git a/.github/workflows/run-static-analysis.yml b/.github/workflows/run-static-analysis.yml index 29a1092..dc70e53 100644 --- a/.github/workflows/run-static-analysis.yml +++ b/.github/workflows/run-static-analysis.yml @@ -25,4 +25,4 @@ jobs: run: composer install --prefer-dist --no-progress --no-suggest - name: Analyze - run: composer analyze + run: vendor/bin/phpstan --no-progress --error-format=github diff --git a/.gitignore b/.gitignore index c92a701..0cdbc31 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .phpunit.result.cache - +/build /.idea /.php-cs-fixer.cache /.php-cs-fixer.php diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 41c5d21..f7e5dce 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -3,7 +3,7 @@ require __DIR__ . '/vendor/autoload.php'; $finder = PhpCsFixer\Finder::create() ->in(__DIR__) - ->exclude('tests'); + ->exclude(['tests', 'build']); $config = require __DIR__ . '/.php-cs-fixer.common.php'; diff --git a/composer.json b/composer.json index d35ddd6..af3686f 100644 --- a/composer.json +++ b/composer.json @@ -35,11 +35,12 @@ "friendsofphp/php-cs-fixer": "^3", "illuminate/config": "^11.15 || ^12", "illuminate/view": "^11.15 || ^12", + "larastan/larastan": "^3.1", "mockery/mockery": "^1.4", "orchestra/testbench": "^9.2 || ^10", + "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^10.5 || ^11.5.3", "spatie/phpunit-snapshot-assertions": "^4 || ^5", - "vimeo/psalm": "^5.4", "vlucas/phpdotenv": "^5" }, "suggest": { @@ -74,7 +75,7 @@ } }, "scripts": { - "analyze": "psalm", + "analyze": "phpstan", "check-style": [ "php-cs-fixer fix --diff --diff-format=udiff --dry-run", "php-cs-fixer fix --diff --diff-format=udiff --dry-run --config=.php_cs.tests.php" @@ -83,7 +84,7 @@ "php-cs-fixer fix", "php-cs-fixer fix --config=.php-cs-fixer.tests.php" ], - "psalm-set-baseline": "psalm --set-baseline=psalm-baseline.xml", + "analyze-set-baseline": "phpstan --generate-baseline", "test": "phpunit", "test-ci": "phpunit -d --without-creating-snapshots", "test-regenerate": "phpunit -d --update-snapshots" diff --git a/config/ide-helper.php b/config/ide-helper.php index 4b9faea..39040b3 100644 --- a/config/ide-helper.php +++ b/config/ide-helper.php @@ -47,20 +47,6 @@ return [ 'include_fluent' => false, - /* - |-------------------------------------------------------------------------- - | Factory builders - |-------------------------------------------------------------------------- - | - | Set to true to generate factory generators for better factory() - | method auto-completion. - | - | Deprecated for Laravel 8 or latest. - | - */ - - 'include_factory_builders' => false, - /* |-------------------------------------------------------------------------- | Write model magic methods diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..6c648c1 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,67 @@ +parameters: + ignoreErrors: + - + message: '#^File ends with a trailing whitespace\. This may cause problems when running the code in the web browser\. Remove the closing \?\> mark or remove the whitespace\.$#' + identifier: whitespace.fileEnd + count: 1 + path: resources/views/helper.php + + - + message: '#^Call to an undefined method Illuminate\\Contracts\\Filesystem\\Filesystem\:\:requireOnce\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/MetaCommand.php + + - + message: '#^Call to an undefined method Illuminate\\Contracts\\Foundation\\Application\:\:getBindings\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/MetaCommand.php + + - + message: '#^Call to an undefined method Barryvdh\\Reflection\\DocBlock\\Tag\:\:getMethodName\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/ModelsCommand.php + + - + message: '#^Call to an undefined method Barryvdh\\Reflection\\DocBlock\\Tag\:\:getType\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/ModelsCommand.php + + - + message: '#^Call to an undefined method Barryvdh\\Reflection\\DocBlock\\Tag\:\:getVariableName\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/ModelsCommand.php + + - + message: '#^Call to an undefined method Illuminate\\Support\\Optional\:\:getNumberOfParameters\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/ModelsCommand.php + + - + message: '#^Call to an undefined method Illuminate\\Support\\Optional\:\:getParameters\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/ModelsCommand.php + + - + message: '#^Call to an undefined method Illuminate\\Support\\Optional\:\:getReturnType\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Console/ModelsCommand.php + + - + message: '#^Call to an undefined static method Illuminate\\Database\\Eloquent\\Model\:\:newFactory\(\)\.$#' + identifier: staticMethod.notFound + count: 2 + path: src/Console/ModelsCommand.php + + - + message: '#^Call to an undefined method ReflectionFunctionAbstract\:\:getDeclaringClass\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Method.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..df64fc2 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,11 @@ +includes: + - phpstan-baseline.neon + - vendor/phpstan/phpstan-phpunit/extension.neon + +parameters: + level: 2 + paths: + - src + - resources/views + excludePaths: + diff --git a/psalm-baseline.xml b/psalm-baseline.xml deleted file mode 100644 index 84de223..0000000 --- a/psalm-baseline.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Factory - Factory - - - - - \Auth - \Cache - \Cache - \DB - \Queue - \Storage - - - - - \UnitEnum - - - diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 86fd1f4..0000000 --- a/psalm.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/resources/views/helper.php b/resources/views/helper.php index 575af25..84fd42e 100644 --- a/resources/views/helper.php +++ b/resources/views/helper.php @@ -3,6 +3,7 @@ /** * @var Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_alias_ns * @var Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_extends_ns + * @var string[] $real_time_facades * @var bool $include_fluent * @var string $helpers */ @@ -119,12 +120,3 @@ namespace Illuminate\Support { } - -namespace getNamespaceName()?> { - /** - * @method \Illuminate\Database\Eloquent\Collection|getShortName()?>[]|getShortName()?> create($attributes = []) - * @method \Illuminate\Database\Eloquent\Collection|getShortName()?>[]|getShortName()?> make($attributes = []) - */ - class getShortName()?>FactoryBuilder extends \Illuminate\Database\Eloquent\FactoryBuilder {} -} - diff --git a/resources/views/meta.php b/resources/views/meta.php index 54854fb..897c06a 100644 --- a/resources/views/meta.php +++ b/resources/views/meta.php @@ -1,4 +1,16 @@ + $expectedArgumentSets + * @var array $expectedArguments + * @var string[] $userMethods + * @var string $userModel/ + * */ +?> /* @noinspection ALL */ // @formatter:off @@ -35,15 +47,6 @@ namespace PHPSTORM_META { ])); - - override(\factory(0), map([ - '' => '@FactoryBuilder', - - 'getName() ?>' => \getName() ?>FactoryBuilder::class, - - ])); - - override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::mock(0), map(["" => "@&\Mockery\MockInterface"])); override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::partialMock(0), map(["" => "@&\Mockery\MockInterface"])); override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::instance(0), type(1)); @@ -79,7 +82,7 @@ namespace PHPSTORM_META { override(\tap(0), type(0)); override(\optional(0), type(0)); - + $argumentsList) : ?> registerArgumentsSet('', $arg) : ?> - + facade; @@ -386,7 +385,6 @@ class Alias if ($class !== $this->root) { $this->methods[] = new Method( $method, - $this->alias, $class, $magic, $this->interfaces, @@ -403,7 +401,6 @@ class Alias /** * Get the methods for one or multiple classes. * - * @return string */ protected function detectMethods() { @@ -419,7 +416,6 @@ class Alias if ($this->extends !== $class && substr($method->name, 0, 2) !== '__') { $this->methods[] = new Method( $method, - $this->alias, $reflection, $method->name, $this->interfaces, @@ -449,7 +445,6 @@ class Alias // Add macros $this->methods[] = new Macro( $method, - $this->alias, $reflection, $macro_name, $this->interfaces, diff --git a/src/Console/MetaCommand.php b/src/Console/MetaCommand.php index 02c8341..fe5189e 100644 --- a/src/Console/MetaCommand.php +++ b/src/Console/MetaCommand.php @@ -11,7 +11,6 @@ namespace Barryvdh\LaravelIdeHelper\Console; -use Barryvdh\LaravelIdeHelper\Factories; use Dotenv\Parser\Entry; use Dotenv\Parser\Parser; use Illuminate\Console\Command; @@ -108,9 +107,6 @@ class MetaCommand extends Command */ public function handle() { - // Needs to run before exception handler is registered - $factories = $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : []; - $ourAutoloader = $this->registerClassAutoloadExceptions(); $bindings = []; @@ -149,7 +145,6 @@ class MetaCommand extends Command $content = $this->view->make('ide-helper::meta', [ 'bindings' => $bindings, 'methods' => $this->methods, - 'factories' => $factories, 'configMethods' => $this->configMethods, 'configValues' => $configValues, 'expectedArgumentSets' => $this->getExpectedArgumentSets(), diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php index c140e7b..a09aeda 100644 --- a/src/Console/ModelsCommand.php +++ b/src/Console/ModelsCommand.php @@ -1133,6 +1133,7 @@ class ModelsCommand extends Command $serializer = new DocBlockSerializer(); $docComment = $serializer->getDocComment($phpdoc); + $mixinClassName = null; if ($this->write_mixin) { $phpdocMixin = new DocBlock($reflection, new Context($namespace)); @@ -1680,7 +1681,7 @@ class ModelsCommand extends Command preg_match( '/@param ((?:(?:[\w?|\\\\<>])+(?:\[])?)+)/', - $docComment ?? '', + $docComment, $matches ); $type = $matches[1] ?? ''; diff --git a/src/Factories.php b/src/Factories.php deleted file mode 100644 index d156c01..0000000 --- a/src/Factories.php +++ /dev/null @@ -1,35 +0,0 @@ -getProperty('definitions'); - - foreach ($definitions->getValue($factory) as $factory_target => $config) { - try { - $factories[] = new ReflectionClass($factory_target); - } catch (Exception $exception) { - } - } - } - - return $factories; - } - - protected static function isLaravelSevenOrLower() - { - return class_exists('Illuminate\Database\Eloquent\Factory'); - } -} diff --git a/src/Generator.php b/src/Generator.php index 87ba1b9..d91fa2f 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -74,7 +74,7 @@ class Generator /** * Generate the helper file contents; * - * @return string; + * @return string */ public function generate() { @@ -85,7 +85,6 @@ class Generator ->with('real_time_facades', $this->getRealTimeFacades()) ->with('helpers', $this->detectHelpers()) ->with('include_fluent', $this->config->get('ide-helper.include_fluent', true)) - ->with('factories', $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : []) ->render(); } diff --git a/src/Macro.php b/src/Macro.php index 2213fa6..a97c52d 100644 --- a/src/Macro.php +++ b/src/Macro.php @@ -15,7 +15,6 @@ class Macro extends Method * Macro constructor. * * @param \ReflectionFunctionAbstract $method - * @param string $alias * @param \ReflectionClass $class * @param null $methodName * @param array $interfaces @@ -24,14 +23,13 @@ class Macro extends Method */ public function __construct( $method, - $alias, $class, $methodName = null, $interfaces = [], $classAliases = [], $returnTypeNormalizers = [] ) { - parent::__construct($method, $alias, $class, $methodName, $interfaces, $classAliases, $returnTypeNormalizers); + parent::__construct($method, $class, $methodName, $interfaces, $classAliases, $returnTypeNormalizers); } public static function setDefaultReturnTypes(array $map = []) @@ -75,8 +73,8 @@ class Macro extends Method $type = $this->concatReflectionTypes($return); - /** @psalm-suppress UndefinedClass */ if (!$return instanceof \ReflectionUnionType) { + /** @phpstan-ignore method.notFound */ $type .= $this->root === "\\{$builder}" && $return->getName() === $builder ? '|static' : ''; $type .= $return->allowsNull() ? '|null' : ''; } diff --git a/src/Method.php b/src/Method.php index 3036786..9f18129 100644 --- a/src/Method.php +++ b/src/Method.php @@ -44,7 +44,6 @@ class Method /** * @param \ReflectionMethod|\ReflectionFunctionAbstract $method - * @param string $alias * @param \ReflectionClass $class * @param string|null $methodName * @param array $interfaces @@ -52,7 +51,7 @@ class Method * @param array $returnTypeNormalizers * @param string[] $templateNames */ - public function __construct($method, $alias, $class, $methodName = null, $interfaces = [], array $classAliases = [], array $returnTypeNormalizers = [], array $templateNames = []) + public function __construct($method, $class, $methodName = null, $interfaces = [], array $classAliases = [], array $returnTypeNormalizers = [], array $templateNames = []) { $this->method = $method; $this->interfaces = $interfaces; diff --git a/tests/Factories/AllTest.php b/tests/Factories/AllTest.php deleted file mode 100644 index f2608b5..0000000 --- a/tests/Factories/AllTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertInstanceOf(Macro::class, $macro); } @@ -296,7 +296,7 @@ class MacroTest extends TestCase { $reflectionMethod = new \ReflectionMethod(UrlGeneratorMacroClass::class, '__invoke'); - $macro = new Macro($reflectionMethod, 'URL', new ReflectionClass(UrlGenerator::class), 'macroName'); + $macro = new Macro($reflectionMethod, new ReflectionClass(UrlGenerator::class), 'macroName'); $output = <<<'DOC' /** * @param string $foo @@ -331,7 +331,7 @@ class MacroMock extends Macro public function getPhpDoc(ReflectionFunctionAbstract $method, ?ReflectionClass $class = null): DocBlock { - return (new Macro($method, '', $class ?? $method->getClosureScopeClass()))->phpdoc; + return (new Macro($method, $class ?? $method->getClosureScopeClass()))->phpdoc; } } diff --git a/tests/MethodTest.php b/tests/MethodTest.php index 4821e44..0141eb5 100644 --- a/tests/MethodTest.php +++ b/tests/MethodTest.php @@ -19,7 +19,7 @@ class MethodTest extends TestCase $reflectionClass = new \ReflectionClass(ExampleClass::class); $reflectionMethod = $reflectionClass->getMethod('setName'); - $method = new Method($reflectionMethod, 'Example', $reflectionClass); + $method = new Method($reflectionMethod, $reflectionClass); $this->assertInstanceOf(Method::class, $method); } @@ -32,7 +32,7 @@ class MethodTest extends TestCase $reflectionClass = new \ReflectionClass(ExampleClass::class); $reflectionMethod = $reflectionClass->getMethod('setName'); - $method = new Method($reflectionMethod, 'Example', $reflectionClass); + $method = new Method($reflectionMethod, $reflectionClass); $output = <<<'DOC' /** @@ -60,7 +60,7 @@ DOC; $reflectionClass = new \ReflectionClass(EloquentBuilder::class); $reflectionMethod = $reflectionClass->getMethod('upsert'); - $method = new Method($reflectionMethod, 'Builder', $reflectionClass); + $method = new Method($reflectionMethod, $reflectionClass); $output = <<<'DOC' /** @@ -92,7 +92,7 @@ DOC; $reflectionClass = new \ReflectionClass(EloquentBuilder::class); $reflectionMethod = $reflectionClass->getMethod('where'); - $method = new Method($reflectionMethod, 'Builder', $reflectionClass, null, [], [], ['$this' => '\\' . EloquentBuilder::class . '']); + $method = new Method($reflectionMethod, $reflectionClass, null, [], [], ['$this' => '\\' . EloquentBuilder::class . '']); $output = <<<'DOC' /** @@ -123,7 +123,7 @@ DOC; $reflectionClass = new \ReflectionClass(QueryBuilder::class); $reflectionMethod = $reflectionClass->getMethod('whereNull'); - $method = new Method($reflectionMethod, 'Builder', $reflectionClass, null, [], [], ['$this' => '\\' . EloquentBuilder::class . '']); + $method = new Method($reflectionMethod, $reflectionClass, null, [], [], ['$this' => '\\' . EloquentBuilder::class . '']); $output = <<<'DOC' /** @@ -154,7 +154,7 @@ DOC; $reflectionClass = new \ReflectionClass(ExampleClass::class); $reflectionMethod = $reflectionClass->getMethod('setSpecialChars'); - $method = new Method($reflectionMethod, 'Example', $reflectionClass); + $method = new Method($reflectionMethod, $reflectionClass); $this->assertSame('$chars', $method->getParams(true)); $this->assertSame(['$chars'], $method->getParams(false)); $this->assertSame('$chars = \'$\\\'\\\\\'', $method->getParamsWithDefault(true)); @@ -169,7 +169,7 @@ DOC; $reflectionClass = new \ReflectionClass(ExampleClass::class); $reflectionMethod = $reflectionClass->getMethod('getApplication'); - $method = new Method($reflectionMethod, 'Example', $reflectionClass, null, [], [ + $method = new Method($reflectionMethod, $reflectionClass, null, [], [ 'Application' => '\\Illuminate\\Foundation\\Application', ]); @@ -195,7 +195,7 @@ DOC; $reflectionClass = new \ReflectionClass(EloquentBuilder::class); $reflectionMethod = $reflectionClass->getMethod('firstOr'); - $method = new Method($reflectionMethod, 'Builder', $reflectionClass, null, [], [], [], ['TModel']); + $method = new Method($reflectionMethod, $reflectionClass, null, [], [], [], ['TModel']); $output = <<<'DOC' /**