diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..afab3a7c --- /dev/null +++ b/docs/README.md @@ -0,0 +1,37 @@ +# About plugin + +The plugin helps Psalm to understand Laravel’s code (which uses a lot of magic) better. +There are 2 main ways how it does it: + - **easy**: by providing stub files (you can find them in `/stubs` dir) + - **medium+**: using custom Handlers (see `/src/Handlers` dir) + + +## How it works + +A single callstack looks like: + +``` +Plugin::__invoke + Providers\ApplicationProvider::bootApp + {instantiate Laravel Application} + Plugin::generateStubFiles + Providers\FacadeStubProvider::generateStubFile + {call `ide-helper:generate` command} // generates "facades.stubphp" + Providers\ModelStubProvider::generateStubFile + Fakes\FakeModelsCommand::run(schema_aggregator(migrations)) + - override parent ModelsCommand::getPropertiesFromTable (extract info from migration files instead using DB connection) + - {call `ide-helper:models --nowrite --reset`} // generates "models.stubphp" + Plugin::registerHandlers + - Container + - Eloquent + - Helpers (that not covered by stubs) + Plugin::registerStubs + - common + - for speficic laravel version + - facades.stubphp (generated) + - models.stubphp (generated) +``` + +## Materials + + - [Authoring Psalm Plugins](https://psalm.dev/docs/running_psalm/plugins/authoring_plugins/) diff --git a/src/Fakes/FakeModelsCommand.php b/src/Fakes/FakeModelsCommand.php index 439496d9..ce4c5181 100644 --- a/src/Fakes/FakeModelsCommand.php +++ b/src/Fakes/FakeModelsCommand.php @@ -12,6 +12,8 @@ use function get_class; use function in_array; use function implode; +use function method_exists; +use function sprintf; /** @psalm-suppress PropertyNotSetInConstructor */ class FakeModelsCommand extends ModelsCommand @@ -35,12 +37,23 @@ public function getModels(): array } /** - * Load the properties from the database table. + * Load Model's properties. + * Overrides {@see \Barryvdh\LaravelIdeHelper\Console\ModelsCommand::getPropertiesFromTable} + * in order to avoid using DB connection and use SchemaAggregator instead. * * @param Model $model */ public function getPropertiesFromTable($model): void { + $is_parent_method_still_exist = method_exists(ModelsCommand::class, __METHOD__); + if (! $is_parent_method_still_exist) { + throw new \BadMethodCallException(sprintf( + 'Method %s::%s() does not exist anymore. Please rename overridden method accordingly.', + ModelsCommand::class, + __METHOD__ + )); + } + $table_name = $model->getTable(); if (!isset($this->schema->tables[$table_name])) { diff --git a/src/Plugin.php b/src/Plugin.php index ca8cddcb..bed2fb1c 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -11,10 +11,7 @@ use Psalm\LaravelPlugin\Handlers\Eloquent\RelationsMethodHandler; use Psalm\LaravelPlugin\Handlers\Helpers\CacheHandler; use Psalm\LaravelPlugin\Handlers\Helpers\PathHandler; -use Psalm\LaravelPlugin\Handlers\Helpers\RedirectHandler; use Psalm\LaravelPlugin\Handlers\Helpers\TransHandler; -use Psalm\LaravelPlugin\Handlers\Helpers\UrlHandler; -use Psalm\LaravelPlugin\Handlers\Helpers\ViewHandler; use Psalm\LaravelPlugin\Handlers\SuppressHandler; use Psalm\LaravelPlugin\Providers\ApplicationProvider; use Psalm\LaravelPlugin\Providers\FacadeStubProvider; @@ -22,15 +19,16 @@ use Psalm\Plugin\PluginEntryPointInterface; use Psalm\Plugin\RegistrationInterface; use SimpleXMLElement; -use Throwable; use function array_merge; use function dirname; +use function fwrite; use function explode; use function glob; /** * @psalm-suppress UnusedClass + * @internal */ class Plugin implements PluginEntryPointInterface { @@ -39,7 +37,8 @@ public function __invoke(RegistrationInterface $registration, ?SimpleXMLElement try { ApplicationProvider::bootApp(); $this->generateStubFiles(); - } catch (Throwable $t) { + } catch (\Throwable $t) { + fwrite(\STDERR, "Laravel plugin error: “{$t->getMessage()}”\n"); return; } @@ -77,9 +76,6 @@ private function registerStubs(RegistrationInterface $registration): void $registration->addStubFile(ModelStubProvider::getStubFileLocation()); } - /** - * @param RegistrationInterface $registration - */ private function registerHandlers(RegistrationInterface $registration): void { require_once 'Handlers/Application/ContainerHandler.php'; diff --git a/src/Providers/ModelStubProvider.php b/src/Providers/ModelStubProvider.php index e23102c6..f5f7c0cd 100644 --- a/src/Providers/ModelStubProvider.php +++ b/src/Providers/ModelStubProvider.php @@ -28,15 +28,14 @@ public static function generateStubFile(): void throw new \RuntimeException('Unsupported Application type.'); } - $migrations_folder = $app->databasePath('migrations/'); + $migrations_directory = $app->databasePath('migrations/'); $project_analyzer = ProjectAnalyzer::getInstance(); $codebase = $project_analyzer->getCodebase(); $schema_aggregator = new SchemaAggregator(); - foreach (glob($migrations_folder . '*.php') as $file) { - //echo $file . "\n"; + foreach (glob($migrations_directory . '*.php') as $file) { $schema_aggregator->addStatements($codebase->getStatementsForFile($file)); }