Skip to content

Commit a40fb53

Browse files
Check also for Node\Scalar\Encapsed (#592)
Co-authored-by: Matt Glaman <[email protected]>
1 parent 0b93022 commit a40fb53

File tree

3 files changed

+190
-19
lines changed

3 files changed

+190
-19
lines changed

src/Rules/Drupal/PluginManager/PluginManagerSetsCacheBackendRule.php

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use PhpParser\Node;
66
use PhpParser\Node\Stmt\ClassMethod;
77
use PHPStan\Analyser\Scope;
8-
use PHPStan\ShouldNotHappenException;
8+
use PHPStan\Type\Type;
99

1010
class PluginManagerSetsCacheBackendRule extends AbstractPluginManagerRule
1111
{
@@ -53,29 +53,30 @@ public function processNode(Node $node, Scope $scope): array
5353
($statement->name instanceof Node\Identifier) &&
5454
$statement->name->name === 'setCacheBackend') {
5555
// setCacheBackend accepts a cache backend, the cache key, and optional (but suggested) cache tags.
56-
$setCacheBackendArgs = $statement->args;
57-
58-
if ($setCacheBackendArgs[1] instanceof Node\VariadicPlaceholder) {
59-
throw new ShouldNotHappenException();
60-
}
61-
$cacheKey = $setCacheBackendArgs[1]->value;
62-
if (!$cacheKey instanceof Node\Scalar\String_) {
56+
$setCacheBackendArgs = $statement->getArgs();
57+
if (count($setCacheBackendArgs) < 2) {
6358
continue;
6459
}
6560
$hasCacheBackendSet = true;
6661

62+
$cacheKey = array_map(
63+
static fn (Type $type) => $type->getValue(),
64+
$scope->getType($setCacheBackendArgs[1]->value)->getConstantStrings()
65+
);
66+
if (count($cacheKey) === 0) {
67+
continue;
68+
}
69+
6770
if (isset($setCacheBackendArgs[2])) {
68-
if ($setCacheBackendArgs[2] instanceof Node\VariadicPlaceholder) {
69-
throw new ShouldNotHappenException();
70-
}
71-
/** @var \PhpParser\Node\Expr\Array_ $cacheTags */
72-
$cacheTags = $setCacheBackendArgs[2]->value;
73-
if (count($cacheTags->items) > 0) {
74-
/** @var \PhpParser\Node\Expr\ArrayItem $item */
75-
foreach ($cacheTags->items as $item) {
76-
if (($item->value instanceof Node\Scalar\String_) &&
77-
strpos($item->value->value, $cacheKey->value) === false) {
78-
$misnamedCacheTagWarnings[] = $item->value->value;
71+
$cacheTagsType = $scope->getType($setCacheBackendArgs[2]->value);
72+
foreach ($cacheTagsType->getConstantArrays() as $constantArray) {
73+
foreach ($constantArray->getValueTypes() as $valueType) {
74+
foreach ($valueType->getConstantStrings() as $cacheTagConstantString) {
75+
foreach ($cacheKey as $cacheKeyValue) {
76+
if (strpos($cacheTagConstantString->getValue(), $cacheKeyValue) === false) {
77+
$misnamedCacheTagWarnings[] = $cacheTagConstantString->getValue();
78+
}
79+
}
7980
}
8081
}
8182
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace mglaman\PHPStanDrupal\Tests\Rules;
6+
7+
use mglaman\PHPStanDrupal\Rules\Drupal\PluginManager\PluginManagerSetsCacheBackendRule;
8+
use mglaman\PHPStanDrupal\Tests\DrupalRuleTestCase;
9+
use PHPStan\Rules\Rule;
10+
11+
final class PluginManagerSetsCacheBackendRuleTest extends DrupalRuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new PluginManagerSetsCacheBackendRule();
17+
}
18+
19+
/**
20+
* @dataProvider ruleData
21+
*/
22+
public function testRule(string $path, array $errorMessages): void
23+
{
24+
$this->analyse([$path], $errorMessages);
25+
}
26+
27+
public static function ruleData(): \Generator
28+
{
29+
yield [
30+
__DIR__ . '/data/plugin-manager-cache-backend.php',
31+
[
32+
[
33+
'Missing cache backend declaration for performance.',
34+
12
35+
],
36+
[
37+
'plugins cache tag might be unclear and does not contain the cache key in it.',
38+
112,
39+
]
40+
]
41+
];
42+
}
43+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
namespace PluginManagerCacheBackend;
4+
5+
use Drupal\Core\Cache\CacheBackendInterface;
6+
use Drupal\Core\Extension\ModuleHandlerInterface;
7+
use Drupal\Core\Plugin\DefaultPluginManager;
8+
9+
class Foo extends DefaultPluginManager
10+
{
11+
12+
public function __construct(
13+
\Traversable $namespaces,
14+
ModuleHandlerInterface $module_handler,
15+
) {
16+
parent::__construct(
17+
'Plugin/Foo',
18+
$namespaces,
19+
$module_handler,
20+
'FooInterface',
21+
'FooAnnotation',
22+
);
23+
}
24+
25+
}
26+
27+
class Bar extends DefaultPluginManager
28+
{
29+
30+
public function __construct(
31+
\Traversable $namespaces,
32+
ModuleHandlerInterface $module_handler,
33+
CacheBackendInterface $cache_backend
34+
) {
35+
parent::__construct(
36+
'Plugin/Bar',
37+
$namespaces,
38+
$module_handler,
39+
'BarInterface',
40+
'BarAnnotation',
41+
);
42+
$this->setCacheBackend($cache_backend, 'bar_plugins');
43+
}
44+
45+
}
46+
47+
class Baz extends DefaultPluginManager
48+
{
49+
50+
public function __construct(
51+
\Traversable $namespaces,
52+
ModuleHandlerInterface $module_handler,
53+
CacheBackendInterface $cache_backend,
54+
string $type,
55+
) {
56+
parent::__construct(
57+
'Plugin/Bar',
58+
$namespaces,
59+
$module_handler,
60+
'BarInterface',
61+
'BarAnnotation',
62+
);
63+
$this->setCacheBackend($cache_backend, 'bar_' . $type . '_plugins');
64+
}
65+
66+
}
67+
68+
class Qux extends DefaultPluginManager
69+
{
70+
71+
public function __construct(
72+
\Traversable $namespaces,
73+
ModuleHandlerInterface $module_handler,
74+
CacheBackendInterface $cache_backend,
75+
string $type,
76+
) {
77+
parent::__construct(
78+
'Plugin/Bar',
79+
$namespaces,
80+
$module_handler,
81+
'BarInterface',
82+
'BarAnnotation',
83+
);
84+
$this->setCacheBackend($cache_backend, "bar_{$type}_plugins");
85+
}
86+
87+
}
88+
89+
class BarTags extends DefaultPluginManager
90+
{
91+
92+
public function __construct(
93+
\Traversable $namespaces,
94+
ModuleHandlerInterface $module_handler,
95+
CacheBackendInterface $cache_backend
96+
) {
97+
parent::__construct(
98+
'Plugin/Bar',
99+
$namespaces,
100+
$module_handler,
101+
'BarInterface',
102+
'BarAnnotation',
103+
);
104+
$this->setCacheBackend($cache_backend, 'bar_plugins', ['bar_plugins']);
105+
}
106+
107+
}
108+
109+
class BarTagsNotClear extends DefaultPluginManager
110+
{
111+
112+
public function __construct(
113+
\Traversable $namespaces,
114+
ModuleHandlerInterface $module_handler,
115+
CacheBackendInterface $cache_backend
116+
) {
117+
parent::__construct(
118+
'Plugin/Bar',
119+
$namespaces,
120+
$module_handler,
121+
'BarInterface',
122+
'BarAnnotation',
123+
);
124+
$this->setCacheBackend($cache_backend, 'bar_plugins', ['plugins']);
125+
}
126+
127+
}

0 commit comments

Comments
 (0)