Conditions | 98 |
Paths | > 20000 |
Total Lines | 495 |
Lines | 43 |
Ratio | 8.69 % |
Changes | 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 |
||
37 | public static function analyze( |
||
38 | StatementsAnalyzer $statements_analyzer, |
||
39 | PhpParser\Node\Stmt\TryCatch $stmt, |
||
40 | Context $context |
||
41 | ) { |
||
42 | $catch_actions = []; |
||
43 | $all_catches_leave = true; |
||
44 | |||
45 | $codebase = $statements_analyzer->getCodebase(); |
||
46 | |||
47 | /** @var int $i */ |
||
48 | foreach ($stmt->catches as $i => $catch) { |
||
49 | $catch_actions[$i] = ScopeAnalyzer::getFinalControlActions( |
||
50 | $catch->stmts, |
||
51 | $statements_analyzer->node_data, |
||
52 | $codebase->config->exit_functions |
||
53 | ); |
||
54 | $all_catches_leave = $all_catches_leave && !in_array(ScopeAnalyzer::ACTION_NONE, $catch_actions[$i], true); |
||
55 | } |
||
56 | |||
57 | $existing_thrown_exceptions = $context->possibly_thrown_exceptions; |
||
58 | |||
59 | /** |
||
60 | * @var array<string, array<array-key, CodeLocation>> |
||
61 | */ |
||
62 | $context->possibly_thrown_exceptions = []; |
||
63 | |||
64 | $old_context = clone $context; |
||
65 | |||
66 | if ($all_catches_leave && !$stmt->finally) { |
||
67 | $try_context = $context; |
||
68 | } else { |
||
69 | $try_context = clone $context; |
||
70 | |||
71 | if ($codebase->alter_code) { |
||
72 | $try_context->branch_point = $try_context->branch_point ?: (int) $stmt->getAttribute('startFilePos'); |
||
73 | } |
||
74 | } |
||
75 | |||
76 | $assigned_var_ids = $try_context->assigned_var_ids; |
||
77 | $context->assigned_var_ids = []; |
||
78 | |||
79 | $old_referenced_var_ids = $try_context->referenced_var_ids; |
||
80 | $old_unreferenced_vars = $try_context->unreferenced_vars; |
||
81 | |||
82 | $newly_unreferenced_vars = []; |
||
83 | |||
84 | if ($statements_analyzer->analyze($stmt->stmts, $context) === false) { |
||
85 | return false; |
||
86 | } |
||
87 | |||
88 | $context->has_returned = false; |
||
89 | |||
90 | $stmt_control_actions = ScopeAnalyzer::getFinalControlActions( |
||
91 | $stmt->stmts, |
||
92 | $statements_analyzer->node_data, |
||
93 | $codebase->config->exit_functions, |
||
94 | $context->break_types |
||
95 | ); |
||
96 | |||
97 | /** @var array<string, bool> */ |
||
98 | $newly_assigned_var_ids = $context->assigned_var_ids; |
||
99 | |||
100 | $context->assigned_var_ids = array_merge( |
||
101 | $assigned_var_ids, |
||
102 | $newly_assigned_var_ids |
||
103 | ); |
||
104 | |||
105 | $possibly_referenced_var_ids = array_merge( |
||
106 | $context->referenced_var_ids, |
||
107 | $old_referenced_var_ids |
||
108 | ); |
||
109 | |||
110 | if ($try_context !== $context) { |
||
111 | foreach ($context->vars_in_scope as $var_id => $type) { |
||
112 | if (!isset($try_context->vars_in_scope[$var_id])) { |
||
113 | $try_context->vars_in_scope[$var_id] = clone $type; |
||
114 | $try_context->vars_in_scope[$var_id]->from_docblock = true; |
||
115 | } else { |
||
116 | $try_context->vars_in_scope[$var_id] = Type::combineUnionTypes( |
||
117 | $try_context->vars_in_scope[$var_id], |
||
118 | $type |
||
119 | ); |
||
120 | } |
||
121 | } |
||
122 | |||
123 | $try_context->vars_possibly_in_scope = $context->vars_possibly_in_scope; |
||
124 | $try_context->possibly_thrown_exceptions = $context->possibly_thrown_exceptions; |
||
125 | |||
126 | $context->referenced_var_ids = array_intersect_key( |
||
127 | $try_context->referenced_var_ids, |
||
128 | $context->referenced_var_ids |
||
129 | ); |
||
130 | |||
131 | if ($codebase->find_unused_variables) { |
||
132 | $newly_unreferenced_vars = array_merge( |
||
133 | $newly_unreferenced_vars, |
||
134 | array_diff_key( |
||
135 | $context->unreferenced_vars, |
||
136 | $old_unreferenced_vars |
||
137 | ) |
||
138 | ); |
||
139 | } |
||
140 | } |
||
141 | |||
142 | $try_leaves_loop = $context->loop_scope |
||
143 | && $context->loop_scope->final_actions |
||
144 | && !in_array(ScopeAnalyzer::ACTION_NONE, $context->loop_scope->final_actions, true); |
||
145 | |||
146 | if (!$all_catches_leave) { |
||
147 | foreach ($newly_assigned_var_ids as $assigned_var_id => $_) { |
||
148 | $context->removeVarFromConflictingClauses($assigned_var_id); |
||
149 | } |
||
150 | } else { |
||
151 | foreach ($newly_assigned_var_ids as $assigned_var_id => $_) { |
||
152 | $try_context->removeVarFromConflictingClauses($assigned_var_id); |
||
153 | } |
||
154 | } |
||
155 | |||
156 | // at this point we have two contexts – $context, in which it is assumed that everything was fine, |
||
157 | // and $try_context - which allows all variables to have the union of the values before and after |
||
158 | // the try was applied |
||
159 | $original_context = clone $try_context; |
||
160 | |||
161 | $issues_to_suppress = [ |
||
162 | 'RedundantCondition', |
||
163 | 'RedundantConditionGivenDocblockType', |
||
164 | 'TypeDoesNotContainNull', |
||
165 | 'TypeDoesNotContainType', |
||
166 | ]; |
||
167 | |||
168 | $definitely_newly_assigned_var_ids = $newly_assigned_var_ids; |
||
169 | |||
170 | /** @var int $i */ |
||
171 | foreach ($stmt->catches as $i => $catch) { |
||
172 | $catch_context = clone $original_context; |
||
173 | $catch_context->has_returned = false; |
||
174 | |||
175 | foreach ($catch_context->vars_in_scope as $var_id => $type) { |
||
176 | if (!isset($old_context->vars_in_scope[$var_id])) { |
||
177 | $type = clone $type; |
||
178 | $type->possibly_undefined_from_try = true; |
||
179 | $catch_context->vars_in_scope[$var_id] = $type; |
||
180 | } else { |
||
181 | $catch_context->vars_in_scope[$var_id] = Type::combineUnionTypes( |
||
182 | $type, |
||
183 | $old_context->vars_in_scope[$var_id] |
||
184 | ); |
||
185 | } |
||
186 | |||
187 | View Code Duplication | if (isset($old_context->unreferenced_vars[$var_id])) { |
|
|
|||
188 | if (!isset($catch_context->unreferenced_vars[$var_id])) { |
||
189 | $catch_context->unreferenced_vars[$var_id] = $old_context->unreferenced_vars[$var_id]; |
||
190 | } else { |
||
191 | $catch_context->unreferenced_vars[$var_id] += $old_context->unreferenced_vars[$var_id]; |
||
192 | } |
||
193 | } |
||
194 | } |
||
195 | |||
196 | $fq_catch_classes = []; |
||
197 | |||
198 | if (!$catch->types) { |
||
199 | throw new \UnexpectedValueException('Very bad'); |
||
200 | } |
||
201 | |||
202 | foreach ($catch->types as $catch_type) { |
||
203 | $fq_catch_class = ClassLikeAnalyzer::getFQCLNFromNameObject( |
||
204 | $catch_type, |
||
205 | $statements_analyzer->getAliases() |
||
206 | ); |
||
207 | |||
208 | $fq_catch_class = $codebase->classlikes->getUnAliasedName($fq_catch_class); |
||
209 | |||
210 | if ($codebase->alter_code && $fq_catch_class) { |
||
211 | $codebase->classlikes->handleClassLikeReferenceInMigration( |
||
212 | $codebase, |
||
213 | $statements_analyzer, |
||
214 | $catch_type, |
||
215 | $fq_catch_class, |
||
216 | $context->calling_method_id |
||
217 | ); |
||
218 | } |
||
219 | |||
220 | if ($original_context->check_classes) { |
||
221 | if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName( |
||
222 | $statements_analyzer, |
||
223 | $fq_catch_class, |
||
224 | new CodeLocation($statements_analyzer->getSource(), $catch_type, $context->include_location), |
||
225 | $context->self, |
||
226 | $context->calling_method_id, |
||
227 | $statements_analyzer->getSuppressedIssues(), |
||
228 | false |
||
229 | ) === false) { |
||
230 | return false; |
||
231 | } |
||
232 | } |
||
233 | |||
234 | if (($codebase->classExists($fq_catch_class) |
||
235 | && strtolower($fq_catch_class) !== 'exception' |
||
236 | && !($codebase->classExtends($fq_catch_class, 'Exception') |
||
237 | || $codebase->classImplements($fq_catch_class, 'Throwable'))) |
||
238 | || ($codebase->interfaceExists($fq_catch_class) |
||
239 | && strtolower($fq_catch_class) !== 'throwable' |
||
240 | && !$codebase->interfaceExtends($fq_catch_class, 'Throwable')) |
||
241 | ) { |
||
242 | if (IssueBuffer::accepts( |
||
243 | new InvalidCatch( |
||
244 | 'Class/interface ' . $fq_catch_class . ' cannot be caught', |
||
245 | new CodeLocation($statements_analyzer->getSource(), $stmt), |
||
246 | $fq_catch_class |
||
247 | ), |
||
248 | $statements_analyzer->getSuppressedIssues() |
||
249 | )) { |
||
250 | return false; |
||
251 | } |
||
252 | } |
||
253 | |||
254 | $fq_catch_classes[] = $fq_catch_class; |
||
255 | } |
||
256 | |||
257 | if ($catch_context->collect_exceptions) { |
||
258 | foreach ($fq_catch_classes as $fq_catch_class) { |
||
259 | $fq_catch_class_lower = strtolower($fq_catch_class); |
||
260 | |||
261 | foreach ($catch_context->possibly_thrown_exceptions as $exception_fqcln => $_) { |
||
262 | $exception_fqcln_lower = strtolower($exception_fqcln); |
||
263 | |||
264 | if ($exception_fqcln_lower === $fq_catch_class_lower |
||
265 | || ($codebase->classExists($exception_fqcln) |
||
266 | && $codebase->classExtendsOrImplements($exception_fqcln, $fq_catch_class)) |
||
267 | || ($codebase->interfaceExists($exception_fqcln) |
||
268 | && $codebase->interfaceExtends($exception_fqcln, $fq_catch_class)) |
||
269 | ) { |
||
270 | unset($original_context->possibly_thrown_exceptions[$exception_fqcln]); |
||
271 | unset($context->possibly_thrown_exceptions[$exception_fqcln]); |
||
272 | unset($catch_context->possibly_thrown_exceptions[$exception_fqcln]); |
||
273 | } |
||
274 | } |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * @var array<string, array<array-key, CodeLocation>> |
||
279 | */ |
||
280 | $catch_context->possibly_thrown_exceptions = []; |
||
281 | } |
||
282 | |||
283 | // discard all clauses because crazy stuff may have happened in try block |
||
284 | $catch_context->clauses = []; |
||
285 | |||
286 | /** @psalm-suppress RedundantConditionGivenDocblockType */ |
||
287 | if ($catch->var && is_string($catch->var->name)) { |
||
288 | $catch_var_id = '$' . $catch->var->name; |
||
289 | |||
290 | $catch_context->vars_in_scope[$catch_var_id] = new Union( |
||
291 | array_map( |
||
292 | /** |
||
293 | * @param string $fq_catch_class |
||
294 | * |
||
295 | * @return Type\Atomic |
||
296 | */ |
||
297 | function ($fq_catch_class) use ($codebase) { |
||
298 | $catch_class_type = new TNamedObject($fq_catch_class); |
||
299 | |||
300 | if (version_compare(PHP_VERSION, '7.0.0dev', '>=') |
||
301 | && strtolower($fq_catch_class) !== 'throwable' |
||
302 | && $codebase->interfaceExists($fq_catch_class) |
||
303 | && !$codebase->interfaceExtends($fq_catch_class, 'Throwable') |
||
304 | ) { |
||
305 | $catch_class_type->addIntersectionType(new TNamedObject('Throwable')); |
||
306 | } |
||
307 | |||
308 | return $catch_class_type; |
||
309 | }, |
||
310 | $fq_catch_classes |
||
311 | ) |
||
312 | ); |
||
313 | |||
314 | $catch_context->vars_possibly_in_scope[$catch_var_id] = true; |
||
315 | |||
316 | if (!$statements_analyzer->hasVariable($catch_var_id)) { |
||
317 | $location = new CodeLocation( |
||
318 | $statements_analyzer, |
||
319 | $catch->var, |
||
320 | $context->include_location |
||
321 | ); |
||
322 | $statements_analyzer->registerVariable( |
||
323 | $catch_var_id, |
||
324 | $location, |
||
325 | $try_context->branch_point |
||
326 | ); |
||
327 | $catch_context->unreferenced_vars[$catch_var_id] = [$location->getHash() => $location]; |
||
328 | } |
||
329 | |||
330 | // this registers the variable to avoid unfair deadcode issues |
||
331 | $catch_context->hasVariable($catch_var_id, $statements_analyzer); |
||
332 | } |
||
333 | |||
334 | $suppressed_issues = $statements_analyzer->getSuppressedIssues(); |
||
335 | |||
336 | foreach ($issues_to_suppress as $issue_to_suppress) { |
||
337 | if (!in_array($issue_to_suppress, $suppressed_issues, true)) { |
||
338 | $statements_analyzer->addSuppressedIssues([$issue_to_suppress]); |
||
339 | } |
||
340 | } |
||
341 | |||
342 | $old_catch_assigned_var_ids = $catch_context->referenced_var_ids; |
||
343 | |||
344 | $catch_context->assigned_var_ids = []; |
||
345 | |||
346 | $statements_analyzer->analyze($catch->stmts, $catch_context); |
||
347 | |||
348 | // recalculate in case there's a no-return clause |
||
349 | $catch_actions[$i] = ScopeAnalyzer::getFinalControlActions( |
||
350 | $catch->stmts, |
||
351 | $statements_analyzer->node_data, |
||
352 | $codebase->config->exit_functions, |
||
353 | $context->break_types |
||
354 | ); |
||
355 | |||
356 | foreach ($issues_to_suppress as $issue_to_suppress) { |
||
357 | if (!in_array($issue_to_suppress, $suppressed_issues, true)) { |
||
358 | $statements_analyzer->removeSuppressedIssues([$issue_to_suppress]); |
||
359 | } |
||
360 | } |
||
361 | |||
362 | /** @var array<string, bool> */ |
||
363 | $new_catch_assigned_var_ids = $catch_context->assigned_var_ids; |
||
364 | |||
365 | $catch_context->assigned_var_ids += $old_catch_assigned_var_ids; |
||
366 | |||
367 | $context->referenced_var_ids = array_intersect_key( |
||
368 | $catch_context->referenced_var_ids, |
||
369 | $context->referenced_var_ids |
||
370 | ); |
||
371 | |||
372 | $possibly_referenced_var_ids = array_merge( |
||
373 | $catch_context->referenced_var_ids, |
||
374 | $possibly_referenced_var_ids |
||
375 | ); |
||
376 | |||
377 | if ($codebase->find_unused_variables && $catch_actions[$i] !== [ScopeAnalyzer::ACTION_END]) { |
||
378 | $newly_unreferenced_vars = array_merge( |
||
379 | $newly_unreferenced_vars, |
||
380 | array_diff_key( |
||
381 | $catch_context->unreferenced_vars, |
||
382 | $old_unreferenced_vars |
||
383 | ) |
||
384 | ); |
||
385 | |||
386 | foreach ($catch_context->unreferenced_vars as $var_id => $locations) { |
||
387 | if (!isset($old_unreferenced_vars[$var_id]) |
||
388 | && (isset($context->unreferenced_vars[$var_id]) |
||
389 | || isset($newly_assigned_var_ids[$var_id])) |
||
390 | ) { |
||
391 | $statements_analyzer->registerVariableUses($locations); |
||
392 | } elseif (isset($old_unreferenced_vars[$var_id]) |
||
393 | && $old_unreferenced_vars[$var_id] !== $locations |
||
394 | ) { |
||
395 | $statements_analyzer->registerVariableUses($locations); |
||
396 | } elseif (isset($newly_unreferenced_vars[$var_id])) { |
||
397 | $context->unreferenced_vars[$var_id] = $newly_unreferenced_vars[$var_id]; |
||
398 | } |
||
399 | } |
||
400 | } |
||
401 | |||
402 | if ($catch_context->collect_exceptions) { |
||
403 | $context->mergeExceptions($catch_context); |
||
404 | } |
||
405 | |||
406 | if ($catch_actions[$i] !== [ScopeAnalyzer::ACTION_END] |
||
407 | && $catch_actions[$i] !== [ScopeAnalyzer::ACTION_CONTINUE] |
||
408 | && $catch_actions[$i] !== [ScopeAnalyzer::ACTION_BREAK] |
||
409 | ) { |
||
410 | $definitely_newly_assigned_var_ids = array_intersect_key( |
||
411 | $new_catch_assigned_var_ids, |
||
412 | $definitely_newly_assigned_var_ids |
||
413 | ); |
||
414 | |||
415 | foreach ($catch_context->vars_in_scope as $var_id => $type) { |
||
416 | if ($stmt_control_actions === [ScopeAnalyzer::ACTION_END]) { |
||
417 | $context->vars_in_scope[$var_id] = $type; |
||
418 | } elseif (isset($context->vars_in_scope[$var_id]) |
||
419 | && !$context->vars_in_scope[$var_id]->equals($type) |
||
420 | ) { |
||
421 | $context->vars_in_scope[$var_id] = Type::combineUnionTypes( |
||
422 | $context->vars_in_scope[$var_id], |
||
423 | $type |
||
424 | ); |
||
425 | } |
||
426 | } |
||
427 | |||
428 | $context->vars_possibly_in_scope = array_merge( |
||
429 | $catch_context->vars_possibly_in_scope, |
||
430 | $context->vars_possibly_in_scope |
||
431 | ); |
||
432 | } else { |
||
433 | if ($stmt->finally) { |
||
434 | $context->vars_possibly_in_scope = array_merge( |
||
435 | $catch_context->vars_possibly_in_scope, |
||
436 | $context->vars_possibly_in_scope |
||
437 | ); |
||
438 | } |
||
439 | } |
||
440 | |||
441 | View Code Duplication | if ($stmt->finally) { |
|
442 | $suppressed_issues = $statements_analyzer->getSuppressedIssues(); |
||
443 | |||
444 | foreach ($issues_to_suppress as $issue_to_suppress) { |
||
445 | if (!in_array($issue_to_suppress, $suppressed_issues, true)) { |
||
446 | $statements_analyzer->addSuppressedIssues([$issue_to_suppress]); |
||
447 | } |
||
448 | } |
||
449 | |||
450 | $catch_context->has_returned = false; |
||
451 | |||
452 | $statements_analyzer->analyze($stmt->finally->stmts, $catch_context); |
||
453 | |||
454 | foreach ($issues_to_suppress as $issue_to_suppress) { |
||
455 | if (!in_array($issue_to_suppress, $suppressed_issues, true)) { |
||
456 | $statements_analyzer->removeSuppressedIssues([$issue_to_suppress]); |
||
457 | } |
||
458 | } |
||
459 | } |
||
460 | } |
||
461 | |||
462 | foreach ($definitely_newly_assigned_var_ids as $var_id => $_) { |
||
463 | if (isset($context->vars_in_scope[$var_id])) { |
||
464 | $new_type = clone $context->vars_in_scope[$var_id]; |
||
465 | $new_type->possibly_undefined_from_try = false; |
||
466 | $context->vars_in_scope[$var_id] = $new_type; |
||
467 | } |
||
468 | } |
||
469 | |||
470 | if ($context->loop_scope |
||
471 | && !$try_leaves_loop |
||
472 | && !in_array(ScopeAnalyzer::ACTION_NONE, $context->loop_scope->final_actions, true) |
||
473 | ) { |
||
474 | $context->loop_scope->final_actions[] = ScopeAnalyzer::ACTION_NONE; |
||
475 | } |
||
476 | |||
477 | $newly_referenced_var_ids = array_diff_key( |
||
478 | $context->referenced_var_ids, |
||
479 | $old_referenced_var_ids |
||
480 | ); |
||
481 | |||
482 | if ($codebase->find_unused_variables) { |
||
483 | foreach ($old_unreferenced_vars as $var_id => $locations) { |
||
484 | if ((isset($context->unreferenced_vars[$var_id]) && $context->unreferenced_vars[$var_id] !== $locations) |
||
485 | || (!isset($newly_referenced_var_ids[$var_id]) && isset($possibly_referenced_var_ids[$var_id])) |
||
486 | ) { |
||
487 | $statements_analyzer->registerVariableUses($locations); |
||
488 | } |
||
489 | } |
||
490 | |||
491 | $newly_unreferenced_vars = array_merge( |
||
492 | $newly_unreferenced_vars, |
||
493 | array_diff_key( |
||
494 | $try_context->unreferenced_vars, |
||
495 | $old_unreferenced_vars |
||
496 | ) |
||
497 | ); |
||
498 | |||
499 | foreach ($newly_unreferenced_vars as $var_id => $locations) { |
||
500 | if (!isset($context->unreferenced_vars[$var_id])) { |
||
501 | $context->unreferenced_vars[$var_id] = $locations; |
||
502 | } |
||
503 | } |
||
504 | } |
||
505 | |||
506 | View Code Duplication | if ($stmt->finally) { |
|
507 | $suppressed_issues = $statements_analyzer->getSuppressedIssues(); |
||
508 | |||
509 | foreach ($issues_to_suppress as $issue_to_suppress) { |
||
510 | if (!in_array($issue_to_suppress, $suppressed_issues, true)) { |
||
511 | $statements_analyzer->addSuppressedIssues([$issue_to_suppress]); |
||
512 | } |
||
513 | } |
||
514 | |||
515 | $statements_analyzer->analyze($stmt->finally->stmts, $context); |
||
516 | |||
517 | foreach ($issues_to_suppress as $issue_to_suppress) { |
||
518 | if (!in_array($issue_to_suppress, $suppressed_issues, true)) { |
||
519 | $statements_analyzer->removeSuppressedIssues([$issue_to_suppress]); |
||
520 | } |
||
521 | } |
||
522 | } |
||
523 | |||
524 | foreach ($existing_thrown_exceptions as $possibly_thrown_exception => $codelocations) { |
||
525 | foreach ($codelocations as $hash => $codelocation) { |
||
526 | $context->possibly_thrown_exceptions[$possibly_thrown_exception][$hash] = $codelocation; |
||
527 | } |
||
528 | } |
||
529 | |||
530 | return null; |
||
531 | } |
||
532 | } |
||
533 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.