tomasvotruba/cognitive-complexity
A PHPStan extension that measures cognitive complexity of functions and classes, flagging code that's hard to read. Also detects complex constructor dependency trees in services like controllers and rules.
Installation Add the package via Composer (PHP 7.4+ required as of 1.3.0):
composer require --dev tomasvotruba/cognitive-complexity
Enable the rules in your phpstan.neon:
includes:
- vendor/tomasvotruba/cognitive-complexity/phpstan-rules.neon
First Run Execute PHPStan with the new rules:
vendor/bin/phpstan analyse src
Look for errors like:
CognitiveComplexity\TooHighCognitiveComplexityMethod
First Use Case Identify a method with high cognitive complexity (e.g., > 20) and refactor it. Note that as of 1.3.0, scores follow the reference cognitive complexity paper more closely, so numbers may shift versus earlier releases:
// Before (complex)
public function calculate($input): float {
if ($input > 10) {
if ($input % 2 === 0) {
return $input * 0.1;
} else {
return $input * 0.2;
}
} else {
return $input * 0.05;
}
}
// After (simplified)
public function calculate($input): float {
$factor = $this->getFactor($input);
return $input * $factor;
}
private function getFactor(float $input): float {
if ($input <= 10) return 0.05;
return $input % 2 === 0 ? 0.1 : 0.2;
}
Integrate with CI/CD Add PHPStan to your pipeline (e.g., GitHub Actions):
- name: PHPStan
run: vendor/bin/phpstan analyse --level=5 src
Fail builds if complexity exceeds thresholds.
Custom Thresholds
Override defaults in phpstan.neon:
parameters:
cognitiveComplexity:
maxMethodComplexity: 15
maxClassComplexity: 50
Focused Analysis Run on specific files/directories:
vendor/bin/phpstan analyse src/Service/
Pair with Other Tools
Combine with phpstan/extension-installer for seamless updates:
composer require --dev phpstan/extension-installer
Pin the Rule Version
Because 1.3.0 re-aligned scoring with the reference paper, pin the package (^1.3) in composer.lock so scores are reproducible across machines and CI agents.
excludeFiles:
- vendor/**
- bootstrap/**
@complexity PHPDoc tags to justify high-complexity methods, or use PHPStan's documented error-ignoring mechanism (see Gotchas) to suppress one-off violations:
/**
* @complexity 25 (legacy, but well-tested)
*/
public function legacyMethod() { ... }
Scoring Shift After Upgrade (1.3.0)
main after upgrading; recalibrate maxMethodComplexity / maxClassComplexity and refresh any stored baseline rather than blindly bumping thresholds.Dependency Tree Rule Removed (1.3.0)
phpstan.neon referenced it, PHPStan will error on the unknown service/rule.
Fix: Remove stale references from phpstan.neon and CI configs when upgrading to 1.3.0.False Positives
phpstan.neon:
excludeFiles:
- app/Models/*.php
Performance
vendor/bin/phpstan analyse --parallel
Configuration Overrides
phpstan.local.neon) can conflict with team settings.
Fix: Document override rules in CONTRIBUTING.md.PHP Version Floor
composer.json platform constraints before composer update.--error-format=json to analyze raw complexity data:
vendor/bin/phpstan analyse --error-format=json > complexity.json
@phpstan-ignore or an ignoreErrors entry for justified exceptions, rather than excluding entire files:
parameters:
ignoreErrors:
- '#CognitiveComplexity.*legacyMethod#'
Custom Rules Extend the package by creating your own PHPStan rules:
use PHPStan\Rules\Rule;
use CognitiveComplexity\Rules\ComplexityRule;
class CustomComplexityRule extends ComplexityRule {
protected function getMaxAllowedComplexity(): int { return 10; }
}
Register in phpstan.neon:
services:
- CognitiveComplexity\Rules\CustomComplexityRule
Error Ignoring
As documented in 1.3.0, leverage PHPStan's ignoreErrors and inline ignore comments for localized exceptions instead of forking the rule — this keeps the package upgradeable.
Integration with Linters
Sync with ESLint (via laravel-shift/laravel-phpstan) for frontend-backend parity.
Dynamic Thresholds Use environment variables for dynamic thresholds:
parameters:
cognitiveComplexity:
maxMethodComplexity: %env.int(MAX_METHOD_COMPLEXITY, 20)%
How can I help you explore Laravel packages today?