Skip to content

Commit ecff639

Browse files
committed
Add SymfonyCmfRouteObjectInterfaceConstantsRule
1 parent c922084 commit ecff639

File tree

6 files changed

+167
-11
lines changed

6 files changed

+167
-11
lines changed

rules.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ rules:
1515
- mglaman\PHPStanDrupal\Rules\Drupal\ModuleLoadInclude
1616
- mglaman\PHPStanDrupal\Rules\Drupal\LoadIncludes
1717
- mglaman\PHPStanDrupal\Rules\Drupal\EntityQuery\EntityQueryHasAccessCheckRule
18+
- mglaman\PHPStanDrupal\Rules\Deprecations\SymfonyCmfRouteObjectInterfaceConstantsRule
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Internal;
4+
5+
use PHPStan\Analyser\Scope;
6+
7+
final class DeprecatedScopeCheck
8+
{
9+
public static function inDeprecatedScope(Scope $scope): bool
10+
{
11+
$class = $scope->getClassReflection();
12+
if ($class !== null && $class->isDeprecated()) {
13+
return true;
14+
}
15+
$trait = $scope->getTraitReflection();
16+
if ($trait !== null && $trait->isDeprecated()) {
17+
return true;
18+
}
19+
$function = $scope->getFunction();
20+
return $function !== null && $function->isDeprecated()->yes();
21+
}
22+
}

src/Rules/Deprecations/AccessDeprecatedConstant.php

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace mglaman\PHPStanDrupal\Rules\Deprecations;
44

5+
use mglaman\PHPStanDrupal\Internal\DeprecatedScopeCheck;
56
use PhpParser\Node;
67
use PHPStan\Analyser\Scope;
7-
use PHPStan\Reflection\FunctionReflection;
88
use PHPStan\Reflection\ReflectionProvider;
99

