Imagine walking through a labyrinth. If you reach an intersection with 2 choices, and then each of those paths leads to another intersection with 3 choices, there are 2 x 3 = 6 unique ways to walk through that small section of the maze.
NPath Complexity is similar to Cyclomatic Complexity, but instead of just adding branches, it multiplies them when they follow each other.
It counts the total number of distinct paths through a method.
If you have 4 if/else statements in a row:
If your score gets too high (default > 200), your method is an impossible maze. No human can hold all 200 possible paths in their head to understand what the method actually does.
Because these if statements follow each other, the number of possible execution paths explodes!
public function labyrinth(string $weather, bool $hasUmbrella, bool $isTired): string {
$plan = '';
// 2 paths
if ($weather === 'rain') {
$plan .= 'wear coat; ';
} else {
$plan .= 'wear t-shirt; ';
}
// 2 paths (Total paths so far: 2 x 2 = 4)
if ($hasUmbrella) {
$plan .= 'take umbrella; ';
} else {
$plan .= 'run fast; ';
}
// 2 paths (Total paths: 4 x 2 = 8)
if ($isTired) {
$plan .= 'walk slowly; ';
} else {
$plan .= 'skip; ';
}
return $plan;
}
Extract complex, multiplicative logic into smaller methods, or use lookups to keep the main flow straight.
public function getPlan(string $weather, bool $hasUmbrella, bool $isTired): string {
return sprintf(
'%s; %s; %s;',
$this->getClothing($weather),
$this->getRainGear($hasUmbrella),
$this->getPace($isTired)
);
}
// Each helper method now has an NPath of 2. Total complexity is vastly reduced!
rules:
E0010:
max_paths: 200
If Phanalist flags your method for NPath Complexity, it means you have too many if statements stacked on top of each other. Break those stacked conditionals out into separate helper methods!