|
| 1 | +# Dead code detector |
| 2 | + |
| 3 | +PHPStan rules to find dead code in your project. |
| 4 | + |
| 5 | +## Installation: |
| 6 | + |
| 7 | +```sh |
| 8 | +composer require --dev shipmonk/dead-code-detector |
| 9 | +``` |
| 10 | + |
| 11 | +Use [official extension-installer](https://phpstan.org/user-guide/extension-library#installing-extensions) or enable just load the rules: |
| 12 | + |
| 13 | +```neon |
| 14 | +includes: |
| 15 | + - vendor/shipmonk/dead-code-detector/rules.neon |
| 16 | +``` |
| 17 | + |
| 18 | + |
| 19 | +## Configuration: |
| 20 | +- You need to mark all entrypoints of your code to get proper results. |
| 21 | +- This is typically long whitelist of all code that is called by your framework and libraries. |
| 22 | + |
| 23 | +```neon |
| 24 | +services: |
| 25 | + - |
| 26 | + class: App\SymfonyEntrypointProvider |
| 27 | + tags: |
| 28 | + - shipmonk.deadCode.entrypointProvider |
| 29 | +``` |
| 30 | +```php |
| 31 | + |
| 32 | +use ReflectionMethod; |
| 33 | +use PHPStan\Reflection\ReflectionProvider; |
| 34 | +use ShipMonk\PHPStan\DeadCode\Provider\EntrypointProvider; |
| 35 | + |
| 36 | +class SymfonyEntrypointProvider implements EntrypointProvider |
| 37 | +{ |
| 38 | + |
| 39 | + public function __construct( |
| 40 | + private ReflectionProvider $reflectionProvider |
| 41 | + ) {} |
| 42 | + |
| 43 | + public function isEntrypoint(ReflectionMethod $method): bool |
| 44 | + { |
| 45 | + $methodName = $method->getName(); |
| 46 | + $reflection = $this->reflectionProvider->getClass($method->getDeclaringClass()->getName()); |
| 47 | + |
| 48 | + return $reflection->is(\Symfony\Bundle\FrameworkBundle\Controller\AbstractController::class) |
| 49 | + || $reflection->is(\Symfony\Component\EventDispatcher\EventSubscriberInterface::class) |
| 50 | + || $reflection->is(\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface::class) |
| 51 | + || ($reflection->is(\Symfony\Component\Console\Command\Command::class) && in_array($methodName, ['execute', 'initialize', ...], true) |
| 52 | + // and many more |
| 53 | + } |
| 54 | +} |
| 55 | +``` |
| 56 | + |
| 57 | +## Limitations |
| 58 | +This project is currently a working prototype (we are using it since 2022) with limited functionality: |
| 59 | + |
| 60 | +- Only method calls are detected |
| 61 | + - Including static methods, trait methods, interface methods, first class callables, etc. |
| 62 | + - Callbacks like `[$this, 'method']` are mostly not detected |
| 63 | + - Any calls on mixed types are not detected, e.g. `$unknownClass->method()` |
| 64 | + - Expression method calls are not detected, e.g. `$this->$methodName()` |
| 65 | + - Anonymous classes are ignored |
| 66 | + - Does not check constructor calls |
| 67 | + - Does not check magic methods |
| 68 | + - No transitive check is performed (dead method called only from dead method) |
| 69 | + - No dead cycles are detected (e.g. dead method calling itself) |
| 70 | + |
| 71 | +## Contributing |
| 72 | +- Check your code by `composer check` |
| 73 | +- Autofix coding-style by `composer fix:cs` |
| 74 | +- All functionality must be tested |
0 commit comments