PHPackages                             shipmonk/dead-code-detector - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. shipmonk/dead-code-detector

ActivePhpstan-extension[Testing &amp; Quality](/categories/testing)

shipmonk/dead-code-detector
===========================

Dead code detector to find unused PHP code via PHPStan extension. Can automatically remove dead PHP code. Supports libraries like Symfony, Doctrine, PHPUnit etc. Detects dead cycles. Can detect dead code that is tested.

1.1.3(3w ago)4753.1M—7%22[6 issues](https://github.com/shipmonk-rnd/dead-code-detector/issues)[3 PRs](https://github.com/shipmonk-rnd/dead-code-detector/pulls)20MITPHPPHP ^8.1CI passing

Since Dec 28Pushed 3w ago5 watchersCompare

[ Source](https://github.com/shipmonk-rnd/dead-code-detector)[ Packagist](https://packagist.org/packages/shipmonk/dead-code-detector)[ RSS](/packages/shipmonk-dead-code-detector/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (10)Dependencies (139)Versions (59)Used By (20)

Dead code detector for PHP
==========================

[](#dead-code-detector-for-php)

[PHPStan](https://phpstan.org/) extension to find unused PHP code in your project with ease!

Summary:
--------

[](#summary)

- ✅ **PHPStan** extension
- ♻️ **Dead cycles** detection
- 🔗 **Transitive dead** member detection
- 🧪 **Dead tested code** detection
- 🧹 **Automatic removal** of unused code
- 📚 **Popular libraries** support
- ✨ **Customizable** usage providers

Installation:
-------------

[](#installation)

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

Use [official extension-installer](https://phpstan.org/user-guide/extension-library#installing-extensions) or just load the rules:

```
# phpstan.neon.dist
includes:
    - vendor/shipmonk/dead-code-detector/rules.neon
```

Usage:
------

[](#usage)

```
$ vendor/bin/phpstan
```

Note

Make sure you analyse whole codebase (e.g. both `src` and `tests`) so that all usages are found.

Slides:
-------

[](#slides)

Check out the recording and slides from TechMeetup Conference (2025) about this package:

- 🎬 [Recording on YouTube](https://youtu.be/-cf1RbsCwnY?si=lXHI-HXbSowT_skv)
- 📋 [Presentation on Google Slides](https://docs.google.com/presentation/d/1wyi-zWKC80jk-z8QqNXsZ1mK4HpxPa5J6kO69UFC-rI/edit?usp=sharing)

Detected class members:
-----------------------

[](#detected-class-members)

All dead class member types are detected by default, you can disable some if needed:

```
parameters:
    shipmonkDeadCode:
        detect:
            deadMethods: true
            deadConstants: true
            deadEnumCases: true
            deadProperties:
                neverRead: true
                neverWritten: true
```

Supported libraries:
--------------------

[](#supported-libraries)

#### Symfony:

[](#symfony)

- Calls made by DIC over your services (constructors, calls, factory methods), requires either of those:
    - [`phpstan/phpstan-symfony`](https://github.com/phpstan/phpstan-symfony) with `containerXmlPath`
    - `shipmonkDeadCode.usageProviders.symfony.containerXmlPaths` configured
- `#[AsEventListener]`, `#[AsMessageHandler]`, `#[AsController]`, `#[AsCommand]`
- `#[Assert\Callback]`, `#[Interact]`, `#[Route]`, `#[Required]` (methods and properties)
- `#[AsSchedule]`, `#[AsCronTask]`, `#[AsPeriodicTask]`
- `#[AutoconfigureTag('doctrine.event_listener')]`, `#[Autoconfigure(constructor:)]`, `#[Autoconfigure(calls:)]`, `#[AutowireCallable]`
- `defaultIndexMethod` and `defaultPriorityMethod` in `#[AutowireLocator]`, `#[AutowireIterator]`, `#[TaggedIterator]`, `#[TaggedLocator]`
- Workflow event listener attributes: `#[AsAnnounceListener]`, ...
- `EventSubscriberInterface::getSubscribedEvents`
- `onKernelResponse`, `onKernelRequest`, etc
- `!php/const` and `!php/enum` references in `config` yamls
- Symfony UX: `#[AsTwigComponent]`/`#[AsLiveComponent]` (constructor, `mount()`), `#[LiveProp]`, `#[LiveAction]`, `#[LiveListener]`, lifecycle hooks

#### Doctrine:

[](#doctrine)

- `#[AsEntityListener]`, `#[AsDoctrineListener]` attribute
- `Doctrine\ORM\Events::*` events, `Doctrine\Common\EventSubscriber` methods
- `repositoryMethod` in `#[UniqueEntity]` attribute
- lifecycle event attributes `#[PreFlush]`, `#[PostLoad]`, ...
- enums in `#[Column(enumType: UserStatus::class)]`

#### PHPUnit:

[](#phpunit)

- **data provider methods**
- `testXxx` methods
- annotations like `@test`, `@before`, `@afterClass` etc
- attributes like `#[Test]`, `#[Before]`, `#[AfterClass]` etc

#### PhpBench:

[](#phpbench)

- `benchXxx` methods
- `#[BeforeMethods]`, `#[AfterMethods]` attributes
- `#[ParamProviders]` attribute for param provider methods

#### Behat:

[](#behat)

- context class constructors
- step definitions via annotations (`@Given`, `@When`, `@Then`) or attributes (`#[Given]`, `#[When]`, `#[Then]`)
- hooks via annotations (`@BeforeScenario`, `@AfterScenario`, etc.) or attributes (`#[BeforeScenario]`, `#[AfterScenario]`, etc.)
- transformations via `@Transform` or `#[Transform]`

#### PHPStan:

[](#phpstan)

- constructor calls for DIC services (rules, extensions, ...)

#### Nette:

[](#nette)

- `handleXxx`, `renderXxx`, `actionXxx`, `injectXxx`, `createComponentXxx`
- `SmartObject` magic calls for `@property` annotations

#### Nette Tester:

[](#nette-tester)

- `test*` methods, `setUp`/`tearDown`, `@dataProvider` methods in `Tester\TestCase` subclasses

#### Laravel:

[](#laravel)

- Route registration — `Route::get/post/put/...()`, `resource()`, `apiResource()` with callable, string (`Controller@method`), and invokable syntax
- Event listeners — `Event::listen()`, `Event::subscribe()`, auto-discovered listeners (`handle*`/`__invoke` with typed first param)
- Scheduled jobs — `Schedule::job()`
- Gates &amp; policies — `Gate::define()`, `Gate::policy()`, `$this->authorize()` with automatic policy class resolution
- Console commands, jobs, service providers, middleware, notifications, form requests, mailables, broadcast events, JSON resources, notifiable routing

#### Eloquent:

[](#eloquent)

- Model methods — constructor, `boot`, `booted`, `casts`, `newFactory`, query scopes, relationships, attribute accessors (modern + legacy)
- Observers — `Model::observe()` + `#[ObservedBy]` attribute
- Factories (`definition`, `configure`), seeders (`run`), migrations (`up`, `down`)

#### Twig:

[](#twig)

- View objects passed as parameters to twig templates (including transitively referenced ones)
    - Passed to `$controller->render('my.twig', ['param' => $viewModel])`,
    - Returned from `#[Template]` controller methods
    - Rendered via `Twig\Environment::render()` and similar
- `#[AsTwigFilter]`, `#[AsTwigFunction]`, `#[AsTwigTest]`
- `new TwigFilter(..., callback)`, `new TwigFunction(..., callback)`, `new TwigTest(..., callback)`

All those libraries are autoenabled when found within your composer dependencies. If you want to force enable/disable some of them, you can:

```
parameters:
    shipmonkDeadCode:
        usageProviders:
            phpunit:
                enabled: true
```

Generic usage providers:
------------------------

[](#generic-usage-providers)

#### Reflection:

[](#reflection)

- Any property, enum, constant or method accessed via `ReflectionClass` is detected as used
    - e.g. `$reflection->getConstructor()`, `$reflection->getConstant('NAME')`, `$reflection->getMethods()`, `$reflection->getCases()`...

#### Vendor:

[](#vendor)

- Any overridden method that originates in `vendor` is not reported as dead
    - e.g. implementing `Psr\Log\LoggerInterface::log` is automatically considered used

#### Builtin:

[](#builtin)

- Any overridden method that originates from PHP core or extensions is not reported as dead
    - e.g. implementing `IteratorAggregate::getIterator` is automatically considered used

#### Enum:

[](#enum)

- Detects usages caused by `BackedEnum::from`, `BackedEnum::tryFrom` and `UnitEnum::cases`

#### StreamWrapper:

[](#streamwrapper)

- Detects usages caused by `stream_wrapper_register`

Those providers are enabled by default, but you can disable them if needed.

Excluding usages in tests:
--------------------------

[](#excluding-usages-in-tests)

- By default, all usages within scanned paths can mark members as used
- But that might not be desirable if class declared in `src` is **only used in `tests`**
- You can exclude those usages by enabling `tests` usage excluder:
    - This **will not disable analysis for tests** as only usages of src-defined classes will be excluded

```
parameters:
    shipmonkDeadCode:
        usageExcluders:
            tests:
                enabled: true
                devPaths: # optional, autodetects from autoload-dev sections of composer.json when omitted
                    - %currentWorkingDirectory%/tests
```

With such setup, members used only in tests will be reported with corresponding message, e.g:

```
Unused AddressValidator::isValidPostalCode (all usages excluded by tests excluder)

```

Tip

**We recommend enabling this excluder for all projects.**

Customization:
--------------

[](#customization)

- If your application does some magic calls unknown to this library, you can implement your own usage provider.
- Just tag it with `shipmonk.deadCode.memberUsageProvider` and implement `ShipMonk\PHPStan\DeadCode\Provider\MemberUsageProvider`

```
services:
    -
        class: App\ApiOutputUsageProvider
        tags:
            - shipmonk.deadCode.memberUsageProvider
```

Important

*The interface &amp; tag changed in [0.7](../../releases/tag/0.7.0). If you are using PHPStan 1.x, those were [used differently](../../blob/0.5.0/README.md#customization).*

### Reflection-based customization:

[](#reflection-based-customization)

- For simple reflection usecases, you can just extend `ShipMonk\PHPStan\DeadCode\Provider\ReflectionBasedMemberUsageProvider`:

```
use ReflectionMethod;
use ShipMonk\PHPStan\DeadCode\Provider\VirtualUsageData;
use ShipMonk\PHPStan\DeadCode\Provider\ReflectionBasedMemberUsageProvider;

class FuzzyTwigUsageProvider extends ReflectionBasedMemberUsageProvider
{

    public function shouldMarkMethodAsUsed(ReflectionMethod $method): ?VirtualUsageData
    {
        if ($method->getDeclaringClass()->implementsInterface(UsedInTwigMarkerInterface::class)) {
            return VirtualUsageData::withNote('Probably used in twig');
        }
        return null;
    }

}
```

### AST-based customization:

[](#ast-based-customization)

- For more complex usecases that are deducible only from AST, you just stick with raw `MemberUsageProvider` interface.
- Here is simplified example how to emit `User::__construct` usage in following PHP snippet:

```
function test(SerializerInterface $serializer): User {
    return $serializer->deserialize('{"name": "John"}', User::class, 'json');
}
```

```
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use ReflectionMethod;
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodRef;
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodUsage;
use ShipMonk\PHPStan\DeadCode\Graph\UsageOrigin;
use ShipMonk\PHPStan\DeadCode\Provider\MemberUsageProvider;
use Symfony\Component\Serializer\SerializerInterface;

class DeserializationUsageProvider implements MemberUsageProvider
{

    public function __construct(
        private UsageOriginDetector $originDetector,
    ) {}

    /**
     * @return list
     */
    public function getUsages(Node $node, Scope $scope): array
    {
        if (!$node instanceof MethodCall) {
            return [];
        }

        if (
            // our deserialization calls constructor
            $scope->getType($node->var)->getObjectClassNames() === [SerializerInterface::class] &&
            $node->name->toString() === 'deserialize'
        ) {
            $secondArgument = $node->getArgs()[1]->value;
            $serializedClass = $scope->getType($secondArgument)->getConstantStrings()[0];

            // record the place it was called from (needed for proper transitive dead code elimination)
            $usageOrigin = UsageOrigin::createRegular($node, $scope);

            // record the hidden constructor call
            $constructorRef = new ClassMethodRef($serializedClass->getValue(), '__construct', possibleDescendant: false);

            return [new ClassMethodUsage($usageOrigin, $constructorRef)];
        }

        return [];
    }

}
```

### Excluding usages:

[](#excluding-usages)

You can exclude any usage based on custom logic, just implement `MemberUsageExcluder` and register it with `shipmonk.deadCode.memberUsageExcluder` tag:

```
use ShipMonk\PHPStan\DeadCode\Excluder\MemberUsageExcluder;

class MyUsageExcluder implements MemberUsageExcluder
{

    public function shouldExclude(ClassMemberUsage $usage, Node $node, Scope $scope): bool
    {
        // ...
    }

}
```

```
# phpstan.neon.dist
services:
    -
        class: App\MyUsageExcluder
        tags:
            - shipmonk.deadCode.memberUsageExcluder
```

The same interface is used for exclusion of test-only usages, see above.

Note

Excluders are called **after** providers.

Dead cycles &amp; transitively dead methods
-------------------------------------------

[](#dead-cycles--transitively-dead-methods)

- This library automatically detects dead cycles and transitively dead methods (methods that are only called from dead methods)
- By default, it reports only the first dead method in the subtree and the rest as a tip:

```
 ------ ------------------------------------------------------------------------
  Line   src/App/Facade/UserFacade.php
 ------ ------------------------------------------------------------------------
  26     Unused App\Facade\UserFacade::updateUserAddress
         🪪  shipmonk.deadMethod
         💡 Thus App\Entity\User::updateAddress is transitively also unused
         💡 Thus App\Entity\Address::setPostalCode is transitively also unused
         💡 Thus App\Entity\Address::setCountry is transitively also unused
         💡 Thus App\Entity\Address::setStreet is transitively also unused
         💡 Thus App\Entity\Address::MAX_STREET_CHARS is transitively also unused
 ------ ------------------------------------------------------------------------

```

- If you want to report all dead methods individually, you can enable it in your `phpstan.neon.dist`:

```
parameters:
    shipmonkDeadCode:
        reportTransitivelyDeadMethodAsSeparateError: true
```

Automatic removal of dead code
------------------------------

[](#automatic-removal-of-dead-code)

- If you are sure that the reported methods are dead, you can automatically remove them by running PHPStan with `removeDeadCode` error format:

```
vendor/bin/phpstan analyse --error-format removeDeadCode
```

```
class UserFacade
{
-    public const TRANSITIVELY_DEAD = 1;
-
-    public function deadMethod(): void
-    {
-        echo self::TRANSITIVELY_DEAD;
-    }
}
```

- If you are excluding tests usages (see above), this will not cause the related tests to be removed alongside.
    - But you will see all those kept usages in output (with links to your IDE if you set up `editorUrl` [parameter](https://phpstan.org/user-guide/output-format#opening-file-in-an-editor))

```
 • Removed method UserFacade::deadMethod
   ! Excluded usage at tests/User/UserFacadeTest.php:241 left intact
```

- Also, removing dead properties currently only removes its definition (leaving all write usages as is).

Calls over unknown types
------------------------

[](#calls-over-unknown-types)

- In order to prevent false positives, we support even calls over unknown types (e.g. `$unknown->method()`) by marking all methods named `method` as used
    - Such behaviour might not be desired for strictly typed codebases, because e.g. single `new $unknown()` will mark all constructors as used
    - The same applies to constant fetches over unknown types (e.g. `$unknown::CONSTANT`)
    - Thus, you can disable this feature in your `phpstan.neon.dist` by excluding such usages:

```
parameters:
    shipmonkDeadCode:
        usageExcluders:
            usageOverMixed:
                enabled: true
```

- If you want to check how many of those cases are present in your codebase, you can run PHPStan analysis with `-vvv` and you will see some diagnostics:

```
Found 2 usages over unknown type:
 • setCountry method, for example in App\Entity\User::updateAddress
 • setStreet method, for example in App\Entity\User::updateAddress

```

Access of unknown member
------------------------

[](#access-of-unknown-member)

- In order to prevent false positives, we support even calls of unknown methods (e.g. `$class->$unknown()`) by marking all possible methods as used
- If we find unknown call over unknown type (e.g. `$unknownClass->$unknownMethod()`), we ignore such usage (as it would mark all methods in codebase as used) and show warning in debug verbosity (`-vvv`)
- Note that some calls over `ReflectionClass` also emit unknown method calls:

```
/** @var ReflectionClass $reflection */
$methods = $reflection->getMethods(); // all Foo methods are used here
```

- All that applies even to constants and properties.

Limitations:
------------

[](#limitations)

- Methods of anonymous classes are never reported as dead ([PHPStan limitation](https://github.com/phpstan/phpstan/issues/8410))
- Abstract trait methods are never reported as dead
- Most magic methods (e.g. `__get`, `__set` etc) are never reported as dead
    - Only supported are: `__construct`, `__clone`

### Other problematic cases:

[](#other-problematic-cases)

#### Constructors:

[](#constructors)

- For symfony apps &amp; PHPStan extensions, we simplify the detection by assuming all DIC classes have used constructor.
- For other apps, you may get false-positives if services are created magically.
    - To avoid those, you can easily disable constructor analysis with single ignore:

```
parameters:
    ignoreErrors:
        - '#^Unused .*?::__construct$#'
```

#### Private constructors:

[](#private-constructors)

- Those are never reported as dead as those are often used to deny class instantiation
    - This applies only to constructors without any parameters

#### Serialization:

[](#serialization)

- Properties/enum cases read only through serialization (e.g. public properties serialized to JSON response) may be reported as `neverRead`
- Custom serialization/deserialization logic needs to be handled via [custom usage providers](#customization)
    - For example, if your API output objects implement a common interface, use simple:

```
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;
    }
}
```

- If you can detect such usages only by e.g. `Controller` return value, use AST-based provider
    - You can inspire by [Twig Provider](src/Provider/TwigUsageProvider.php#L283) which does a very similar thing

#### Interface methods:

[](#interface-methods)

- If you never call interface method over the interface, but only over its implementors, it gets reported as dead
- But you may want to keep the interface method to force some unification across implementors
    - The easiest way to ignore it is via custom `MemberUsageProvider`:

```
use ShipMonk\PHPStan\DeadCode\Provider\VirtualUsageData;
use ShipMonk\PHPStan\DeadCode\Provider\ReflectionBasedMemberUsageProvider;

class IgnoreDeadInterfaceUsageProvider extends ReflectionBasedMemberUsageProvider
{
    public function shouldMarkMethodAsUsed(ReflectionMethod $method): ?VirtualUsageData
    {
        if ($method->getDeclaringClass()->isInterface()) {
            return VirtualUsageData::withNote('Interface method, kept for unification even when possibly unused');
        }

        return null;
    }
}
```

Debugging:
----------

[](#debugging)

- If you want to see how dead code detector evaluated usages of certain member, you do the following:

```
parameters:
    shipmonkDeadCode:
        debug:
            usagesOf:
                - App\User\Entity\Address::__construct
```

Then, run PHPStan with `-vvv` CLI option and you will see the output like this:

```
App\User\Entity\Address::__construct
|
| Marked as alive by:
| entry virtual usage from ShipMonk\PHPStan\DeadCode\Provider\SymfonyUsageProvider
|   calls App\User\RegisterUserController::__invoke:36
|     calls App\User\UserFacade::registerUser:142
|       calls App\User\Entity\Address::__construct
|
| Found 2 usages:
|  • src/User/UserFacade.php:142
|  • tests/User/Entity/AddressTest.php:64 - excluded by tests excluder
```

If you set up `editorUrl` [parameter](https://phpstan.org/user-guide/output-format#opening-file-in-an-editor), you can click on the usages to open it in your IDE.

Tip

You can change the list of debug references without affecting result cache, so rerun is instant!

Usage in libraries:
-------------------

[](#usage-in-libraries)

- Libraries typically contain public api, that is unused
    - If you mark such methods with `@api` phpdoc, those will be considered entrypoints
    - You can also mark whole class or interface with `@api` to mark all its methods as entrypoints

Running PHPStan partial analysis:
---------------------------------

[](#running-phpstan-partial-analysis)

- Dead code detection can be reliable executed only on full codebase, thus it gets autodisabled during partial analysis (when only files are passed to PHPStan analysis)
    - In such cases, PHPStan will report `No error with identifier shipmonk.deadMethod is reported on line X` [false positives](https://github.com/phpstan/phpstan/issues/12328) for every inline ignore (e.g. `// @phpstan-ignore shipmonk.deadMethod`) as those errors are no longer emitted
    - To eliminate those false positives, use built-in formatter that filters out those errors:

```
parameters:
    errorFormat: filterOutUnmatchedInlineIgnoresDuringPartialAnalysis

    # optionally:
    shipmonkDeadCode:
        filterOutUnmatchedInlineIgnoresDuringPartialAnalysis:
            wrappedErrorFormatter: table
```

Future scope:
-------------

[](#future-scope)

- Dead class detection
- Dead parameters detection
- Useless public/protected visibility

Contributing
------------

[](#contributing)

- Check your code by `composer check`
- Autofix coding-style by `composer fix:cs`
- All functionality must be tested

Supported PHP versions
----------------------

[](#supported-php-versions)

- `0.x` — PHP 7.4 - 8.5
- `1.x` — PHP 8.1+

###  Health Score

69

—

FairBetter than 100% of packages

Maintenance95

Actively maintained with recent releases

Popularity64

Solid adoption and visibility

Community41

Growing community involvement

Maturity65

Established project with proven stability

 Bus Factor1

Top contributor holds 84.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~21 days

Recently: every ~9 days

Total

41

Last Release

22d ago

Major Versions

0.15.1 → 1.0.02026-04-08

PHP version history (2 changes)0.1.0PHP ^7.4 || ^8.0

1.0.0PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/5b545e3f9d982d538f11bc42b3dc2d186f706cef92c8bc8bc8f8788b08186ea5?d=identicon)[janedbal](/maintainers/janedbal)

![](https://www.gravatar.com/avatar/7a4170ebe9281cb76be91fe00f8eee307beb3e0744dfd40ba89d9c856372c7eb?d=identicon)[shipmonk](/maintainers/shipmonk)

---

Top Contributors

[![janedbal](https://avatars.githubusercontent.com/u/1993453?v=4)](https://github.com/janedbal "janedbal (231 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (17 commits)")[![JanTvrdik](https://avatars.githubusercontent.com/u/175109?v=4)](https://github.com/JanTvrdik "JanTvrdik (5 commits)")[![ruudk](https://avatars.githubusercontent.com/u/104180?v=4)](https://github.com/ruudk "ruudk (4 commits)")[![simPod](https://avatars.githubusercontent.com/u/327717?v=4)](https://github.com/simPod "simPod (3 commits)")[![staabm](https://avatars.githubusercontent.com/u/120441?v=4)](https://github.com/staabm "staabm (2 commits)")[![S1ructure](https://avatars.githubusercontent.com/u/18121250?v=4)](https://github.com/S1ructure "S1ructure (2 commits)")[![zacharylund](https://avatars.githubusercontent.com/u/4418431?v=4)](https://github.com/zacharylund "zacharylund (2 commits)")[![Orest-Divintari](https://avatars.githubusercontent.com/u/9808549?v=4)](https://github.com/Orest-Divintari "Orest-Divintari (1 commits)")[![canvural](https://avatars.githubusercontent.com/u/1574232?v=4)](https://github.com/canvural "canvural (1 commits)")[![devbanana](https://avatars.githubusercontent.com/u/1540834?v=4)](https://github.com/devbanana "devbanana (1 commits)")[![mabar](https://avatars.githubusercontent.com/u/20974277?v=4)](https://github.com/mabar "mabar (1 commits)")[![mjanser](https://avatars.githubusercontent.com/u/1826722?v=4)](https://github.com/mjanser "mjanser (1 commits)")[![alcohol](https://avatars.githubusercontent.com/u/21414?v=4)](https://github.com/alcohol "alcohol (1 commits)")[![sasezaki](https://avatars.githubusercontent.com/u/42755?v=4)](https://github.com/sasezaki "sasezaki (1 commits)")[![szepeviktor](https://avatars.githubusercontent.com/u/952007?v=4)](https://github.com/szepeviktor "szepeviktor (1 commits)")

---

Tags

deaddead-code-removalphpphpstanphpstan-extensionphpstan-rulesstatic-analysisunused-codePHPStanstatic analysisdead codeunused code

###  Code Quality

TestsPHPUnit

Type Coverage Yes

### Embed Badge

![Health badge](/badges/shipmonk-dead-code-detector/health.svg)

```
[![Health](https://phpackages.com/badges/shipmonk-dead-code-detector/health.svg)](https://phpackages.com/packages/shipmonk-dead-code-detector)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[phpstan/phpstan-symfony

Symfony Framework extensions and rules for PHPStan

78973.3M2.0k](/packages/phpstan-phpstan-symfony)[phpstan/phpstan-doctrine

Doctrine extensions for PHPStan

66970.7M1.3k](/packages/phpstan-phpstan-doctrine)[ekino/phpstan-banned-code

Detected banned code using PHPStan

2986.0M111](/packages/ekino-phpstan-banned-code)[spaze/phpstan-disallowed-calls

PHPStan rules to detect disallowed method &amp; function calls, constant, namespace, attribute, property &amp; superglobal usages, with powerful rules to re-allow a call or a usage in places where it should be allowed.

33321.8M504](/packages/spaze-phpstan-disallowed-calls)[szepeviktor/phpstan-wordpress

WordPress extensions for PHPStan

3309.4M1.2k](/packages/szepeviktor-phpstan-wordpress)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
