diff --git a/rules.neon b/rules.neon index 34f4b3b6..19d56a96 100644 --- a/rules.neon +++ b/rules.neon @@ -12,6 +12,7 @@ rules: - mglaman\PHPStanDrupal\Rules\Drupal\LoadIncludes - mglaman\PHPStanDrupal\Rules\Drupal\EntityQuery\EntityQueryHasAccessCheckRule - mglaman\PHPStanDrupal\Rules\Drupal\TestClassesProtectedPropertyModulesRule + - mglaman\PHPStanDrupal\Rules\Drupal\CacheableDependencyRule conditionalTags: mglaman\PHPStanDrupal\Rules\Drupal\Tests\TestClassSuffixNameRule: diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php new file mode 100644 index 00000000..0e968319 --- /dev/null +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -0,0 +1,48 @@ + + */ +class CacheableDependencyRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Expr\MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $nodeFinder = new NodeFinder(); + $method = $nodeFinder->findFirst($class->stmts, static function (Node $node) { + return $node instanceof Node\Stmt\ClassMethod && $node->name->toString() === 'addCacheableDependency'; + }); + if (!$method instanceof Node\Stmt\ClassMethod) { + return []; + } + + $args = $node->getArgs(); + $traversableArg = $args[0]->value; + $object = $scope->getType($traversableArg); + + // We need to check if isInstanceOf method exists as phpstan returns + // MixedType for unknown objects. + if (method_exists($object, 'isInstanceOf') && $object->isInstanceOf('Drupal\Core\Cache\CacheableDependencyInterface')) { + return []; + } + return [ + 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', + ]; + } +} diff --git a/tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php b/tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php new file mode 100644 index 00000000..ce6d68e7 --- /dev/null +++ b/tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php @@ -0,0 +1,15 @@ +addCacheableDependency($object); + } +} diff --git a/tests/src/Rules/CachableDependencyRuleTest.php b/tests/src/Rules/CachableDependencyRuleTest.php new file mode 100644 index 00000000..9e22fe71 --- /dev/null +++ b/tests/src/Rules/CachableDependencyRuleTest.php @@ -0,0 +1,40 @@ + $errorMessages + */ + public function testRule(string $path, array $errorMessages): void + { + $this->analyse([$path], $errorMessages); + } + + public static function resultData(): \Generator + { + yield [ + __DIR__ . '/../../fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php', + [ + [ + 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', + 07 + ], + ] + ]; + + } + + +}