Skip to content

Commit e6f5cab

Browse files
authored
Merge pull request #174 from mglaman/166-module_load_include_support
Handle dynamic file loading with module_load_include
2 parents 74e75c4 + 642a995 commit e6f5cab

File tree

5 files changed

+112
-0
lines changed

5 files changed

+112
-0
lines changed

extension.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,8 @@ services:
4848
tags: [phpstan.rules.rule]
4949
arguments:
5050
- %drupal.drupal_root%
51+
-
52+
class: PHPStan\Rules\Drupal\ModuleLoadInclude
53+
tags: [phpstan.rules.rule]
54+
arguments:
55+
- %drupal.drupal_root%
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace PHPStan\Rules\Drupal;
4+
5+
use DrupalFinder\DrupalFinder;
6+
use PhpParser\Node;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Drupal\ExtensionDiscovery;
9+
use PHPStan\Rules\Rule;
10+
11+
/**
12+
* Handles module_load_include dynamic file loading.
13+
*
14+
* @note may become deprecated and removed in D10
15+
* @see https://www.drupal.org/project/drupal/issues/697946
16+
*/
17+
class ModuleLoadInclude implements Rule
18+
{
19+
20+
/**
21+
* The project root.
22+
*
23+
* @var string
24+
*/
25+
protected $projectRoot;
26+
27+
/**
28+
* ModuleLoadInclude constructor.
29+
* @param string $project_root
30+
*/
31+
public function __construct(string $project_root)
32+
{
33+
$this->projectRoot = $project_root;
34+
}
35+
36+
public function getNodeType(): string
37+
{
38+
return Node\Expr\FuncCall::class;
39+
}
40+
41+
public function processNode(Node $node, Scope $scope): array
42+
{
43+
assert($node instanceof Node\Expr\FuncCall);
44+
if (!$node->name instanceof \PhpParser\Node\Name) {
45+
return [];
46+
}
47+
$name = (string) $node->name;
48+
if ($name !== 'module_load_include') {
49+
return [];
50+
}
51+
$stop = null;
52+
53+
try {
54+
// Try to invoke it similarily as the module handler itself.
55+
$finder = new DrupalFinder();
56+
$finder->locateRoot($this->projectRoot);
57+
$drupal_root = $finder->getDrupalRoot();
58+
$extensionDiscovery = new ExtensionDiscovery($drupal_root);
59+
$modules = $extensionDiscovery->scan('module');
60+
$type_arg = $node->args[0];
61+
assert($type_arg->value instanceof Node\Scalar\String_);
62+
$module_arg = $node->args[1];
63+
assert($module_arg->value instanceof Node\Scalar\String_);
64+
$name_arg = $node->args[2] ?? null;
65+
66+
if ($name_arg === null) {
67+
$name_arg = $module_arg;
68+
}
69+
assert($name_arg->value instanceof Node\Scalar\String_);
70+
71+
$module_name = $module_arg->value->value;
72+
if (!isset($modules[$module_name])) {
73+
return [];
74+
}
75+
$type_prefix = $name_arg->value->value;
76+
$type_filename = $type_arg->value->value;
77+
$module = $modules[$module_name];
78+
$file = $drupal_root . '/' . $module->getPath() . "/$type_prefix.$type_filename";
79+
if (is_file($file)) {
80+
require_once $file;
81+
return [];
82+
}
83+
return [sprintf('File %s could not be loaded from module_load_include', $file)];
84+
} catch (\Throwable $e) {
85+
return ['A file could not be loaded from module_load_include'];
86+
}
87+
}
88+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
type: module
2+
name: Module with tests
3+
core: 8.x
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php declare(strict_types=1);
2+
3+
function test_locale_translation_inc_included() {
4+
module_load_include('bulk.inc', 'locale');
5+
locale_translate_get_interface_translation_files();
6+
module_load_include('compare.inc', 'locale');
7+
locale_translation_build_projects();
8+
module_load_include('translation.inc', 'locale');
9+
locale_translation_build_sources();
10+
}

tests/src/DrupalIntegrationTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,10 @@ public function testThemeSettingsFile() {
119119
$this->assertCount(0, $errors->getErrors(), var_export($errors, TRUE));
120120
$this->assertCount(0, $errors->getInternalErrors(), var_export($errors, TRUE));
121121
}
122+
123+
public function testModuleLoadInclude() {
124+
$errors = $this->runAnalyze(__DIR__ . '/../fixtures/drupal/modules/module_load_include_fixture/module_load_include_fixture.module');
125+
$this->assertCount(0, $errors->getErrors(), var_export($errors, TRUE));
126+
$this->assertCount(0, $errors->getInternalErrors(), var_export($errors, TRUE));
127+
}
122128
}

0 commit comments

Comments
 (0)