Skip to content

Commit 4462777

Browse files
committed
Add DrupalRequestStackShimInClassMethodRule rule
1 parent a0afcb0 commit 4462777

File tree

5 files changed

+143
-1
lines changed

5 files changed

+143
-1
lines changed

rules.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ rules:
1717
- mglaman\PHPStanDrupal\Rules\Drupal\EntityQuery\EntityQueryHasAccessCheckRule
1818
- mglaman\PHPStanDrupal\Rules\Deprecations\SymfonyCmfRouteObjectInterfaceConstantsRule
1919
- mglaman\PHPStanDrupal\Rules\Deprecations\SymfonyCmfRoutingInClassMethodSignatureRule
20+
- mglaman\PHPStanDrupal\Rules\Drupal\DrupalRequestStackShimInClassMethodRule

src/Rules/Deprecations/SymfonyCmfRoutingInClassMethodSignatureRule.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function processNode(Node $node, Scope $scope): array
2727
return [];
2828
}
2929
[$major, $minor] = explode('.', \Drupal::VERSION, 3);
30-
if ($major !== '9' && (int) $minor > 1) {
30+
if ($major !== '9' || (int) $minor < 1) {
3131
return [];
3232
}
3333
$method = $scope->getFunction();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Rules\Drupal;
4+
5+
use mglaman\PHPStanDrupal\Internal\DeprecatedScopeCheck;
6+
use PhpParser\Node;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Node\InClassMethodNode;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Reflection\ParametersAcceptorSelector;
11+
use PHPStan\Rules\Rule;
12+
use PHPStan\Rules\RuleErrorBuilder;
13+
use PHPStan\Type\ObjectType;
14+
15+
final class DrupalRequestStackShimInClassMethodRule implements Rule
16+
{
17+
18+
public function getNodeType(): string
19+
{
20+
return InClassMethodNode::class;
21+
}
22+
23+
public function processNode(Node $node, Scope $scope): array
24+
{
25+
assert($node instanceof InClassMethodNode);
26+
if (DeprecatedScopeCheck::inDeprecatedScope($scope)) {
27+
return [];
28+
}
29+
[$major, $minor] = explode('.', \Drupal::VERSION, 3);
30+
// Only valid for 9.3 -> 9.5. Deprecated in Drupal 10.
31+
if ($major !== '9' || (int) $minor < 3) {
32+
return [];
33+
}
34+
$method = $scope->getFunction();
35+
if (!$method instanceof MethodReflection) {
36+
throw new \PHPStan\ShouldNotHappenException();
37+
}
38+
$errors = [];
39+
40+
$symfonyRequestStackType = new ObjectType(\Symfony\Component\HttpFoundation\RequestStack::class);
41+
42+
$methodSignature = ParametersAcceptorSelector::selectSingle($method->getVariants());
43+
foreach ($methodSignature->getParameters() as $parameter) {
44+
foreach ($parameter->getType()->getReferencedClasses() as $referencedClass) {
45+
$referencedClassType = new ObjectType($referencedClass);
46+
if ($referencedClassType->equals($symfonyRequestStackType)) {
47+
$errors[] = RuleErrorBuilder::message(
48+
sprintf(
49+
'Parameter $%s of method %s() uses \Symfony\Component\HttpFoundation\RequestStack. Use \Drupal\Core\Http\RequestStack instead for forward compatibility to Symfony 5.',
50+
$parameter->getName(),
51+
$method->getName(),
52+
)
53+
)
54+
->tip('Change record: https://www.drupal.org/node/3253744')
55+
->build();
56+
}
57+
}
58+
}
59+
$returnClasses = $methodSignature->getReturnType()->getReferencedClasses();
60+
foreach ($returnClasses as $returnClass) {
61+
$returnType = new ObjectType($returnClass);
62+
if ($returnType->equals($symfonyRequestStackType)) {
63+
$errors[] = RuleErrorBuilder::message('Return type of method %s::%s() uses \Symfony\Component\HttpFoundation\RequestStack. Use \Drupal\Core\Http\RequestStack instead for forward compatibility to Symfony 5.')
64+
->tip('Change record: https://www.drupal.org/node/3253744')
65+
->build();
66+
}
67+
}
68+
69+
return $errors;
70+
}
71+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Tests\Rules;
4+
5+
use mglaman\PHPStanDrupal\Rules\Drupal\DrupalRequestStackShimInClassMethodRule;
6+
use mglaman\PHPStanDrupal\Tests\DrupalRuleTestCase;
7+
8+
final class DrupalRequestStackShimInClassMethodRuleTest extends DrupalRuleTestCase
9+
{
10+
11+
protected function getRule(): \PHPStan\Rules\Rule
12+
{
13+
return new DrupalRequestStackShimInClassMethodRule();
14+
}
15+
16+
public function testRule(): void
17+
{
18+
[$version] = explode('.', \Drupal::VERSION, 2);
19+
if ($version === '8') {
20+
$this->analyse([__DIR__.'/data/request-stack.php'], []);
21+
} elseif ($version === '10') {
22+
self::markTestSkipped('Not tested on 10.x.x');
23+
} else {
24+
$this->analyse(
25+
[__DIR__.'/data/request-stack.php'],
26+
[
27+
[
28+
'Parameter $stack of method __construct() uses \Symfony\Component\HttpFoundation\RequestStack. Use \Drupal\Core\Http\RequestStack instead for forward compatibility to Symfony 5.',
29+
6,
30+
'Change record: https://www.drupal.org/node/3253744',
31+
],
32+
[
33+
'Return type of method %s::%s() uses \Symfony\Component\HttpFoundation\RequestStack. Use \Drupal\Core\Http\RequestStack instead for forward compatibility to Symfony 5.',
34+
9,
35+
'Change record: https://www.drupal.org/node/3253744',
36+
],
37+
// @todo needs specific \PhpParser\Node\Expr\New_ rule.
38+
[
39+
'',
40+
11,
41+
'Change record: https://www.drupal.org/node/3253744',
42+
],
43+
]
44+
);
45+
}
46+
}
47+
48+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace DrupalRequestStackShim;
4+
5+
class Foo {
6+
public function __construct(\Symfony\Component\HttpFoundation\RequestStack $stack)
7+
{
8+
}
9+
public function getStack(): \Symfony\Component\HttpFoundation\RequestStack
10+
{
11+
return new \Symfony\Component\HttpFoundation\RequestStack();
12+
}
13+
}
14+
class Bar {
15+
public function __construct(\Drupal\Core\Http\RequestStack $stack)
16+
{
17+
}
18+
public function getStack(): \Drupal\Core\Http\RequestStack
19+
{
20+
return new \Drupal\Core\Http\RequestStack();
21+
}
22+
}

0 commit comments

Comments
 (0)