7
7
use PHP_CodeSniffer \Util \Tokens ;
8
8
use SlevomatCodingStandard \Helpers \FixerHelper ;
9
9
use SlevomatCodingStandard \Helpers \IdentificatorHelper ;
10
+ use SlevomatCodingStandard \Helpers \IndentationHelper ;
10
11
use SlevomatCodingStandard \Helpers \SniffSettingsHelper ;
11
12
use SlevomatCodingStandard \Helpers \TokenHelper ;
13
+ use function array_keys ;
14
+ use function count ;
15
+ use function in_array ;
16
+ use function range ;
17
+ use function sprintf ;
18
+ use function trim ;
12
19
use const T_COALESCE ;
20
+ use const T_ELSE ;
21
+ use const T_ELSEIF ;
13
22
use const T_EQUAL ;
23
+ use const T_IF ;
24
+ use const T_IS_IDENTICAL ;
25
+ use const T_NULL ;
14
26
use const T_SEMICOLON ;
15
27
16
28
class RequireNullCoalesceEqualOperatorSniff implements Sniff
@@ -20,6 +32,8 @@ class RequireNullCoalesceEqualOperatorSniff implements Sniff
20
32
21
33
public ?bool $ enable = null ;
22
34
35
+ public bool $ checkIfConditions = false ;
36
+
23
37
/**
24
38
* @return array<int, (int|string)>
25
39
*/
@@ -42,6 +56,12 @@ public function process(File $phpcsFile, $equalPointer): void
42
56
return ;
43
57
}
44
58
59
+ $ this ->checkCoalesce ($ phpcsFile , $ equalPointer );
60
+ $ this ->checkIf ($ phpcsFile , $ equalPointer );
61
+ }
62
+
63
+ private function checkCoalesce (File $ phpcsFile , int $ equalPointer ): void
64
+ {
45
65
/** @var int $variableStartPointer */
46
66
$ variableStartPointer = TokenHelper::findNextEffective ($ phpcsFile , $ equalPointer + 1 );
47
67
$ variableEndPointer = IdentificatorHelper::findEndPointer ($ phpcsFile , $ variableStartPointer );
@@ -95,4 +115,100 @@ public function process(File $phpcsFile, $equalPointer): void
95
115
$ phpcsFile ->fixer ->endChangeset ();
96
116
}
97
117
118
+ private function checkIf (File $ phpcsFile , int $ equalPointer ): void
119
+ {
120
+ if (!$ this ->checkIfConditions ) {
121
+ return ;
122
+ }
123
+
124
+ $ tokens = $ phpcsFile ->getTokens ();
125
+
126
+ $ conditionsCount = count ($ tokens [$ equalPointer ]['conditions ' ]);
127
+ if ($ conditionsCount === 0 ) {
128
+ return ;
129
+ }
130
+
131
+ $ ifPointer = array_keys ($ tokens [$ equalPointer ]['conditions ' ])[$ conditionsCount - 1 ];
132
+ if ($ tokens [$ ifPointer ]['code ' ] !== T_IF ) {
133
+ return ;
134
+ }
135
+
136
+ $ pointerAfterIfCondition = TokenHelper::findNextEffective ($ phpcsFile , $ tokens [$ ifPointer ]['scope_closer ' ] + 1 );
137
+ if ($ pointerAfterIfCondition !== null && in_array ($ tokens [$ pointerAfterIfCondition ]['code ' ], [T_ELSEIF , T_ELSE ], true )) {
138
+ return ;
139
+ }
140
+
141
+ $ ifVariableStartPointer = TokenHelper::findNextEffective ($ phpcsFile , $ tokens [$ ifPointer ]['parenthesis_opener ' ] + 1 );
142
+ $ ifVariableEndPointer = IdentificatorHelper::findEndPointer ($ phpcsFile , $ ifVariableStartPointer );
143
+ if ($ ifVariableEndPointer === null ) {
144
+ return ;
145
+ }
146
+
147
+ $ nextIfPointer = TokenHelper::findNextEffective ($ phpcsFile , $ ifVariableEndPointer + 1 );
148
+ if ($ tokens [$ nextIfPointer ]['code ' ] !== T_IS_IDENTICAL ) {
149
+ return ;
150
+ }
151
+
152
+ $ nextIfPointer = TokenHelper::findNextEffective ($ phpcsFile , $ nextIfPointer + 1 );
153
+ if ($ tokens [$ nextIfPointer ]['code ' ] !== T_NULL ) {
154
+ return ;
155
+ }
156
+
157
+ if (TokenHelper::findNextEffective ($ phpcsFile , $ nextIfPointer + 1 ) !== $ tokens [$ ifPointer ]['parenthesis_closer ' ]) {
158
+ return ;
159
+ }
160
+
161
+ $ beforeEqualVariableStartPointer = TokenHelper::findNextEffective ($ phpcsFile , $ tokens [$ ifPointer ]['scope_opener ' ] + 1 );
162
+ $ beforeEqualVariableEndPointer = IdentificatorHelper::findEndPointer ($ phpcsFile , $ beforeEqualVariableStartPointer );
163
+ if ($ beforeEqualVariableEndPointer === null ) {
164
+ return ;
165
+ }
166
+
167
+ if (TokenHelper::findNextEffective ($ phpcsFile , $ beforeEqualVariableEndPointer + 1 ) !== $ equalPointer ) {
168
+ return ;
169
+ }
170
+
171
+ $ variableName = IdentificatorHelper::getContent ($ phpcsFile , $ ifVariableStartPointer , $ ifVariableEndPointer );
172
+
173
+ if ($ variableName !== IdentificatorHelper::getContent (
174
+ $ phpcsFile ,
175
+ $ beforeEqualVariableStartPointer ,
176
+ $ beforeEqualVariableEndPointer ,
177
+ )) {
178
+ return ;
179
+ }
180
+
181
+ $ semicolonPointer = TokenHelper::findNext ($ phpcsFile , T_SEMICOLON , $ equalPointer + 1 );
182
+ if (TokenHelper::findNextEffective ($ phpcsFile , $ semicolonPointer + 1 ) !== $ tokens [$ ifPointer ]['scope_closer ' ]) {
183
+ return ;
184
+ }
185
+
186
+ $ fix = $ phpcsFile ->addFixableError (
187
+ 'Use "??=" operator instead of if condition and "=". ' ,
188
+ $ ifPointer ,
189
+ self ::CODE_REQUIRED_NULL_COALESCE_EQUAL_OPERATOR ,
190
+ );
191
+
192
+ if (!$ fix ) {
193
+ return ;
194
+ }
195
+
196
+ $ codeStartPointer = TokenHelper::findNextEffective ($ phpcsFile , $ equalPointer + 1 );
197
+
198
+ $ afterNullCoalesceEqualCode = IndentationHelper::fixIndentation (
199
+ $ phpcsFile ,
200
+ range ($ codeStartPointer , $ semicolonPointer ),
201
+ IndentationHelper::getIndentation ($ phpcsFile , $ ifPointer ),
202
+ );
203
+
204
+ $ phpcsFile ->fixer ->beginChangeset ();
205
+ FixerHelper::change (
206
+ $ phpcsFile ,
207
+ $ ifPointer ,
208
+ $ tokens [$ ifPointer ]['scope_closer ' ],
209
+ sprintf ('%s ??= %s ' , $ variableName , trim ($ afterNullCoalesceEqualCode )),
210
+ );
211
+ $ phpcsFile ->fixer ->endChangeset ();
212
+ }
213
+
98
214
}
0 commit comments