2
2
3
3
namespace mglaman \PHPStanDrupal \Rules \Classes ;
4
4
5
+ use Drupal \Component \Plugin \PluginManagerInterface ;
6
+ use Drupal \Core \Plugin \DefaultPluginManager ;
5
7
use PhpParser \Node ;
8
+ use PhpParser \NodeFinder ;
6
9
use PHPStan \Analyser \Scope ;
7
10
use PHPStan \Reflection \ReflectionProvider ;
8
11
use PHPStan \Rules \Rule ;
12
+ use PHPStan \Rules \RuleErrorBuilder ;
9
13
use PHPStan \Type \ObjectType ;
10
14
use function sprintf ;
11
15
@@ -35,80 +39,78 @@ public function processNode(Node $node, Scope $scope): array
35
39
if ($ node ->extends === null ) {
36
40
return [];
37
41
}
38
- $ className = (string ) $ node ->namespacedName ;
39
- $ pluginManagerType = new ObjectType ($ className );
40
- $ pluginManagerInterfaceType = new ObjectType ('\Drupal\Component\Plugin\PluginManagerInterface ' );
42
+ if (str_contains ($ node ->namespacedName ->toLowerString (), 'test ' )) {
43
+ return [];
44
+ }
45
+
46
+ $ pluginManagerType = $ scope ->resolveTypeByName ($ node ->namespacedName );
47
+ $ pluginManagerInterfaceType = new ObjectType (PluginManagerInterface::class);
41
48
if (!$ pluginManagerInterfaceType ->isSuperTypeOf ($ pluginManagerType )->yes ()) {
42
49
return [];
43
50
}
51
+ $ defaultPluginManager = new ObjectType (DefaultPluginManager::class);
52
+ if ($ defaultPluginManager ->equals ($ pluginManagerType )) {
53
+ return [];
54
+ }
55
+
56
+ $ constructorMethodNode = (new NodeFinder ())->findFirst ($ node ->stmts , static function (Node $ node ) {
57
+ return $ node instanceof Node \Stmt \ClassMethod && $ node ->name ->toString () === '__construct ' ;
58
+ });
59
+ if (!$ constructorMethodNode instanceof Node \Stmt \ClassMethod) {
60
+ return [];
61
+ }
44
62
45
63
$ errors = [];
46
64
if ($ this ->isYamlDiscovery ($ node )) {
47
- $ errors = $ this ->inspectYamlPluginManager ($ node );
65
+ $ errors = $ this ->inspectYamlPluginManager ($ node, $ constructorMethodNode );
48
66
} else {
49
67
// @todo inspect annotated plugin managers.
50
68
}
51
69
52
- $ hasAlterInfoSet = false ;
53
-
54
- foreach ($ node ->stmts as $ stmt ) {
55
- if ($ stmt instanceof Node \Stmt \ClassMethod && $ stmt ->name ->toString () === '__construct ' ) {
56
- foreach ($ stmt ->stmts ?? [] as $ statement ) {
57
- if ($ statement instanceof Node \Stmt \Expression) {
58
- $ statement = $ statement ->expr ;
59
- }
60
- if ($ statement instanceof Node \Expr \MethodCall
61
- && $ statement ->name instanceof Node \Identifier
62
- && $ statement ->name ->name === 'alterInfo ' ) {
63
- $ hasAlterInfoSet = true ;
64
- }
65
- }
66
- }
67
- }
70
+ $ alterInfoMethodNode = (new NodeFinder ())->findFirst ($ constructorMethodNode ->stmts ?? [], static function (Node $ node ) {
71
+ return $ node instanceof Node \Stmt \Expression
72
+ && $ node ->expr instanceof Node \Expr \MethodCall
73
+ && $ node ->expr ->name instanceof Node \Identifier
74
+ && $ node ->expr ->name ->toString () === 'alterInfo ' ;
75
+ });
68
76
69
- if (!$ hasAlterInfoSet ) {
70
- $ errors [] = 'Plugin definitions cannot be altered. ' ;
77
+ if ($ alterInfoMethodNode === null ) {
78
+ $ errors [] = RuleErrorBuilder::message (
79
+ 'Plugin managers should call alterInfo to allow plugin definitions to be altered. '
80
+ )
81
+ ->identifier ('plugin.manager.alterInfoMissing ' )
82
+ ->tip ('For example, to invoke hook_mymodule_data_alter() call alterInfo with "mymodule_data". ' )
83
+ ->line ($ node ->getStartLine ())
84
+ ->build ();
71
85
}
72
86
73
87
return $ errors ;
74
88
}
75
89
76
90
private function isYamlDiscovery (Node \Stmt \Class_ $ class ): bool
77
91
{
78
- foreach ($ class ->stmts as $ stmt ) {
79
- // YAML discovery plugin managers must override getDiscovery.
80
- if ($ stmt instanceof Node \Stmt \ClassMethod && $ stmt ->name ->toString () === 'getDiscovery ' ) {
81
- foreach ($ stmt ->stmts ?? [] as $ methodStmt ) {
82
- if ($ methodStmt instanceof Node \Stmt \If_) {
83
- foreach ($ methodStmt ->stmts as $ ifStmt ) {
84
- if ($ ifStmt instanceof Node \Stmt \Expression) {
85
- $ ifStmtExpr = $ ifStmt ->expr ;
86
- if ($ ifStmtExpr instanceof Node \Expr \Assign) {
87
- $ ifStmtExprVar = $ ifStmtExpr ->var ;
88
- if ($ ifStmtExprVar instanceof Node \Expr \PropertyFetch
89
- && $ ifStmtExprVar ->var instanceof Node \Expr \Variable
90
- && $ ifStmtExprVar ->name instanceof Node \Identifier
91
- && $ ifStmtExprVar ->name ->name === 'discovery '
92
- ) {
93
- $ ifStmtExprExpr = $ ifStmtExpr ->expr ;
94
- if ($ ifStmtExprExpr instanceof Node \Expr \New_
95
- && ($ ifStmtExprExpr ->class instanceof Node \Name)
96
- && $ ifStmtExprExpr ->class ->toString () === 'Drupal\Core\Plugin\Discovery\YamlDiscovery ' ) {
97
- return true ;
98
- }
99
- }
100
- }
101
- }
102
- }
103
- }
104
- }
105
- }
92
+ $ nodeFinder = new NodeFinder ();
93
+ $ getDiscoveryMethodNode = $ nodeFinder ->findFirst ($ class ->stmts , static function (Node $ node ) {
94
+ return $ node instanceof Node \Stmt \ClassMethod && $ node ->name ->toString () === 'getDiscovery ' ;
95
+ });
96
+ if (!$ getDiscoveryMethodNode instanceof Node \Stmt \ClassMethod) {
97
+ return false ;
98
+ }
99
+
100
+ $ assignDiscovery = $ nodeFinder ->findFirstInstanceOf ($ getDiscoveryMethodNode ->stmts ?? [], Node \Expr \Assign::class);
101
+ if ($ assignDiscovery === null ) {
102
+ return false ;
103
+ }
104
+ if ($ assignDiscovery ->expr instanceof Node \Expr \New_
105
+ && $ assignDiscovery ->expr ->class instanceof Node \Name
106
+ && $ assignDiscovery ->expr ->class ->toString () === 'Drupal\Core\Plugin\Discovery\YamlDiscovery ' ) {
107
+ return true ;
106
108
}
107
109
108
110
return false ;
109
111
}
110
112
111
- private function inspectYamlPluginManager (Node \Stmt \Class_ $ class ): array
113
+ private function inspectYamlPluginManager (Node \Stmt \Class_ $ class, Node \ Stmt \ ClassMethod $ constructorMethodNode ): array
112
114
{
113
115
$ errors = [];
114
116
@@ -119,20 +121,16 @@ private function inspectYamlPluginManager(Node\Stmt\Class_ $class): array
119
121
if ($ constructor ->getDeclaringClass ()->getName () !== $ fqn ) {
120
122
$ errors [] = sprintf ('%s must override __construct if using YAML plugins. ' , $ fqn );
121
123
} else {
122
- foreach ($ class ->stmts as $ stmt ) {
123
- if ($ stmt instanceof Node \Stmt \ClassMethod && $ stmt ->name ->toString () === '__construct ' ) {
124
- foreach ($ stmt ->stmts ?? [] as $ constructorStmt ) {
125
- if ($ constructorStmt instanceof Node \Stmt \Expression) {
126
- $ constructorStmt = $ constructorStmt ->expr ;
127
- }
128
- if ($ constructorStmt instanceof Node \Expr \StaticCall
129
- && $ constructorStmt ->class instanceof Node \Name
130
- && ((string )$ constructorStmt ->class === 'parent ' )
131
- && $ constructorStmt ->name instanceof Node \Identifier
132
- && $ constructorStmt ->name ->name === '__construct ' ) {
133
- $ errors [] = sprintf ('YAML plugin managers should not invoke its parent constructor. ' );
134
- }
135
- }
124
+ foreach ($ constructorMethodNode ->stmts ?? [] as $ constructorStmt ) {
125
+ if ($ constructorStmt instanceof Node \Stmt \Expression) {
126
+ $ constructorStmt = $ constructorStmt ->expr ;
127
+ }
128
+ if ($ constructorStmt instanceof Node \Expr \StaticCall
129
+ && $ constructorStmt ->class instanceof Node \Name
130
+ && ((string )$ constructorStmt ->class === 'parent ' )
131
+ && $ constructorStmt ->name instanceof Node \Identifier
132
+ && $ constructorStmt ->name ->name === '__construct ' ) {
133
+ $ errors [] = 'YAML plugin managers should not invoke its parent constructor. ' ;
136
134
}
137
135
}
138
136
}
0 commit comments