diff --git a/psalm.xml.dist b/psalm.xml.dist index 1fdcb9cb4d3..5ca67684de5 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -86,6 +86,7 @@ + diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 493f5218d21..e3d7753d389 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -750,8 +750,6 @@ public function initializePlugins(ProjectChecker $project_checker) } } - $codebase = $project_checker->codebase; - foreach ($this->filetype_scanner_paths as $extension => $path) { $fq_class_name = $this->getPluginClassForPath($project_checker, $path, 'Psalm\\Scanner\\FileScanner'); @@ -771,33 +769,12 @@ public function initializePlugins(ProjectChecker $project_checker) } foreach ($this->plugin_paths as $path) { - $fq_class_name = $this->getPluginClassForPath($project_checker, $path, 'Psalm\\Plugin'); - - /** @psalm-suppress UnresolvableInclude */ - require_once($path); - - if ($codebase->methods->methodExists($fq_class_name . '::afterMethodCallCheck')) { - $this->after_method_checks[$fq_class_name] = $fq_class_name; - } - - if ($codebase->methods->methodExists($fq_class_name . '::afterFunctionCallCheck')) { - $this->after_function_checks[$fq_class_name] = $fq_class_name; - } - - if ($codebase->methods->methodExists($fq_class_name . '::afterExpressionCheck')) { - $this->after_expression_checks[$fq_class_name] = $fq_class_name; - } - - if ($codebase->methods->methodExists($fq_class_name . '::afterStatementCheck')) { - $this->after_statement_checks[$fq_class_name] = $fq_class_name; - } - - if ($codebase->methods->methodExists($fq_class_name . '::afterClassLikeExistsCheck')) { - $this->after_classlike_exists_checks[$fq_class_name] = $fq_class_name; - } - - if ($codebase->methods->methodExists($fq_class_name . '::afterVisitClassLike')) { - $this->after_visit_classlikes[$fq_class_name] = $fq_class_name; + try { + $plugin_object = new LegacyPlugin($path, $this, $project_checker); + $plugin_object($facade); + } catch (\Throwable $e) { + // todo: ??? + throw $e; } } } diff --git a/src/Psalm/LegacyPlugin.php b/src/Psalm/LegacyPlugin.php new file mode 100644 index 00000000000..4ef0e5e82e1 --- /dev/null +++ b/src/Psalm/LegacyPlugin.php @@ -0,0 +1,99 @@ +path = $path; + $this->config = $config; + $this->project_checker = $project_checker; + } + + public function __invoke(PluginApi\RegistrationInterface $api): void + { + $codebase = $this->project_checker->codebase; + $fq_class_name = $this->getPluginClassForPath($this->path, Plugin::class); + + /** @psalm-suppress UnresolvableInclude */ + require_once($this->path); + + if ($codebase->methods->methodExists($fq_class_name . '::afterMethodCallCheck')) { + $this->config->after_method_checks[$fq_class_name] = $fq_class_name; + } + + if ($codebase->methods->methodExists($fq_class_name . '::afterFunctionCallCheck')) { + $this->config->after_function_checks[$fq_class_name] = $fq_class_name; + } + + if ($codebase->methods->methodExists($fq_class_name . '::afterExpressionCheck')) { + $this->config->after_expression_checks[$fq_class_name] = $fq_class_name; + } + + if ($codebase->methods->methodExists($fq_class_name . '::afterStatementCheck')) { + $this->config->after_statement_checks[$fq_class_name] = $fq_class_name; + } + + if ($codebase->methods->methodExists($fq_class_name . '::afterClassLikeExistsCheck')) { + $this->config->after_classlike_exists_checks[$fq_class_name] = $fq_class_name; + } + + if ($codebase->methods->methodExists($fq_class_name . '::afterVisitClassLike')) { + $this->config->after_visit_classlikes[$fq_class_name] = $fq_class_name; + } + } + + /** + * @param string $path + * @param string $must_extend + * + * @return string + */ + private function getPluginClassForPath(string $path, string $must_extend) + { + $codebase = $this->project_checker->codebase; + + $file_storage = $codebase->createFileStorageForPath($path); + $file_to_scan = new FileScanner($path, $this->config->shortenFileName($path), true); + $file_to_scan->scan( + $codebase, + $file_storage + ); + + $declared_classes = ClassLikeChecker::getClassesForFile($this->project_checker, $path); + + if (count($declared_classes) !== 1) { + throw new \InvalidArgumentException( + 'Plugins must have exactly one class in the file - ' . $path . ' has ' . + count($declared_classes) + ); + } + + $fq_class_name = reset($declared_classes); + + if (!$codebase->classExtends( + $fq_class_name, + $must_extend + ) + ) { + throw new \InvalidArgumentException( + 'This plugin must extend ' . $must_extend . ' - ' . $path . ' does not' + ); + } + + return $fq_class_name; + } +} diff --git a/src/Psalm/PluginApi/RegistrationInterface.php b/src/Psalm/PluginApi/RegistrationInterface.php new file mode 100644 index 00000000000..0592e0d4e99 --- /dev/null +++ b/src/Psalm/PluginApi/RegistrationInterface.php @@ -0,0 +1,7 @@ +