Skip to content

Commit 1ed9ed8

Browse files
committed
Rule for ConditionManager::createInstance() when 'context' is passed as array key
1 parent f2335bc commit 1ed9ed8

File tree

4 files changed

+103
-0
lines changed

4 files changed

+103
-0
lines changed

extension.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,6 @@ services:
371371
-
372372
class: mglaman\PHPStanDrupal\Rules\Drupal\RenderCallbackRule
373373
tags: [phpstan.rules.rule]
374+
-
375+
class: mglaman\PHPStanDrupal\Rules\Deprecations\ConditionManagerCreateInstanceContextConfigurationRule
376+
tags: [phpstan.rules.rule]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Rules\Deprecations;
4+
5+
use Drupal\Core\Condition\ConditionManager;
6+
use PhpParser\Node;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\Type\Constant\ConstantArrayType;
11+
use PHPStan\Type\Constant\ConstantStringType;
12+
use PHPStan\Type\ObjectType;
13+
14+
final class ConditionManagerCreateInstanceContextConfigurationRule implements Rule
15+
{
16+
public function getNodeType(): string
17+
{
18+
return Node\Expr\MethodCall::class;
19+
}
20+
21+
public function processNode(Node $node, Scope $scope): array
22+
{
23+
assert($node instanceof Node\Expr\MethodCall);
24+
if (!$node->name instanceof Node\Identifier) {
25+
return [];
26+
}
27+
if ($node->name->toString() !== 'createInstance') {
28+
return [];
29+
}
30+
$args = $node->getArgs();
31+
if (count($args) !== 2) {
32+
return [];
33+
}
34+
$conditionManagerType = new ObjectType(ConditionManager::class);
35+
$type = $scope->getType($node->var);
36+
if (!$conditionManagerType->isSuperTypeOf($type)->yes()) {
37+
return [];
38+
}
39+
$configuration = $args[1];
40+
$configurationType = $scope->getType($configuration->value);
41+
// Must be an array, return [] and allow parameter inspection rule to report error.
42+
if (!$configurationType instanceof ConstantArrayType) {
43+
return [];
44+
}
45+
46+
foreach ($configurationType->getKeyTypes() as $keyType) {
47+
if ($keyType instanceof ConstantStringType && $keyType->getValue() === 'context') {
48+
return [
49+
RuleErrorBuilder::message('Passing context values to plugins via configuration is deprecated in drupal:9.1.0 and will be removed before drupal:10.0.0. Instead, call ::setContextValue() on the plugin itself. See https://www.drupal.org/node/3120980')
50+
->line($node->getLine())
51+
->build()
52+
];
53+
}
54+
}
55+
56+
return [];
57+
}
58+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,14 @@ function phpstan_fixtures_using_deprecated_constants(): void {
4848
print FILE_INSECURE_EXTENSION_REGEX;
4949
print PREG_CLASS_PUNCTUATION;
5050
}
51+
52+
function phpstan_fixtures_condition_manager_context(): void {
53+
$condition_manager = \Drupal::service('plugin.manager.condition');
54+
$configuration = [
55+
'foo' => 'bar',
56+
'context' => [
57+
'entity:type' => 'node',
58+
],
59+
];
60+
$foo = $condition_manager->createInstance('barbaz', $configuration);
61+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Tests\Rules;
4+
5+
use mglaman\PHPStanDrupal\Rules\Deprecations\ConditionManagerCreateInstanceContextConfigurationRule;
6+
use mglaman\PHPStanDrupal\Tests\DrupalRuleTestCase;
7+
8+
final class ConditionManagerCreateInstanceContextConfigurationRuleTest extends DrupalRuleTestCase
9+
{
10+
protected function getRule(): \PHPStan\Rules\Rule
11+
{
12+
return new ConditionManagerCreateInstanceContextConfigurationRule();
13+
}
14+
15+
public function testRule(): void
16+
{
17+
[$version] = explode('.', \Drupal::VERSION, 2);
18+
if ($version !== '9') {
19+
self::markTestSkipped('Only tested on Drupal 9.x.x');
20+
}
21+
$this->analyse(
22+
[__DIR__ . '/../../fixtures/drupal/modules/phpstan_fixtures/phpstan_fixtures.module'],
23+
[
24+
[
25+
'Passing context values to plugins via configuration is deprecated in drupal:9.1.0 and will be removed before drupal:10.0.0. Instead, call ::setContextValue() on the plugin itself. See https://www.drupal.org/node/3120980',
26+
60,
27+
]
28+
],
29+
);
30+
}
31+
}

0 commit comments

Comments
 (0)