Skip to content

Commit b01f06e

Browse files
committed
Add tests, fix PHPStan errors
1 parent 3aca30d commit b01f06e

File tree

3 files changed

+55
-30
lines changed

3 files changed

+55
-30
lines changed

src/Rules/Drupal/LoadIncludes.php

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Reflection\MethodReflection;
1212
use PHPStan\Rules\Rule;
1313
use PHPStan\ShouldNotHappenException;
14+
use PHPStan\Type\ObjectType;
1415

1516
class LoadIncludes implements Rule
1617
{
@@ -24,8 +25,9 @@ class LoadIncludes implements Rule
2425

2526
/**
2627
* LoadIncludes constructor.
28+
* @param string $project_root
2729
*/
28-
public function __construct($project_root)
30+
public function __construct(string $project_root)
2931
{
3032
$this->projectRoot = $project_root;
3133
}
@@ -38,26 +40,30 @@ public function getNodeType(): string
3840
public function processNode(Node $node, Scope $scope): array
3941
{
4042
assert($node instanceof Node\Expr\MethodCall);
43+
if (!$node->name instanceof Node\Identifier) {
44+
return [];
45+
}
46+
$method_name = $node->name->toString();
47+
if ($method_name !== 'loadInclude') {
48+
return [];
49+
}
50+
$variable = $node->var;
51+
if (!$variable instanceof Node\Expr\Variable) {
52+
return [];
53+
}
54+
$var_name = $variable->name;
55+
if (!is_string($var_name)) {
56+
throw new ShouldNotHappenException(sprintf('Expected string for variable in %s, please open an issue on GitHub https://github.com/mglaman/phpstan-drupal/issues', get_called_class()));
57+
}
58+
$type = $scope->getVariableType($var_name);
59+
assert($type instanceof ObjectType);
60+
if (!class_exists($type->getClassName()) && !interface_exists($type->getClassName())) {
61+
throw new ShouldNotHappenException(sprintf('Could not find class for %s from reflection.', get_called_class()));
62+
}
4163

4264
try {
43-
$method_name = $node->name;
44-
if ($method_name instanceof Node\Identifier) {
45-
$method_name = $method_name->name;
46-
}
47-
if ($method_name !== 'loadInclude') {
48-
return [];
49-
}
50-
$var_name = $node->var->name;
51-
if ($var_name instanceof Node\Identifier) {
52-
$var_name = $var_name->name;
53-
}
54-
if (!$var_name) {
55-
return [];
56-
}
57-
$type = $scope->getVariableType($var_name);
5865
$reflected = new \ReflectionClass($type->getClassName());
59-
$implements = $reflected->implementsInterface(ModuleHandlerInterface::class);
60-
if (!$implements) {
66+
if (!$reflected->implementsInterface(ModuleHandlerInterface::class)) {
6167
return [];
6268
}
6369
// Try to invoke it similarily as the module handler itself.
@@ -66,26 +72,32 @@ public function processNode(Node $node, Scope $scope): array
6672
$drupal_root = $finder->getDrupalRoot();
6773
$extensionDiscovery = new ExtensionDiscovery($drupal_root);
6874
$modules = $extensionDiscovery->scan('module');
69-
$module_arg = $node->args[0]->value->value;
70-
$type_arg = $node->args[1]->value->value;
71-
$name_arg = !empty($node->args[2]) ? $node->args[2] : null;
72-
if (!$name_arg) {
75+
$module_arg = $node->args[0];
76+
assert($module_arg->value instanceof Node\Scalar\String_);
77+
$type_arg = $node->args[1];
78+
assert($type_arg->value instanceof Node\Scalar\String_);
79+
$name_arg = $node->args[2] ?? null;
80+
81+
if ($name_arg === null) {
7382
$name_arg = $module_arg;
74-
} else {
75-
$name_arg = $name_arg->value->value;
7683
}
77-
if (empty($modules[$module_arg])) {
84+
assert($name_arg->value instanceof Node\Scalar\String_);
85+
86+
$module_name = $module_arg->value->value;
87+
if (!isset($modules[$module_name])) {
7888
return [];
7989
}
80-
/** @var \PHPStan\Drupal\Extension $module */
81-
$module = $modules[$module_arg];
82-
$file = $drupal_root . '/' . $module->getPath() . "/$name_arg.$type_arg";
90+
$type_prefix = $name_arg->value->value;
91+
$type_filename = $type_arg->value->value;
92+
$module = $modules[$module_name];
93+
$file = $drupal_root . '/' . $module->getPath() . "/$type_prefix.$type_filename";
8394
if (is_file($file)) {
8495
require_once $file;
8596
return [];
8697
}
87-
return ['File could not be loaded from ModuleHandler::loadInclude'];
98+
return [sprintf('File %s could not be loaded from %s::loadInclude', $file, $type->getClassName())];
8899
} catch (\Throwable $e) {
100+
return [sprintf('A file could not be loaded from %s::loadInclude', $type->getClassName())];
89101
}
90102

91103
return [];

tests/fixtures/drupal/modules/phpstan_fixtures/phpstan_fixtures.module

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,14 @@ function phpstan_fixtures_get_app_root(): string {
1616
$app_root = \Drupal::getContainer()->get('app.root');
1717
return $app_root . '/core/includes/install.inc';
1818
}
19+
20+
function phpstan_fixtures_module_load_includes_test(): array {
21+
$module_handler = \Drupal::moduleHandler();
22+
$module_handler->loadInclude('locale', 'fetch.inc');
23+
return _locale_translation_default_update_options();
24+
}
25+
26+
function phpstan_fixtures_module_load_includes_negative_test(): void {
27+
$module_handler = \Drupal::moduleHandler();
28+
$module_handler->loadInclude('phpstan_fixtures', 'fetch.inc');
29+
}

tests/src/DrupalIntegrationTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,16 @@ public function testDrupalTestInChildSiteContant() {
3838

3939
public function testExtensionReportsError() {
4040
$errors = $this->runAnalyze(__DIR__ . '/../fixtures/drupal/modules/phpstan_fixtures/phpstan_fixtures.module');
41-
$this->assertCount(2, $errors->getErrors(), var_export($errors, true));
41+
$this->assertCount(3, $errors->getErrors(), var_export($errors, true));
4242
$this->assertCount(0, $errors->getInternalErrors(), var_export($errors, true));
4343

4444
$errors = $errors->getErrors();
4545
$error = array_shift($errors);
4646
$this->assertEquals('If condition is always false.', $error->getMessage());
4747
$error = array_shift($errors);
4848
$this->assertEquals('Function phpstan_fixtures_MissingReturnRule() should return string but return statement is missing.', $error->getMessage());
49+
$error = array_shift($errors);
50+
$this->assertStringContainsString('phpstan_fixtures/phpstan_fixtures.fetch.inc could not be loaded from Drupal\\Core\\Extension\\ModuleHandlerInterface::loadInclude', $error->getMessage());
4951
}
5052

5153
public function testExtensionTestSuiteAutoloading() {

0 commit comments

Comments
 (0)