@@ -17,210 +17,191 @@ class AutogeneratedClassNotInConstructorSniff implements Sniff
17
17
{
18
18
private const ERROR_CODE = 'AutogeneratedClassNotInConstructor ' ;
19
19
20
- /**
21
- * @var array
22
- */
23
- private $ constructorParameters = [];
24
-
25
- /**
26
- * @var array
27
- */
28
- private $ uses = [];
20
+ private const AUTOGENERATED_CLASS_SUFFIXES = [
21
+ 'Factory '
22
+ ];
29
23
30
24
/**
31
25
* @inheritdoc
32
26
*/
33
27
public function register ()
34
28
{
35
- return [T_FUNCTION , T_DOUBLE_COLON , T_USE ];
29
+ return [T_DOUBLE_COLON ];
36
30
}
37
31
38
32
/**
39
33
* @inheritdoc
40
34
*/
41
35
public function process (File $ phpcsFile , $ stackPtr )
42
36
{
43
- if ($ phpcsFile -> getTokens ()[ $ stackPtr ][ ' type ' ] === ' T_USE ' ) {
44
- $ this -> registerUse ( $ phpcsFile , $ stackPtr ) ;
37
+ if (! $ this -> isClass ( $ phpcsFile ) ) {
38
+ return ;
45
39
}
46
- if ($ phpcsFile ->getTokens ()[$ stackPtr ]['type ' ] === 'T_FUNCTION ' ) {
47
- $ this ->registerConstructorParameters ($ phpcsFile , $ stackPtr );
40
+
41
+ $ tokens = $ phpcsFile ->getTokens ();
42
+
43
+ if ($ tokens [$ stackPtr - 1 ]['content ' ] !== 'ObjectManager '
44
+ && $ tokens [$ stackPtr + 1 ]['content ' ] !== 'getInstance '
45
+ ) {
46
+ return ;
48
47
}
49
- if ($ phpcsFile ->getTokens ()[$ stackPtr ]['type ' ] === 'T_DOUBLE_COLON ' ) {
50
- if (!$ this ->isObjectManagerGetInstance ($ phpcsFile , $ stackPtr )) {
51
- return ;
52
- }
53
48
54
- $ statementStart = $ phpcsFile ->findStartOfStatement ($ stackPtr );
55
- $ statementEnd = $ phpcsFile ->findEndOfStatement ($ stackPtr );
56
- $ equalsPtr = $ phpcsFile ->findNext (T_EQUAL , $ statementStart , $ statementEnd );
49
+ if (!isset ($ tokens [$ stackPtr + 4 ]) || $ tokens [$ stackPtr + 4 ]['code ' ] !== T_SEMICOLON ) {
50
+ $ this ->validateRequestedClass (
51
+ $ phpcsFile ,
52
+ $ phpcsFile ->findNext (T_OBJECT_OPERATOR , $ stackPtr )
53
+ );
54
+ return ;
55
+ }
57
56
58
- if (!$ equalsPtr ) {
59
- return ;
60
- }
57
+ $ objectManagerVariableName = $ this ->getObjectManagerVariableName ($ phpcsFile , $ stackPtr );
61
58
62
- if (!$ this ->isVariableInConstructorParameters ($ phpcsFile , $ equalsPtr , $ statementEnd )) {
63
- $ className = $ this ->obtainClassToGetOrCreate ($ phpcsFile , $ stackPtr , $ statementEnd );
59
+ if (!$ objectManagerVariableName ) {
60
+ return ;
61
+ }
64
62
65
- $ phpcsFile ->addError (
66
- sprintf ("Class %s needs to be requested in constructor, " .
67
- "otherwise compiler will not be able to find and generate these classes " , $ className ),
68
- $ stackPtr ,
69
- self ::ERROR_CODE
70
- );
71
- }
63
+ $ variablePosition = $ phpcsFile ->findNext (T_VARIABLE , $ stackPtr , null , false , $ objectManagerVariableName );
64
+ if ($ variablePosition ) {
65
+ $ this ->validateRequestedClass ($ phpcsFile , $ phpcsFile ->findNext (T_OBJECT_OPERATOR , $ variablePosition ));
72
66
}
73
67
}
74
68
75
69
/**
76
- * Check if it is a ObjectManager::getInstance
70
+ * Check if the class is instantiated via get/create method, it is autogenerated and present in constructor
77
71
*
78
72
* @param File $phpcsFile
79
- * @param int $stackPtr
80
- * @return bool
73
+ * @param int $arrowPosition
81
74
*/
82
- private function isObjectManagerGetInstance (File $ phpcsFile , int $ stackPtr ): bool
75
+ private function validateRequestedClass (File $ phpcsFile , int $ arrowPosition ): void
83
76
{
84
- $ prev = $ phpcsFile ->findPrevious (T_STRING , $ stackPtr - 1 );
85
- $ next = $ phpcsFile ->findNext (T_STRING , $ stackPtr + 1 );
86
- return $ prev &&
87
- $ next &&
88
- $ phpcsFile ->getTokens ()[$ prev ]['content ' ] === 'ObjectManager ' &&
89
- $ phpcsFile ->getTokens ()[$ next ]['content ' ] === 'getInstance ' ;
77
+ $ requestedClass = $ this ->getRequestedClass ($ phpcsFile , $ arrowPosition );
78
+
79
+ if (!$ requestedClass
80
+ || !$ this ->isClassAutogenerated ($ requestedClass )
81
+ || $ this ->isConstructorParameter ($ phpcsFile , $ requestedClass )
82
+ ) {
83
+ return ;
84
+ }
85
+
86
+ $ phpcsFile ->addError (
87
+ sprintf (
88
+ 'Class %s needs to be requested in constructor, ' .
89
+ 'otherwise compiler will not be able to find and generate this class ' ,
90
+ $ requestedClass
91
+ ),
92
+ $ arrowPosition ,
93
+ self ::ERROR_CODE
94
+ );
90
95
}
91
96
92
97
/**
93
- * Get the complete class namespace from the use's
98
+ * Does the class have the suffix common for autogenerated classes e.g. Factory
94
99
*
95
100
* @param string $className
96
- * @return string
101
+ * @return bool
97
102
*/
98
- private function getClassNamespace (string $ className ): string
103
+ private function isClassAutogenerated (string $ className ): bool
99
104
{
100
- foreach ($ this -> uses as $ key => $ use ) {
101
- if ($ key === $ className ) {
102
- return $ use ;
105
+ foreach (self :: AUTOGENERATED_CLASS_SUFFIXES as $ suffix ) {
106
+ if (substr ( $ className , - strlen ( $ suffix )) === $ suffix ) {
107
+ return true ;
103
108
}
104
109
}
105
- return $ className ;
110
+ return false ;
106
111
}
107
112
108
113
/**
109
- * Register php uses
114
+ * Get the variable name to which the ObjectManager::getInstance() result is assigned
110
115
*
111
116
* @param File $phpcsFile
112
117
* @param int $stackPtr
118
+ * @return string|null
113
119
*/
114
- private function registerUse (File $ phpcsFile , int $ stackPtr ): void
120
+ private function getObjectManagerVariableName (File $ phpcsFile , int $ stackPtr ): ? string
115
121
{
116
- $ useEnd = $ phpcsFile ->findEndOfStatement ($ stackPtr );
117
- $ use = [];
118
- $ usePosition = $ stackPtr ;
119
- while ($ usePosition = $ phpcsFile ->findNext (T_STRING , $ usePosition + 1 , $ useEnd )) {
120
- $ use [] = $ phpcsFile ->getTokens ()[$ usePosition ]['content ' ];
122
+ $ matches = [];
123
+ $ found = preg_match (
124
+ '/(\$[A-Za-z]+) ?= ?ObjectManager::getInstance\(\);/ ' ,
125
+ $ phpcsFile ->getTokensAsString ($ stackPtr - 5 , 10 ),
126
+ $ matches
127
+ );
128
+
129
+ if (!$ found || !isset ($ matches [1 ])) {
130
+ return null ;
121
131
}
122
132
123
- $ key = end ($ use );
124
- if ($ phpcsFile ->findNext (T_AS , $ stackPtr , $ useEnd )) {
125
- $ this ->uses [$ key ] = implode ("\\" , array_slice ($ use , 0 , count ($ use ) - 1 ));
126
- } else {
127
- $ this ->uses [$ key ] = implode ("\\" , $ use );
128
- }
133
+ return $ matches [1 ];
129
134
}
130
135
131
136
/**
132
- * Register php constructor parameters
137
+ * Get class name requested from ObjectManager
133
138
*
134
139
* @param File $phpcsFile
135
- * @param int $stackPtr
140
+ * @param int $callerPosition
141
+ * @return string|null
136
142
*/
137
- private function registerConstructorParameters (File $ phpcsFile , int $ stackPtr ): void
143
+ private function getRequestedClass (File $ phpcsFile , int $ callerPosition ): ? string
138
144
{
139
- $ functionName = $ phpcsFile ->getDeclarationName ($ stackPtr );
140
- if ($ functionName == '__construct ' ) {
141
- $ this ->constructorParameters = $ phpcsFile ->getMethodParameters ($ stackPtr );
145
+ $ matches = [];
146
+ $ found = preg_match (
147
+ '/->(get|create)\(([A-Za-z \\\]+)::class/ ' ,
148
+ $ phpcsFile ->getTokensAsString ($ callerPosition , $ phpcsFile ->findNext (T_CLOSE_PARENTHESIS , $ callerPosition )),
149
+ $ matches
150
+ );
151
+
152
+ if (!$ found || !isset ($ matches [2 ])) {
153
+ return null ;
142
154
}
143
- }
144
155
145
- /**
146
- * Get next token
147
- *
148
- * @param File $phpcsFile
149
- * @param int $from
150
- * @param int $to
151
- * @param int|string|array $types
152
- * @return mixed
153
- */
154
- private function getNext (File $ phpcsFile , int $ from , int $ to , $ types )
155
- {
156
- return $ phpcsFile ->getTokens ()[$ phpcsFile ->findNext ($ types , $ from + 1 , $ to )];
156
+ return $ matches [2 ];
157
157
}
158
158
159
159
/**
160
- * Get previous token
160
+ * Does the file contain class declaration
161
161
*
162
162
* @param File $phpcsFile
163
- * @param int $from
164
- * @param int|string|array $types
165
- * @return mixed
166
- */
167
- private function getPrevious (File $ phpcsFile , int $ from , $ types )
168
- {
169
- return $ phpcsFile ->getTokens ()[$ phpcsFile ->findPrevious ($ types , $ from - 1 )];
170
- }
171
-
172
- /**
173
- * Get name of the variable without $
174
- *
175
- * @param string $parameterName
176
- * @return string
163
+ * @return bool
177
164
*/
178
- protected function variableName ( string $ parameterName ): string
165
+ private function isClass ( File $ phpcsFile ): bool
179
166
{
180
- return str_replace ('$ ' , '' , $ parameterName );
167
+ foreach ($ phpcsFile ->getTokens () as $ token ) {
168
+ if ($ token ['code ' ] === T_CLASS ) {
169
+ return true ;
170
+ }
171
+ }
172
+ return false ;
181
173
}
182
174
183
175
/**
184
- * Checks if a variable is present in the constructor parameters
176
+ * Get an array of constructor parameters
185
177
*
186
178
* @param File $phpcsFile
187
- * @param int $equalsPtr
188
- * @param int $statementEnd
189
- * @return bool
179
+ * @return array
190
180
*/
191
- private function isVariableInConstructorParameters (File $ phpcsFile, int $ equalsPtr , int $ statementEnd ): bool
181
+ private function getConstructorParameters (File $ phpcsFile ): array
192
182
{
193
- if ($ variable = $ phpcsFile ->findNext (T_VARIABLE , $ equalsPtr , $ statementEnd )) {
194
- $ variableName = $ phpcsFile ->getTokens ()[$ variable ]['content ' ];
195
- if ($ variableName === '$this ' ) {
196
- $ variableName = $ this ->getNext ($ phpcsFile , $ variable , $ statementEnd , T_STRING )['content ' ];
197
- }
198
- foreach ($ this ->constructorParameters as $ parameter ) {
199
- $ parameterName = $ parameter ['name ' ];
200
- if ($ this ->variableName ($ parameterName ) === $ this ->variableName ($ variableName )) {
201
- return true ;
202
- }
183
+ foreach ($ phpcsFile ->getTokens () as $ stackPtr => $ token ) {
184
+ if ($ token ['code ' ] === T_FUNCTION && $ phpcsFile ->getDeclarationName ($ stackPtr ) === '__construct ' ) {
185
+ return $ phpcsFile ->getMethodParameters ($ stackPtr );
203
186
}
204
187
}
205
- return false ;
188
+ return [] ;
206
189
}
207
190
208
191
/**
209
- * Obtain the class inside ObjectManager::getInstance()->get|create()
192
+ * Is the class name present between constructor parameters
210
193
*
211
194
* @param File $phpcsFile
212
- * @param int $next
213
- * @param int $statementEnd
214
- * @return string
195
+ * @param string $className
196
+ * @return bool
215
197
*/
216
- private function obtainClassToGetOrCreate (File $ phpcsFile , int $ next , int $ statementEnd ): string
198
+ private function isConstructorParameter (File $ phpcsFile , string $ className ): bool
217
199
{
218
- while ($ next = $ phpcsFile -> findNext ( T_DOUBLE_COLON , $ next + 1 , $ statementEnd ) ) {
219
- if ($ this -> getNext ( $ phpcsFile , $ next , $ statementEnd , T_STRING ) ['content ' ] === ' class ' ) {
220
- $ className = $ this -> getPrevious ( $ phpcsFile , $ next , T_STRING )[ ' content ' ] ;
200
+ foreach ($ this -> getConstructorParameters ( $ phpcsFile ) as $ parameter ) {
201
+ if (strpos ( $ parameter ['content ' ], $ className ) !== false ) {
202
+ return true ;
221
203
}
222
204
}
223
-
224
- return $ this ->getClassNamespace ($ className );
205
+ return false ;
225
206
}
226
207
}
0 commit comments