shipmonk-rnd/dead-code-detector

repository·master·Indexed 19 days ago

https://github.com/shipmonk-rnd/dead-code-detector

A PHPStan extension for detecting unused PHP code, including dead cycles, transitive dead members, and code used only in tests. It supports automatic dead code removal, custom MemberUsageProvider and MemberUsageExcluder implementations, and integration with libraries like Symfony, Doctrine, and PHPUnit.

Tokens
3K
Snippets
15
Records
16
Agent score
17%

What's inside dead-code-detector

  1. Marking entrypoints in libraries using @api

    master
    When developing a library, public APIs will naturally appear 'unused' to the detector. To prevent this, you can use the @api PHPDoc tag. You can mark individual methods with @api, or mark an entire class or interface with @api to treat all its members as entrypoints.
  2. Configure PHPStan to use Dead Code Detector

    master

    If you are not using the official extension-installer, add the following to your phpstan.neon.dist file to load the rules:

    includes:
        - vendor/shipmonk/dead-code-detector/rules.neon
    # phpstan.neon.dist
    includes:
        - vendor/shipmonk/dead-code-detector/rules.neon
  3. Install the Dead Code Detector

    master

    Install the extension as a development dependency using Composer:

    composer require --dev shipmonk/dead-code-detector

    To activate the rules in PHPStan, you can either use the official PHPStan extension-installer or manually include the rules file in your phpstan.neon.dist configuration.

  4. Configure detected class members

    master

    By default, all dead class member types are detected. You can customize which types are checked using the shipmonkDeadCode.detect configuration key in your phpstan.neon.dist.

    parameters:
        shipmonkDeadCode:
            detect:
                deadMethods: true
                deadConstants: true
                deadEnumCases: true
                deadProperties:
                    neverRead: true
                    neverWritten: true
  5. Exclude usages in tests

    master

    By default, any usage within your scanned paths marks a member as used. If you want to identify code that is only used within your test suite (and thus potentially dead in production), enable the tests usage excluder.

    When enabled, members used only in tests will be reported with a message like: Unused AddressValidator::isValidPostalCode (all usages excluded by tests excluder).

    Recommendation: It is recommended to enable this excluder for all projects.

    parameters:
        shipmonkDeadCode:
            usageExcluders:
                tests:
                    enabled: true
                    devPaths: # optional, autodetects from autoload-dev sections of composer.json when omitted
                        - %currentWorkingDirectory%/tests
  6. Handle calls over unknown types (Mixed)

    master

    To prevent false positives, the library marks all methods/constants named after a call over an unknown type (e.g., $unknown->method()) as used.

    If your codebase is strictly typed and you want to disable this behavior to avoid marking all constructors or constants as used, you can enable the usageOverMixed excluder in your phpstan.neon.dist.

    parameters:
        shipmonkDeadCode:
            usageExcluders:
                usageOverMixed:
                    enabled: true
  7. Enable or disable supported library providers

    master

    The detector automatically enables support for popular libraries (Symfony, Doctrine, PHPUnit, etc.) when they are found in your composer dependencies. You can manually force-enable or disable a specific provider using the shipmonkDeadCode.usageProviders.{provider}.enabled key.

    parameters:
        shipmonkDeadCode:
            usageProviders:
                phpunit:
                    enabled: true
  8. Configure reporting for transitively dead methods

    master

    By default, the library reports only the first dead method in a subtree and lists subsequent transitively dead methods as tips (💡). If you prefer to have every transitively dead method reported as its own individual error, enable reportTransitivelyDeadMethodAsSeparateError in your phpstan.neon.dist configuration.

    parameters:
        shipmonkDeadCode:
            reportTransitivelyDeadMethodAsSeparateError: true
  9. Fixing false positives during partial PHPStan analysis

    master

    Dead code detection requires a full codebase analysis and is automatically disabled during partial analysis (when only specific files are passed to PHPStan). This causes inline ignores (e.g., // @phpstan-ignore shipmonk.deadMethod) to report as unmatched errors.

    To fix this, use the filterOutUnmatchedInlineIgnoresDuringPartialAnalysis error format in your configuration.

    parameters:
        errorFormat: filterOutUnmatchedInlineIgnoresDuringPartialAnalysis
    
        # optionally:
        shipmonkDeadCode:
            filterOutUnmatchedInlineIgnoresDuringPartialAnalysis:
                wrappedErrorFormatter: table
  10. Debug dead code usage evaluation

    master

    To understand why a specific member is marked as alive or dead, you can provide a list of members to debug in your phpstan.neon.dist. After configuring this, run PHPStan with the -vvv flag to see the trace of how the member was evaluated (e.g., which provider marked it as alive and the call chain leading to it).

    parameters:
        shipmonkDeadCode:
            debug:
                usagesOf:
                    - App\User\Entity\Address::__construct
  11. Customizing dead code detection via Reflection

    master

    You can extend the detection logic by implementing a custom ReflectionBasedMemberUsageProvider. This is useful for handling cases like serialization or interface methods that should be considered 'used' even if no direct call is found.

    Example: Marking properties as read during JSON serialization

    If your API output objects implement a specific interface, you can use shouldMarkPropertyAsRead to prevent them from being reported as dead.

    use ReflectionProperty;
    use ShipMonk\PHPStan\DeadCode\Provider\VirtualUsageData;
    use ShipMonk\PHPStan\DeadCode\Provider\ReflectionBasedMemberUsageProvider;
    
    class ApiOutputPropertyUsageProvider extends ReflectionBasedMemberUsageProvider
    {
        protected function shouldMarkPropertyAsRead(ReflectionProperty $property): ?VirtualUsageData
        {
            if ($property->getDeclaringClass()->implementsInterface(ApiOutput::class)) {
                return VirtualUsageData::withNote('Used upon JSON serialization');
            }
    
            return null;
        }
    }