Conditions | 24 |
Paths | 780 |
Total Lines | 164 |
Code Lines | 87 |
Lines | 0 |
Ratio | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
1 | <?php |
||
112 | private function fixFunction(Tokens $tokens, int $functionIndex, int $functionOpenIndex, int $functionCloseIndex): int |
||
113 | { |
||
114 | static $riskyKinds = [ |
||
115 | CT::T_DYNAMIC_VAR_BRACE_OPEN, // "$h = ${$g};" case |
||
116 | T_EVAL, // "$c = eval('return $this;');" case |
||
117 | T_GLOBAL, |
||
118 | T_INCLUDE, // loading additional symbols we cannot analyze here |
||
119 | T_INCLUDE_ONCE, // " |
||
120 | T_REQUIRE, // " |
||
121 | T_REQUIRE_ONCE, // " |
||
122 | T_STATIC, |
||
123 | ]; |
||
124 | |||
125 | $inserted = 0; |
||
126 | $candidates = []; |
||
127 | $isRisky = false; |
||
128 | |||
129 | // go through the function declaration and check if references are passed |
||
130 | // - check if it will be risky to fix return statements of this function |
||
131 | for ($index = $functionIndex + 1; $index < $functionOpenIndex; ++$index) { |
||
132 | if ($tokens[$index]->equals('&')) { |
||
133 | $isRisky = true; |
||
134 | |||
135 | break; |
||
136 | } |
||
137 | } |
||
138 | |||
139 | // go through all the tokens of the body of the function: |
||
140 | // - check if it will be risky to fix return statements of this function |
||
141 | // - check nested functions; fix when found and update the upper limit + number of inserted token |
||
142 | // - check for return statements that might be fixed (based on if fixing will be risky, which is only know after analyzing the whole function) |
||
143 | |||
144 | for ($index = $functionOpenIndex + 1; $index < $functionCloseIndex; ++$index) { |
||
145 | if ($tokens[$index]->isGivenKind(T_FUNCTION)) { |
||
146 | $nestedFunctionOpenIndex = $tokens->getNextTokenOfKind($index, ['{', ';']); |
||
147 | if ($tokens[$nestedFunctionOpenIndex]->equals(';')) { // abstract function |
||
148 | $index = $nestedFunctionOpenIndex - 1; |
||
149 | |||
150 | continue; |
||
151 | } |
||
152 | |||
153 | $nestedFunctionCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nestedFunctionOpenIndex); |
||
154 | |||
155 | $tokensAdded = $this->fixFunction( |
||
156 | $tokens, |
||
157 | $index, |
||
158 | $nestedFunctionOpenIndex, |
||
159 | $nestedFunctionCloseIndex |
||
160 | ); |
||
161 | |||
162 | $index = $nestedFunctionCloseIndex + $tokensAdded; |
||
163 | $functionCloseIndex += $tokensAdded; |
||
164 | $inserted += $tokensAdded; |
||
165 | } |
||
166 | |||
167 | if ($isRisky) { |
||
168 | continue; // don't bother to look into anything else than nested functions as the current is risky already |
||
169 | } |
||
170 | |||
171 | if ($tokens[$index]->equals('&')) { |
||
172 | $isRisky = true; |
||
173 | |||
174 | continue; |
||
175 | } |
||
176 | |||
177 | if ($tokens[$index]->isGivenKind(T_RETURN)) { |
||
178 | $candidates[] = $index; |
||
179 | |||
180 | continue; |
||
181 | } |
||
182 | |||
183 | // test if there this is anything in the function body that might |
||
184 | // change global state or indirect changes (like through references, eval, etc.) |
||
185 | |||
186 | if ($tokens[$index]->isGivenKind($riskyKinds)) { |
||
187 | $isRisky = true; |
||
188 | |||
189 | continue; |
||
190 | } |
||
191 | |||
192 | if ($tokens[$index]->equals('$')) { |
||
193 | $nextIndex = $tokens->getNextMeaningfulToken($index); |
||
194 | if ($tokens[$nextIndex]->isGivenKind(T_VARIABLE)) { |
||
195 | $isRisky = true; // "$$a" case |
||
196 | |||
197 | continue; |
||
198 | } |
||
199 | } |
||
200 | |||
201 | if ($this->tokensAnalyzer->isSuperGlobal($index)) { |
||
202 | $isRisky = true; |
||
203 | |||
204 | continue; |
||
205 | } |
||
206 | } |
||
207 | |||
208 | if ($isRisky) { |
||
209 | return $inserted; |
||
210 | } |
||
211 | |||
212 | // fix the candidates in reverse order when applicable |
||
213 | for ($i = \count($candidates) - 1; $i >= 0; --$i) { |
||
214 | $index = $candidates[$i]; |
||
215 | |||
216 | // Check if returning only a variable (i.e. not the result of an expression, function call etc.) |
||
217 | $returnVarIndex = $tokens->getNextMeaningfulToken($index); |
||
218 | if (!$tokens[$returnVarIndex]->isGivenKind(T_VARIABLE)) { |
||
219 | continue; // example: "return 1;" |
||
220 | } |
||
221 | |||
222 | $endReturnVarIndex = $tokens->getNextMeaningfulToken($returnVarIndex); |
||
223 | if (!$tokens[$endReturnVarIndex]->equalsAny([';', [T_CLOSE_TAG]])) { |
||
224 | continue; // example: "return $a + 1;" |
||
225 | } |
||
226 | |||
227 | // Check that the variable is assigned just before it is returned |
||
228 | $assignVarEndIndex = $tokens->getPrevMeaningfulToken($index); |
||
229 | if (!$tokens[$assignVarEndIndex]->equals(';')) { |
||
230 | continue; // example: "? return $a;" |
||
231 | } |
||
232 | |||
233 | // Note: here we are @ "; return $a;" (or "; return $a ? >") |
||
234 | do { |
||
235 | $prevMeaningFul = $tokens->getPrevMeaningfulToken($assignVarEndIndex); |
||
236 | |||
237 | if (!$tokens[$prevMeaningFul]->equals(')')) { |
||
238 | break; |
||
239 | } |
||
240 | |||
241 | $assignVarEndIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $prevMeaningFul); |
||
242 | } while (true); |
||
243 | |||
244 | $assignVarOperatorIndex = $tokens->getPrevTokenOfKind( |
||
245 | $assignVarEndIndex, |
||
246 | ['=', ';', '{', [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]] |
||
247 | ); |
||
248 | |||
249 | if (null === $assignVarOperatorIndex || !$tokens[$assignVarOperatorIndex]->equals('=')) { |
||
250 | continue; |
||
251 | } |
||
252 | |||
253 | // Note: here we are @ "= [^;{<? ? >] ; return $a;" |
||
254 | $assignVarIndex = $tokens->getPrevMeaningfulToken($assignVarOperatorIndex); |
||
255 | if (!$tokens[$assignVarIndex]->equals($tokens[$returnVarIndex], false)) { |
||
256 | continue; |
||
257 | } |
||
258 | |||
259 | // Note: here we are @ "$a = [^;{<? ? >] ; return $a;" |
||
260 | $beforeAssignVarIndex = $tokens->getPrevMeaningfulToken($assignVarIndex); |
||
261 | if (!$tokens[$beforeAssignVarIndex]->equalsAny([';', '{', '}'])) { |
||
262 | continue; |
||
263 | } |
||
264 | |||
265 | // Note: here we are @ "[;{}] $a = [^;{<? ? >] ; return $a;" |
||
266 | $inserted += $this->simplifyReturnStatement( |
||
267 | $tokens, |
||
268 | $assignVarIndex, |
||
269 | $assignVarOperatorIndex, |
||
270 | $index, |
||
271 | $endReturnVarIndex |
||
272 | ); |
||
273 | } |
||
274 | |||
275 | return $inserted; |
||
276 | } |
||
356 |