1010
class AccessDeprecatedConstant implements \PHPStan\Rules\Rule
@@ -24,16 +24,7 @@ public function getNodeType(): string
2424
public function processNode(Node $node, Scope $scope): array
2525
{
2626
assert($node instanceof Node\Expr\ConstFetch);
27-
$class = $scope->getClassReflection();
28-
if ($class !== null && $class->isDeprecated()) {
29-
return [];
30-
}
31-
$trait = $scope->getTraitReflection();
32-
if ($trait !== null && $trait->isDeprecated()) {
33-
return [];
34-
}
35-
$function = $scope->getFunction();
36-
if ($function instanceof FunctionReflection && $function->isDeprecated()->yes()) {
27+
if (DeprecatedScopeCheck::inDeprecatedScope($scope)) {
3728
return [];
3829
}
3930

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace mglaman\PHPStanDrupal\Rules\Deprecations;
4+
5+
use Drupal\Core\Routing\RouteObjectInterface;
6+
use mglaman\PHPStanDrupal\Internal\DeprecatedScopeCheck;
7+
use PhpParser\Node;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Rules\Rule;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
use PHPStan\Type\ObjectType;
12+
13+
final class SymfonyCmfRouteObjectInterfaceConstantsRule implements Rule
14+
{
15+
16+
public function getNodeType(): string
17+
{
18+
return Node\Expr\ClassConstFetch::class;
19+
}
20+
21+
public function processNode(Node $node, Scope $scope): array
22+
{
23+
assert($node instanceof Node\Expr\ClassConstFetch);
24+
if (!$node->name instanceof Node\Identifier) {
25+
return [];
26+
}
27+
if (!$node->class instanceof Node\Name) {
28+
return [];
29+
}
30+
$constantName = $node->name->name;
31+
$className = $node->class;
32+
$classType = $scope->resolveTypeByName($className);
33+
if (!$classType->hasConstant($constantName)->yes()) {
34+
return [];
35+
}
36+
if (DeprecatedScopeCheck::inDeprecatedScope($scope)) {
37+
return [];
38+
}
39+
[$major, $minor] = explode('.', \Drupal::VERSION, 3);
40+
if ($major !== '9' && (int) $minor > 1) {
41+
return [];
42+
}
43+
$cmfRouteObjectInterfaceType = new ObjectType(\Symfony\Cmf\Component\Routing\RouteObjectInterface::class);
44+
if (!$classType->isSuperTypeOf($cmfRouteObjectInterfaceType)->yes()) {
45+
return [];
46+
}
47+
48+
$coreRouteObjectInterfaceType = new ObjectType(RouteObjectInterface::class);
49+
if (!$coreRouteObjectInterfaceType->hasConstant($constantName)->yes()) {
50+
return [
51+
RuleErrorBuilder::message(
52+
sprintf('The core dependency symfony-cmf/routing is deprecated and %s::%s is not supported.', $className, $constantName)
53+
)->tip('Change record: https://www.drupal.org/node/3151009')->build(),
54+
];
55+
}
56+
57+
return [
58+
RuleErrorBuilder::message(
59+
sprintf('%s::%s is deprecated and removed in Drupal 10. Use \Drupal\Core\Routing\RouteObjectInterface::%2$s instead.', $className, $constantName)
60+
)->tip('Change record: https://www.drupal.org/node/3151009')->build(),
61+
];
62+
}
63+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace mglaman\PHPStanDrupal\Tests\Rules;
5+
6+
use mglaman\PHPStanDrupal\Rules\Deprecations\SymfonyCmfRouteObjectInterfaceConstantsRule;
7+
use mglaman\PHPStanDrupal\Tests\DrupalRuleTestCase;
8+
9+
final class SymfonyCmfRouteObjectInterfaceConstantsRuleTest extends DrupalRuleTestCase
10+
{
11+
12+
protected function getRule(): \PHPStan\Rules\Rule
13+
{
14+
return new SymfonyCmfRouteObjectInterfaceConstantsRule();
15+
}
16+
17+
public function testRule(): void
18+
{
19+
$this->analyse(
20+
[__DIR__.'/data/symfony-cmf-routing.php'],
21+
[
22+
[
23+
'Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_NAME is deprecated and removed in Drupal 10. Use \Drupal\Core\Routing\RouteObjectInterface::ROUTE_NAME instead.',
24+
6,
25+
'Change record: https://www.drupal.org/node/3151009'
26+
],
27+
[
28+
'Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_OBJECT is deprecated and removed in Drupal 10. Use \Drupal\Core\Routing\RouteObjectInterface::ROUTE_OBJECT instead.',
29+
7,
30+
'Change record: https://www.drupal.org/node/3151009'
31+
],
32+
[
33+
'Symfony\Cmf\Component\Routing\RouteObjectInterface::CONTROLLER_NAME is deprecated and removed in Drupal 10. Use \Drupal\Core\Routing\RouteObjectInterface::CONTROLLER_NAME instead.',
34+
8,
35+
'Change record: https://www.drupal.org/node/3151009'
36+
],
37+
[
38+
'The core dependency symfony-cmf/routing is deprecated and Symfony\Cmf\Component\Routing\RouteObjectInterface::TEMPLATE_NAME is not supported.',
39+
9,
40+
'Change record: https://www.drupal.org/node/3151009'
41+
],
42+
]
43+
);
44+
}
45+
46+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace SymfonyCmfRoutingUsage;
4+
5+
class Foo {
6+
public const NAME = \Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_NAME;
7+
public const OBJECT = \Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_OBJECT;
8+
public const CONTROLLER = \Symfony\Cmf\Component\Routing\RouteObjectInterface::CONTROLLER_NAME;
9+
public const UNMAPPED = \Symfony\Cmf\Component\Routing\RouteObjectInterface::TEMPLATE_NAME;
10+
public function a(\Symfony\Cmf\Component\Routing\RouteObjectInterface $object) {
11+
12+
}
13+
public function b(\Symfony\Cmf\Component\Routing\RouteProviderInterface $provider) {
14+
15+
}
16+
public function c(\Symfony\Cmf\Component\Routing\LazyRouteCollection $collection) {
17+
18+
}
19+
}
20+
class Bar {
21+
public const NAME = \Drupal\Core\Routing\RouteObjectInterface::ROUTE_NAME;
22+
public const OBJECT = \Drupal\Core\Routing\RouteObjectInterface::ROUTE_OBJECT;
23+
public const CONTROLLER = \Drupal\Core\Routing\RouteObjectInterface::CONTROLLER_NAME;
24+
public function a(\Drupal\Core\Routing\RouteObjectInterface $object) {
25+
26+
}
27+
public function b(\Drupal\Core\Routing\RouteProviderInterface $provider) {
28+
29+
}
30+
public function c(\Drupal\Core\Routing\LazyRouteCollection $collection) {
31+
32+
}
33+
}

0 commit comments

Comments
 (0)