From 1cb8e83f74aa6582905900dd240679c5ec439b82 Mon Sep 17 00:00:00 2001 From: Mariano D'Agostino Date: Fri, 29 Aug 2025 16:45:36 -0300 Subject: [PATCH 01/10] New rule to detect incorrect use of addCacheableDependency --- rules.neon | 1 + src/Rules/Drupal/CacheableDependencyRule.php | 49 +++++++++++++++++++ .../src/UsesIncorrectCacheableDependency.php | 15 ++++++ .../src/Rules/CachableDependencyRuleTest.php | 40 +++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 src/Rules/Drupal/CacheableDependencyRule.php create mode 100644 tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php create mode 100644 tests/src/Rules/CachableDependencyRuleTest.php 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..d0cbe9ee --- /dev/null +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -0,0 +1,49 @@ + + */ +class CacheableDependencyRule implements Rule { + + /** + * {@inheritdoc} + */ + public function getNodeType(): string { + return MethodCall::class; + } + + /** + * {@inheritdoc} + */ + public function processNode(Node $node, Scope $scope): array { + if (!$node instanceof MethodCall || + !$node->name instanceof Identifier || + $node->name->toString() !== 'addCacheableDependency' + ) { + return []; + } + + $object = $scope->getType($node->args[0]->value); + + // 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 + ], + ] + ]; + + } + + +} From 383c16deff7f14495b6bc037d677e9fdb5281089 Mon Sep 17 00:00:00 2001 From: Mariano D'Agostino Date: Mon, 1 Sep 2025 10:46:37 -0300 Subject: [PATCH 02/10] Fix coding standards --- src/Rules/Drupal/CacheableDependencyRule.php | 58 +++++++++----------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php index d0cbe9ee..c6c215aa 100644 --- a/src/Rules/Drupal/CacheableDependencyRule.php +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -4,46 +4,40 @@ namespace mglaman\PHPStanDrupal\Rules\Drupal; -use PHPStan\Analyser\Scope; -use PHPStan\Rules\Rule; use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Identifier; +use PHPStan\Analyser\Scope; +use PHPStan\Rules\Rule; /** * @implements Rule */ -class CacheableDependencyRule implements Rule { - - /** - * {@inheritdoc} - */ - public function getNodeType(): string { - return MethodCall::class; - } - - /** - * {@inheritdoc} - */ - public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall || - !$node->name instanceof Identifier || - $node->name->toString() !== 'addCacheableDependency' - ) { - return []; - } - - $object = $scope->getType($node->args[0]->value); +class CacheableDependencyRule implements Rule +{ - // 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 []; + public function getNodeType(): string { + return MethodCall::class; } - return [ - 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', - ]; - } + public function processNode(Node $node, Scope $scope): array + { + if (!$node instanceof MethodCall || + !$node->name instanceof Identifier || + $node->name->toString() !== 'addCacheableDependency' + ) { + return []; + } + + $object = $scope->getType($node->args[0]->value); + + // 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.', + ]; + } } - From a70ef94f20bae3d920ee3a8fb57fdb7c957cca2a Mon Sep 17 00:00:00 2001 From: Mariano D'Agostino Date: Mon, 1 Sep 2025 10:57:22 -0300 Subject: [PATCH 03/10] Fix coding standards --- src/Rules/Drupal/CacheableDependencyRule.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php index c6c215aa..036c00c2 100644 --- a/src/Rules/Drupal/CacheableDependencyRule.php +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -16,7 +16,8 @@ class CacheableDependencyRule implements Rule { - public function getNodeType(): string { + public function getNodeType(): string + { return MethodCall::class; } From 769af3a3d0e1bba3248f65cd0f1c279b0b64132f Mon Sep 17 00:00:00 2001 From: Mariano D'Agostino Date: Mon, 1 Sep 2025 11:27:05 -0300 Subject: [PATCH 04/10] Use nodeFinder --- src/Rules/Drupal/CacheableDependencyRule.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php index 036c00c2..0e968319 100644 --- a/src/Rules/Drupal/CacheableDependencyRule.php +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -5,6 +5,7 @@ namespace mglaman\PHPStanDrupal\Rules\Drupal; use PhpParser\Node; +use PhpParser\NodeFinder; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Identifier; use PHPStan\Analyser\Scope; @@ -18,19 +19,22 @@ class CacheableDependencyRule implements Rule public function getNodeType(): string { - return MethodCall::class; + return Node\Expr\MethodCall::class; } public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall || - !$node->name instanceof Identifier || - $node->name->toString() !== 'addCacheableDependency' - ) { + $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 []; } - $object = $scope->getType($node->args[0]->value); + $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. From f38a800d6dab24b694e9384dc6e04690abddc45d Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Thu, 16 Oct 2025 11:14:31 +0200 Subject: [PATCH 05/10] Fixup CacheableDependencyRule --- src/Rules/Drupal/CacheableDependencyRule.php | 20 +++++++++++-------- .../src/Rules/CachableDependencyRuleTest.php | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php index 0e968319..f8f3020f 100644 --- a/src/Rules/Drupal/CacheableDependencyRule.php +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -10,9 +10,10 @@ use PhpParser\Node\Identifier; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; /** - * @implements Rule + * @implements Rule */ class CacheableDependencyRule implements Rule { @@ -24,25 +25,28 @@ public function getNodeType(): string 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) { + if (!$node->name instanceof Identifier || $node->name->toString() !== 'addCacheableDependency') { return []; } $args = $node->getArgs(); + if (count($args) === 0) { + return []; + } + $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')) { + if (method_exists($object, 'isInstanceOf') && $object->isInstanceOf('Drupal\Core\Cache\CacheableDependencyInterface')->yes()) { return []; } + return [ - 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', + RuleErrorBuilder::message('Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.') + ->identifier('cacheable.dependency') + ->build(), ]; } } diff --git a/tests/src/Rules/CachableDependencyRuleTest.php b/tests/src/Rules/CachableDependencyRuleTest.php index 9e22fe71..35148409 100644 --- a/tests/src/Rules/CachableDependencyRuleTest.php +++ b/tests/src/Rules/CachableDependencyRuleTest.php @@ -29,7 +29,7 @@ public static function resultData(): \Generator [ [ 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', - 07 + 13 ], ] ]; From f3e070b7630df333b44d434d0cd162332b59b7c2 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Thu, 16 Oct 2025 12:05:17 +0200 Subject: [PATCH 06/10] Use type check and add test cases --- src/Rules/Drupal/CacheableDependencyRule.php | 14 +++--- .../src/Rules/CachableDependencyRuleTest.php | 13 ++++-- tests/src/Rules/data/cacheable-dependency.php | 46 +++++++++++++++++++ 3 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 tests/src/Rules/data/cacheable-dependency.php diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php index f8f3020f..a0833afe 100644 --- a/src/Rules/Drupal/CacheableDependencyRule.php +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -5,12 +5,11 @@ namespace mglaman\PHPStanDrupal\Rules\Drupal; use PhpParser\Node; -use PhpParser\NodeFinder; -use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Identifier; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectType; /** * @implements Rule @@ -34,12 +33,13 @@ public function processNode(Node $node, Scope $scope): array return []; } - $traversableArg = $args[0]->value; - $object = $scope->getType($traversableArg); + $dependencyArg = $args[0]->value; + $object = $scope->getType($dependencyArg); - // 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')->yes()) { + $interfaceType = new ObjectType('Drupal\Core\Cache\CacheableDependencyInterface'); + $implementsInterface = $interfaceType->isSuperTypeOf($object); + + if (!$implementsInterface->no()) { return []; } diff --git a/tests/src/Rules/CachableDependencyRuleTest.php b/tests/src/Rules/CachableDependencyRuleTest.php index 35148409..26fc648b 100644 --- a/tests/src/Rules/CachableDependencyRuleTest.php +++ b/tests/src/Rules/CachableDependencyRuleTest.php @@ -24,16 +24,23 @@ public function testRule(string $path, array $errorMessages): void public static function resultData(): \Generator { - yield [ - __DIR__ . '/../../fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php', + yield 'all test cases' => [ + __DIR__ . '/data/cacheable-dependency.php', [ [ 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', 13 ], + [ + 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', + 39 + ], + [ + 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', + 43 + ], ] ]; - } diff --git a/tests/src/Rules/data/cacheable-dependency.php b/tests/src/Rules/data/cacheable-dependency.php new file mode 100644 index 00000000..3ef1cb6d --- /dev/null +++ b/tests/src/Rules/data/cacheable-dependency.php @@ -0,0 +1,46 @@ +addCacheableDependency($object); + } +} + +class UsesCorrectCacheableDependency { + public function test() { + $element = []; + $cacheable_metadata = CacheableMetadata::createFromRenderArray($element); + + // This should NOT trigger an error - CacheableMetadata implements CacheableDependencyInterface + $correct_dependency = new CacheableMetadata(); + $cacheable_metadata->addCacheableDependency($correct_dependency); + } +} + +class MultipleCacheableDependencyCalls { + public function test() { + $element = []; + $cacheable_metadata = CacheableMetadata::createFromRenderArray($element); + + // Correct usage + $correct_dependency = new CacheableMetadata(); + $cacheable_metadata->addCacheableDependency($correct_dependency); + + // Incorrect usage - should trigger error + $object = new \StdClass; + $cacheable_metadata->addCacheableDependency($object); + + // Another incorrect usage - should trigger error + $another_object = new \DateTime(); + $cacheable_metadata->addCacheableDependency($another_object); + } +} + From b7218bc71ed71f2661d348f2cd6c3603e1a2f35a Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Thu, 16 Oct 2025 12:21:56 +0200 Subject: [PATCH 07/10] handle different interfaces with addCacheableDependency --- src/Rules/Drupal/CacheableDependencyRule.php | 31 +++++++++++++++++-- .../src/Rules/CachableDependencyRuleTest.php | 10 +++++- tests/src/Rules/data/cacheable-dependency.php | 21 ++++++++++--- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php index a0833afe..a2b25ff0 100644 --- a/src/Rules/Drupal/CacheableDependencyRule.php +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -10,6 +10,10 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; +use Drupal\Core\Cache\RefinableCacheableDependencyInterface; +use Drupal\Core\Cache\CacheableResponseInterface; +use Drupal\Core\Plugin\Context\ContextInterface; +use Drupal\Core\Render\RendererInterface; /** * @implements Rule @@ -28,12 +32,35 @@ public function processNode(Node $node, Scope $scope): array return []; } + $receiverType = $scope->getType($node->var); + + $allowedInterfaces = [ + RefinableCacheableDependencyInterface::class => 0, + CacheableResponseInterface::class => 0, + ContextInterface::class => 0, + RendererInterface::class => 1, + ]; + + $argumentIndex = null; + + foreach ($allowedInterfaces as $interfaceName => $argPosition) { + $interfaceType = new ObjectType($interfaceName); + if (!$interfaceType->isSuperTypeOf($receiverType)->no()) { + $argumentIndex = $argPosition; + break; + } + } + + if ($argumentIndex === null) { + return []; + } + $args = $node->getArgs(); - if (count($args) === 0) { + if (count($args) <= $argumentIndex) { return []; } - $dependencyArg = $args[0]->value; + $dependencyArg = $args[$argumentIndex]->value; $object = $scope->getType($dependencyArg); $interfaceType = new ObjectType('Drupal\Core\Cache\CacheableDependencyInterface'); diff --git a/tests/src/Rules/CachableDependencyRuleTest.php b/tests/src/Rules/CachableDependencyRuleTest.php index 26fc648b..8d6e0882 100644 --- a/tests/src/Rules/CachableDependencyRuleTest.php +++ b/tests/src/Rules/CachableDependencyRuleTest.php @@ -31,13 +31,21 @@ public static function resultData(): \Generator 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', 13 ], + [ + 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', + 36 + ], [ 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', 39 ], [ 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', - 43 + 48 + ], + [ + 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', + 55 ], ] ]; diff --git a/tests/src/Rules/data/cacheable-dependency.php b/tests/src/Rules/data/cacheable-dependency.php index 3ef1cb6d..5964dc0b 100644 --- a/tests/src/Rules/data/cacheable-dependency.php +++ b/tests/src/Rules/data/cacheable-dependency.php @@ -19,7 +19,6 @@ public function test() { $element = []; $cacheable_metadata = CacheableMetadata::createFromRenderArray($element); - // This should NOT trigger an error - CacheableMetadata implements CacheableDependencyInterface $correct_dependency = new CacheableMetadata(); $cacheable_metadata->addCacheableDependency($correct_dependency); } @@ -30,17 +29,31 @@ public function test() { $element = []; $cacheable_metadata = CacheableMetadata::createFromRenderArray($element); - // Correct usage $correct_dependency = new CacheableMetadata(); $cacheable_metadata->addCacheableDependency($correct_dependency); - // Incorrect usage - should trigger error $object = new \StdClass; $cacheable_metadata->addCacheableDependency($object); - // Another incorrect usage - should trigger error $another_object = new \DateTime(); $cacheable_metadata->addCacheableDependency($another_object); } } +class RendererInterfaceTestCase { + public function testCorrectUsage(\Drupal\Core\Render\RendererInterface $renderer) { + $elements = []; + + $correct_dependency = new CacheableMetadata(); + $renderer->addCacheableDependency($elements, $correct_dependency); + } + + public function testIncorrectUsage(\Drupal\Core\Render\RendererInterface $renderer) { + $elements = []; + + $object = new \StdClass; + $renderer->addCacheableDependency($elements, $object); + } +} + + From 9b7d4e9a764dc77335070b99f963c6c7192d3174 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Thu, 16 Oct 2025 13:02:14 +0200 Subject: [PATCH 08/10] make sure the rule is behind a feature flag --- bleedingEdge.neon | 1 + extension.neon | 2 ++ rules.neon | 5 ++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/bleedingEdge.neon b/bleedingEdge.neon index 4690264c..9d0c6cfb 100644 --- a/bleedingEdge.neon +++ b/bleedingEdge.neon @@ -8,3 +8,4 @@ parameters: testClassSuffixNameRule: true dependencySerializationTraitPropertyRule: true accessResultConditionRule: true + cacheableDependencyRule: true diff --git a/extension.neon b/extension.neon index 8cd6318a..6a2e15c4 100644 --- a/extension.neon +++ b/extension.neon @@ -33,6 +33,7 @@ parameters: accessResultConditionRule: false classExtendsInternalClassRule: true pluginManagerInspectionRule: false + cacheableDependencyRule: false entityMapping: aggregator_feed: class: Drupal\aggregator\Entity\Feed @@ -267,6 +268,7 @@ parametersSchema: accessResultConditionRule: boolean() classExtendsInternalClassRule: boolean() pluginManagerInspectionRule: boolean() + cacheableDependencyRule: boolean() ]) entityMapping: arrayOf(anyOf( structure([ diff --git a/rules.neon b/rules.neon index 19d56a96..d435567a 100644 --- a/rules.neon +++ b/rules.neon @@ -12,7 +12,6 @@ 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: @@ -25,6 +24,8 @@ conditionalTags: phpstan.rules.rule: %drupal.rules.classExtendsInternalClassRule% mglaman\PHPStanDrupal\Rules\Classes\PluginManagerInspectionRule: phpstan.rules.rule: %drupal.rules.pluginManagerInspectionRule% + mglaman\PHPStanDrupal\Rules\Drupal\CacheableDependencyRule: + phpstan.rules.rule: %drupal.rules.cacheableDependencyRule% services: - @@ -39,3 +40,5 @@ services: class: mglaman\PHPStanDrupal\Rules\Classes\ClassExtendsInternalClassRule - class: mglaman\PHPStanDrupal\Rules\Classes\PluginManagerInspectionRule + - + class: mglaman\PHPStanDrupal\Rules\Drupal\CacheableDependencyRule From 046448e5e0271f0c3aba1d180f9dc71ebc35d38f Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Thu, 16 Oct 2025 14:01:52 +0200 Subject: [PATCH 09/10] fix a bug on renderer check --- src/Rules/Drupal/CacheableDependencyRule.php | 14 +++++++------- tests/src/Rules/CachableDependencyRuleTest.php | 4 ---- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Rules/Drupal/CacheableDependencyRule.php b/src/Rules/Drupal/CacheableDependencyRule.php index a2b25ff0..74e711bd 100644 --- a/src/Rules/Drupal/CacheableDependencyRule.php +++ b/src/Rules/Drupal/CacheableDependencyRule.php @@ -4,16 +4,17 @@ namespace mglaman\PHPStanDrupal\Rules\Drupal; +use Drupal\Core\Cache\CacheableDependencyInterface; +use Drupal\Core\Cache\CacheableResponseInterface; +use Drupal\Core\Cache\RefinableCacheableDependencyInterface; +use Drupal\Core\Plugin\Context\ContextInterface; +use Drupal\Core\Render\RendererInterface; use PhpParser\Node; use PhpParser\Node\Identifier; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; -use Drupal\Core\Cache\RefinableCacheableDependencyInterface; -use Drupal\Core\Cache\CacheableResponseInterface; -use Drupal\Core\Plugin\Context\ContextInterface; -use Drupal\Core\Render\RendererInterface; /** * @implements Rule @@ -42,10 +43,9 @@ public function processNode(Node $node, Scope $scope): array ]; $argumentIndex = null; - foreach ($allowedInterfaces as $interfaceName => $argPosition) { $interfaceType = new ObjectType($interfaceName); - if (!$interfaceType->isSuperTypeOf($receiverType)->no()) { + if ($interfaceType->isSuperTypeOf($receiverType)->yes()) { $argumentIndex = $argPosition; break; } @@ -63,7 +63,7 @@ public function processNode(Node $node, Scope $scope): array $dependencyArg = $args[$argumentIndex]->value; $object = $scope->getType($dependencyArg); - $interfaceType = new ObjectType('Drupal\Core\Cache\CacheableDependencyInterface'); + $interfaceType = new ObjectType(CacheableDependencyInterface::class); $implementsInterface = $interfaceType->isSuperTypeOf($object); if (!$implementsInterface->no()) { diff --git a/tests/src/Rules/CachableDependencyRuleTest.php b/tests/src/Rules/CachableDependencyRuleTest.php index 8d6e0882..d4fef310 100644 --- a/tests/src/Rules/CachableDependencyRuleTest.php +++ b/tests/src/Rules/CachableDependencyRuleTest.php @@ -39,10 +39,6 @@ public static function resultData(): \Generator 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', 39 ], - [ - 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', - 48 - ], [ 'Calling addCacheableDependency($object) when $object does not implement CacheableDependencyInterface effectively disables caching and should be avoided.', 55 From abe945c66e1ea31af47476bebcb86299fae9df91 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Thu, 16 Oct 2025 14:03:17 +0200 Subject: [PATCH 10/10] remove old file --- .../src/UsesIncorrectCacheableDependency.php | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php diff --git a/tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php b/tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php deleted file mode 100644 index ce6d68e7..00000000 --- a/tests/fixtures/drupal/modules/phpstan_fixtures/src/UsesIncorrectCacheableDependency.php +++ /dev/null @@ -1,15 +0,0 @@ -addCacheableDependency($object); - } -}