From 642a995a9ca60b057cced243e264e3afbc00ba5e Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Wed, 12 May 2021 14:52:01 -0500 Subject: [PATCH] Handle dynamic file loading with module_load_include Fixes #166 --- extension.neon | 5 ++ src/Rules/Drupal/ModuleLoadInclude.php | 88 +++++++++++++++++++ .../module_load_include_fixture.info.yml | 3 + .../module_load_include_fixture.module | 10 +++ tests/src/DrupalIntegrationTest.php | 6 ++ 5 files changed, 112 insertions(+) create mode 100644 src/Rules/Drupal/ModuleLoadInclude.php create mode 100644 tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.info.yml create mode 100644 tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.module diff --git a/extension.neon b/extension.neon index 398bc7e1..ee77b6f7 100644 --- a/extension.neon +++ b/extension.neon @@ -48,3 +48,8 @@ services: tags: [phpstan.rules.rule] arguments: - %drupal.drupal_root% + - + class: PHPStan\Rules\Drupal\ModuleLoadInclude + tags: [phpstan.rules.rule] + arguments: + - %drupal.drupal_root% diff --git a/src/Rules/Drupal/ModuleLoadInclude.php b/src/Rules/Drupal/ModuleLoadInclude.php new file mode 100644 index 00000000..ae5bd32c --- /dev/null +++ b/src/Rules/Drupal/ModuleLoadInclude.php @@ -0,0 +1,88 @@ +projectRoot = $project_root; + } + + public function getNodeType(): string + { + return Node\Expr\FuncCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + assert($node instanceof Node\Expr\FuncCall); + if (!$node->name instanceof \PhpParser\Node\Name) { + return []; + } + $name = (string) $node->name; + if ($name !== 'module_load_include') { + return []; + } + $stop = null; + + try { + // Try to invoke it similarily as the module handler itself. + $finder = new DrupalFinder(); + $finder->locateRoot($this->projectRoot); + $drupal_root = $finder->getDrupalRoot(); + $extensionDiscovery = new ExtensionDiscovery($drupal_root); + $modules = $extensionDiscovery->scan('module'); + $type_arg = $node->args[0]; + assert($type_arg->value instanceof Node\Scalar\String_); + $module_arg = $node->args[1]; + assert($module_arg->value instanceof Node\Scalar\String_); + $name_arg = $node->args[2] ?? null; + + if ($name_arg === null) { + $name_arg = $module_arg; + } + assert($name_arg->value instanceof Node\Scalar\String_); + + $module_name = $module_arg->value->value; + if (!isset($modules[$module_name])) { + return []; + } + $type_prefix = $name_arg->value->value; + $type_filename = $type_arg->value->value; + $module = $modules[$module_name]; + $file = $drupal_root . '/' . $module->getPath() . "/$type_prefix.$type_filename"; + if (is_file($file)) { + require_once $file; + return []; + } + return [sprintf('File %s could not be loaded from module_load_include', $file)]; + } catch (\Throwable $e) { + return ['A file could not be loaded from module_load_include']; + } + } +} diff --git a/tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.info.yml b/tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.info.yml new file mode 100644 index 00000000..eca1c5c3 --- /dev/null +++ b/tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.info.yml @@ -0,0 +1,3 @@ +type: module +name: Module with tests +core: 8.x diff --git a/tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.module b/tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.module new file mode 100644 index 00000000..75c5b438 --- /dev/null +++ b/tests/fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.module @@ -0,0 +1,10 @@ +assertCount(0, $errors->getErrors(), var_export($errors, TRUE)); $this->assertCount(0, $errors->getInternalErrors(), var_export($errors, TRUE)); } + + public function testModuleLoadInclude() { + $errors = $this->runAnalyze(__DIR__ . '/../fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.module'); + $this->assertCount(0, $errors->getErrors(), var_export($errors, TRUE)); + $this->assertCount(0, $errors->getInternalErrors(), var_export($errors, TRUE)); + } }