4. Challenges we met
● If-statements analysis:
○ Execution costs estimation;
○ Interconnected conditions; if (is_array($a) && $a[0] > 0) ;
○ Variadic constructions, booleans, identical sub-expressions detection and more;
● exceptions handling workflow analysis:
○ Simulation of the workflow for running analysis;
○ PhpDoc parsing: PHP is not supporting “throws” declarations;
○ Nested catch and finally has implementation issues in older PHP versions;
● analysis performance:
○ Concurrency (inspections are running in several independent threads);
○ GC: memory optimization (VisualVM, data structures);
○ Avoid low-performing analysis, stop as early as possible;
5. Challenges we met
● If-statements analysis:
○ Execution costs estimation;
○ Interconnected conditions;
○ Variadic constructions, booleans, identical sub-expressions detection and more;
6. If-statements analysis: patterns
● Execution costs: if ($var->method($a) && $b > 0) ;
● Identical operands: if ($a !== $a) ;
● Ambiguous type checks: if ($a instanceof Date || $a instanceof DateInterface) ;
● If ($a instanceof DateInterface && null !== $a) ;
● Variadic constructions: if (isset($a) && isset($b)) ; => if (isset($a, $b)) ;
● Hardcoded booleans: if ($a > 0 || true) ;
● Confusing conditions: if ($a > 0 || $a <= 0 && $a > $minValue) ;
● Duplicated expressions in elseif and nested ifs:
● If (is_array($a) || is_string($a)) {
● If (is_array($a) && count($a) > 0) ;
● }
7. Execution costs estimation: idea
The challenge has a name: “Shortest path problem” from Graphs theory (Discrete
mathematics).
Applying the problem e.g. to “if ($var->method($a) && $a > 0) ;” we have 2 paths:
● $var->method($a)
○ Method lookup ;
○ Calls stack: push and pop ;
○ Complex operation, therefore high execution costs ;
● $a > 0
○ Primitive operation, therefore low execution costs ;
8. Execution costs estimation: example
Let’s take more common case: if-else construct.
If (<conditions>) {
<operation 1>;
} else {
<operation 2>;
}
Formula for if-else construction cost estimation will be*:
C(<if-else>) = C(<conditions>) + max(C(<operation 1>), C(<operation 2>))
* The theory of parsing, translation, and compiling
9. Execution costs estimation: C() function
This is most important part: which weight to assign to different constructs?
For this you need know your compiler/interpreter internals and language
capabilities.
Example weights specific for PHP:
● Binary/Unary operations: 0 ; (primitive operations)
● Array access: +1 (hash-maps based arrays implementation) ;
● Method/function reference: +5 (call stack invocation) ;
● Lambdas: +10 (no JIT compiler, dynamically allocated) ;
● etc.
10. Code samples: hooking into IDE
public class NotOptimalIfConditionsInspection extends BasePhpInspection {
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
public void visitPhpIf(If ifStatement) {
/* we are visiting a branch of AST-tree here, analyze it */
}
/* other visitors here */
};
}
}