Giter VIP home page Giter VIP logo

class-leak's Introduction

Class Leak

Downloads total

Find leaking classes that you never use... and get rid of them.

Install

composer require tomasvotruba/class-leak --dev

Usage

Pass directories you want to check:

vendor/bin/class-leak check bin src

Make sure to exclude /tests directories, to keep reporting classes that are used in tests, but never used in the code-base.


Many types are excluded by default, as they're collected by framework magic, e.g. console command classes. To exclude another class, e.g. your interface collector, use --skip-type:

vendor/bin/class-leak check bin src --skip-type="App\\Contract\\SomeInterface"

What if your classes do no implement any type? Use --skip-suffix instead:

vendor/bin/class-leak check bin src --skip-suffix "Controller"

If you want to skip classes that use a specific attribute or have methods that use a specific attribute, use --skip-attribute:

vendor/bin/class-leak check bin src --skip-attribute "Symfony\\Component\\HttpKernel\\Attribute\\AsController"

Happy coding!

class-leak's People

Contributors

erjanmx avatar qroques avatar ruudk avatar staabm avatar tomasvotruba avatar zairigimad avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

class-leak's Issues

Allow to specify directory/namespace

It could be helpful to not just skip a single type, but a whole directory or namespace.

In my case I have a folder with generated classes for fields based on some CSV input. Those field classes can then be used by a developer in the future, but are not all used yet.

false positivs in Symfony projects

Hi Tomas,

thanks for this Tool! Maybe i am doing something wrong, but right now i am getting a lot of false positivs in a Symfony project.

For example:

  1. Constraints/Validators
    Only the Contraint is used (e.q. in an Entity) and not the Validator itself.

From the docs:

you create a custom Constraint (e.g. MyConstraint), Symfony will automatically look for another class, MyConstraintValidator when actually performing the validation.

So no direct use of the Validator

  1. Tagged Iterator

I am using the tagged Iterator (https://symfony.com/doc/current/service_container/tags.html#reference-tagged-services).
I tag all classes implementing some Interface and inject them into a Service. So there is no direct use of the class implementing the Interface

  1. Mutations/Resolver in GraphQL

This is maybe not directly related to Symfony, but maybe you have some thoughts on how to handle this.
im use the overblog/GraphQLBundle to handle GQL Request. If you are not familier with GQL i'll try to give you some quick introduction. You have an Query (or Mutation), which is defined in an yaml (see Example below). In this example: "adresseUsage". You can think of it as an "action" in a Symfony Controller. Every GQL Request send to a single REST Endpoint and internally handled by the configured Resolver ("AdresseResolver")

AdresseQuery:
    type: object
    config:
        fields:
            adresseUsage:
                type: AdresseUsage
                resolve: '@=resolver("App\\Resolver\\AdresseResolver", [info, value, args])'
                args:
                    adressId:
                        type: ID

So, again no direct use of the Resolver itself ๐Ÿ˜ž

  1. Messenges/MessageHandler
    Very similar to Constraints/Validator. You have same Messages dispatched through an MessageBus and the handled by a MessageHandler. The MessageHandlers are configured by an Attribute and Symfony will take care of everything.

Any toughts on how to solve these issues?

best regards!

ignore paths

in our project we want to analyze certain paths, but not some of the subpaths included in them

e.g. analyze library/ and all its subfolders but not library/FolderToIgnore/

Latest class-leak version 0.2.12 - 0.2.13 cause error in rector-src

see https://github.com/rectorphp/rector-src/actions/runs/8854029950/job/24316165596?pr=5845#step:5:1

Before => 0.2.11

โžœ  rector-src git:(cache-classname) vendor/bin/class-leak check bin config src rules \
                                --skip-type="Rector\\NodeTypeResolver\\PHPStan\\Scope\\Contract\\NodeVisitor\\ScopeResolverNodeVisitorInterface" \
                                --skip-type="Rector\\BetterPhpDocParser\\Contract\\BasePhpDocNodeVisitorInterface" \
                                --skip-type="Rector\\BetterPhpDocParser\\Contract\\PhpDocParser\\PhpDocNodeDecoratorInterface" \
                                --skip-type="Rector\\BetterPhpDocParser\\ValueObject\\Type\\FullyQualifiedIdentifierTypeNode"
    0/1142 [โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘]   0%
 1142/1142 [โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“] 100%

                                                                                                                        
 [OK] All the 508 services are used. Great job!                                                                         
                                                                                                                        

After => 0.2.13

rector-src git:(cache-classname) vendor/bin/class-leak check bin config src rules \
                                --skip-type="Rector\\NodeTypeResolver\\PHPStan\\Scope\\Contract\\NodeVisitor\\ScopeResolverNodeVisitorInterface" \
                                --skip-type="Rector\\BetterPhpDocParser\\Contract\\BasePhpDocNodeVisitorInterface" \
                                --skip-type="Rector\\BetterPhpDocParser\\Contract\\PhpDocParser\\PhpDocNodeDecoratorInterface" \
                                --skip-type="Rector\\BetterPhpDocParser\\ValueObject\\Type\\FullyQualifiedIdentifierTypeNode"
    0/1142 [โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘]   0%
 1142/1142 [โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“] 100%

Classes with a parent/interface - possibly used by type
=======================================================

 * Rector\Arguments\Rector\MethodCall\RemoveMethodCallParamRector
rules/Arguments/Rector/MethodCall/RemoveMethodCallParamRector.php

 * Rector\CodeQuality\Rector\FuncCall\BoolvalToTypeCastRector
rules/CodeQuality/Rector/FuncCall/BoolvalToTypeCastRector.php

 * Rector\CodeQuality\Rector\FuncCall\FloatvalToTypeCastRector
rules/CodeQuality/Rector/FuncCall/FloatvalToTypeCastRector.php

 * Rector\CodeQuality\Rector\FuncCall\IntvalToTypeCastRector
rules/CodeQuality/Rector/FuncCall/IntvalToTypeCastRector.php

 * Rector\CodeQuality\Rector\FuncCall\StrvalToTypeCastRector
rules/CodeQuality/Rector/FuncCall/StrvalToTypeCastRector.php

 * Rector\CodingStyle\Rector\FuncCall\ArraySpreadInsteadOfArrayMergeRector
rules/CodingStyle/Rector/FuncCall/ArraySpreadInsteadOfArrayMergeRector.php

 * Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector
rules/DeadCode/Rector/ClassLike/RemoveAnnotationRector.php

 * Rector\DeadCode\Rector\ClassMethod\RemoveNullTagValueNodeRector
rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php

 * Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector
rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php

 * Rector\Php80\Rector\Class_\AnnotationToAttributeRector
rules/Php80/Rector/Class_/AnnotationToAttributeRector.php

 * Rector\Php80\Rector\Property\NestedAnnotationToAttributeRector
rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php

 * Rector\Php81\Rector\ClassConst\FinalizePublicClassConstantRector
rules/Php81/Rector/ClassConst/FinalizePublicClassConstantRector.php

 * Rector\Privatization\Rector\Class_\FinalizeClassesWithoutChildrenRector
rules/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenRector.php

 * Rector\Privatization\Rector\Class_\FinalizeTestCaseClassRector
rules/Privatization/Rector/Class_/FinalizeTestCaseClassRector.php

 * Rector\Removing\Rector\ClassMethod\ArgumentRemoverRector
rules/Removing/Rector/ClassMethod/ArgumentRemoverRector.php

 * Rector\Removing\Rector\Class_\RemoveInterfacesRector
rules/Removing/Rector/Class_/RemoveInterfacesRector.php

 * Rector\Removing\Rector\Class_\RemoveTraitUseRector
rules/Removing/Rector/Class_/RemoveTraitUseRector.php

 * Rector\Removing\Rector\FuncCall\RemoveFuncCallRector
rules/Removing/Rector/FuncCall/RemoveFuncCallRector.php

 * Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector
rules/Renaming/Rector/ClassConstFetch/RenameClassConstFetchRector.php

 * Rector\Renaming\Rector\ClassMethod\RenameAnnotationRector
rules/Renaming/Rector/ClassMethod/RenameAnnotationRector.php

 * Rector\Renaming\Rector\ConstFetch\RenameConstantRector
rules/Renaming/Rector/ConstFetch/RenameConstantRector.php

 * Rector\Renaming\Rector\FunctionLike\RenameFunctionLikeParamWithinCallLikeArgRector
rules/Renaming/Rector/FunctionLike/RenameFunctionLikeParamWithinCallLikeArgRector.php

 * Rector\Renaming\Rector\PropertyFetch\RenamePropertyRector
rules/Renaming/Rector/PropertyFetch/RenamePropertyRector.php

 * Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector
rules/Renaming/Rector/StaticCall/RenameStaticMethodRector.php

 * Rector\Renaming\Rector\String_\RenameStringRector
rules/Renaming/Rector/String_/RenameStringRector.php

 * Rector\Transform\Rector\ArrayDimFetch\ArrayDimFetchToMethodCallRector
rules/Transform/Rector/ArrayDimFetch/ArrayDimFetchToMethodCallRector.php

 * Rector\Transform\Rector\Assign\PropertyAssignToMethodCallRector
rules/Transform/Rector/Assign/PropertyAssignToMethodCallRector.php

 * Rector\Transform\Rector\Assign\PropertyFetchToMethodCallRector
rules/Transform/Rector/Assign/PropertyFetchToMethodCallRector.php

 * Rector\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector
rules/Transform/Rector/ClassMethod/ReturnTypeWillChangeRector.php

 * Rector\Transform\Rector\ClassMethod\WrapReturnRector
rules/Transform/Rector/ClassMethod/WrapReturnRector.php

 * Rector\Transform\Rector\Class_\AddAllowDynamicPropertiesAttributeRector
rules/Transform/Rector/Class_/AddAllowDynamicPropertiesAttributeRector.php

 * Rector\Transform\Rector\Class_\MergeInterfacesRector
rules/Transform/Rector/Class_/MergeInterfacesRector.php

 * Rector\Transform\Rector\Class_\ParentClassToTraitsRector
rules/Transform/Rector/Class_/ParentClassToTraitsRector.php

 * Rector\Transform\Rector\ConstFetch\ConstFetchToClassConstFetchRector
rules/Transform/Rector/ConstFetch/ConstFetchToClassConstFetchRector.php

 * Rector\Transform\Rector\FileWithoutNamespace\RectorConfigBuilderRector
rules/Transform/Rector/FileWithoutNamespace/RectorConfigBuilderRector.php

 * Rector\Transform\Rector\FuncCall\FuncCallToMethodCallRector
rules/Transform/Rector/FuncCall/FuncCallToMethodCallRector.php

 * Rector\Transform\Rector\FuncCall\FuncCallToNewRector
rules/Transform/Rector/FuncCall/FuncCallToNewRector.php

 * Rector\Transform\Rector\FuncCall\FuncCallToStaticCallRector
rules/Transform/Rector/FuncCall/FuncCallToStaticCallRector.php

 * Rector\Transform\Rector\MethodCall\MethodCallToFuncCallRector
rules/Transform/Rector/MethodCall/MethodCallToFuncCallRector.php

 * Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector
rules/Transform/Rector/MethodCall/MethodCallToPropertyFetchRector.php

 * Rector\Transform\Rector\MethodCall\MethodCallToStaticCallRector
rules/Transform/Rector/MethodCall/MethodCallToStaticCallRector.php

 * Rector\Transform\Rector\MethodCall\ReplaceParentCallByPropertyCallRector
rules/Transform/Rector/MethodCall/ReplaceParentCallByPropertyCallRector.php

 * Rector\Transform\Rector\New_\NewToStaticCallRector
rules/Transform/Rector/New_/NewToStaticCallRector.php

 * Rector\Transform\Rector\StaticCall\StaticCallToMethodCallRector
rules/Transform/Rector/StaticCall/StaticCallToMethodCallRector.php

 * Rector\Transform\Rector\StaticCall\StaticCallToNewRector
rules/Transform/Rector/StaticCall/StaticCallToNewRector.php

 * Rector\Transform\Rector\String_\StringToClassConstantRector
rules/Transform/Rector/String_/StringToClassConstantRector.php

 * Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector
rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php

 * Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector
rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php

 * Rector\TypeDeclaration\Rector\FunctionLike\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector
rules/TypeDeclaration/Rector/FunctionLike/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector.php

 * Rector\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector
rules/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector.php

 * Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector
rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php

 * Rector\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector
rules/Visibility/Rector/ClassConst/ChangeConstantVisibilityRector.php

 * Rector\Visibility\Rector\ClassMethod\ChangeMethodVisibilityRector
rules/Visibility/Rector/ClassMethod/ChangeMethodVisibilityRector.php


Classes without any parent/interface - easier to remove
=======================================================

 * Rector\PhpDocParser\PhpParser\SmartPhpParserFactory
src/PhpDocParser/PhpParser/SmartPhpParserFactory.php


                                                                                                                        
 [ERROR] Found 54 unused classes. Check and remove them or skip them using "--skip-type" option                         
                                                                                                                        

Specify skipped classes via a config file

Specifying the skipped class list via a flag is not ideal when the list has tens or more entries. Is it planned to add something like a config file where we could comfortably maintain this list ?

Warn on skipped but non-leaked classes

Let's say I skip a class that is referenced in the code.
vendor/bin/class-leak check src/ --skip-type 'A\Referenced\Class'
It would be nice to get a warning about it.

Class not found errors

the tools analyzes source code by calling is_a on found class names.

since is_a is a runtime-concept it kicks in autoloading and currently there is no way to define a autoloader.

this means when code is found which declares classes which cannot be found the analysis results in something like

$ vendor/bin/class-leak check application/ library/
    0/2296 [>---------------------------]   0%
 2296/2296 [============================] 100%PHP Fatal error:  Uncaught Error: Class "ApplicationController" not found in C:\dvl\Workspace\daiber\library\clxProductNet\application\Rocket\Controllers\AccountController.php:11
Stack trace:
#0 C:\dvl\Workspace\daiber\vendor\composer\ClassLoader.php(576): include()
Stack trace:
#0 C:\dvl\Workspace\daiber\vendor\composer\ClassLoader.php(576): include()
#1 C:\dvl\Workspace\daiber\vendor\composer\ClassLoader.php(427): Composer\Autoload\{closure}()
#2 [internal function]: Composer\Autoload\ClassLoader->loadClass()
#3 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\app\Filtering\PossiblyUnusedClassesFilter.php(71): is_a()
#4 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\app\Filtering\PossiblyUnusedClassesFilter.php(60): TomasVotruba\ClassLeak\Filtering\PossiblyUnusedClassesFilter->isClassSkipped()
#5 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\app\Console\Commands\CheckCommand.php(80): TomasVotruba\ClassLeak\Filtering\PossiblyUnusedClassesFilter->filter()
#6 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\vendor\symfony\console\Command\Command.php(325): TomasVotruba\ClassLeak\Console\Commands\CheckCommand->execute()
#7 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\vendor\symfony\console\Application.php(944): ClassLeak202307\Symfony\Component\Console\Command\Command->run()
#8 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\vendor\symfony\console\Application.php(326): ClassLeak202307\Symfony\Component\Console\Application->doRunCommand()
#9 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\vendor\symfony\console\Application.php(212): ClassLeak202307\Symfony\Component\Console\Application->doRun()
#10 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\bin\class-leak.php(25): ClassLeak202307\Symfony\Component\Console\Application->run()
#11 C:\dvl\Workspace\daiber\vendor\tomasvotruba\class-leak\bin\class-leak(5): require('...')
#12 C:\dvl\Workspace\daiber\vendor\bin\class-leak(119): include('...')
#13 {main}
  thrown in C:\dvl\Workspace\daiber\library\clxProductNet\application\Rocket\Controllers\AccountController.php on line 11

global namespaced classes

it seems classes which are defined in the global namespace are not found, as the source of truth are the "use" statements, which global classes don't necessarily have

use via magic property

classes used via magic properties are not detected

<?php

declare(strict_types=1);

namespace TomasVotruba\ClassLeak\Tests\UseImportsResolver\Fixture;

/**
 * @property \TomasVotruba\ClassLeak\Tests\UseImportsResolver\Source\FirstUsedClass $firstUsedClass
 * @property-read  \TomasVotruba\ClassLeak\Tests\UseImportsResolver\Source\SecondUsedClass $secondUsedClass
 */
final class FileUsingMagicProperties
{
}

Skip classes by Attribute

The symfony/messenger uses an attribute #[AsMessageHandler]. If this attribute is used and/or MessageHandlerInterface is implemented, the class should not be marked as leaked

What about having specific rule sets for frameworks like Symfony and Laravel?

wrongly prefixed classes in PossiblyUnusedClassesFilter

after installing the tool with composer require tomasvotruba/class-leak --dev open the file

vendor/tomasvotruba/class-leak/src/Filtering/PossiblyUnusedClassesFilter.php and find wrongly-prefixed code in the static DEFAULT_TYPES_TO_SKIP-array

grafik

which effectively makes the default-types-to-skip useless

Class used in the same namespace but marked as unused

Hi, @TomasVotruba thanks for creating this tool. We started using it in our company, and it found many classes that can be removed! We love it!

To the point, we found this tool has problems when a class is used in the same namespace. It marks it as unused, but in fact, a class is used.

Example:

<?php

namespace App;

class A {
    public function main()
    {
        return B::someMethod();
    }
}
<?php

namespace App;

class B {
    public static function someMethod()
    {
        return 1;
    }
}

In this example, class B is marked as unused. To fix it, I have to add use, but in this case it is redundant. Rule no_unused_imports in phpcs remove it.

<?php

namespace App;

+use App/B;

class A {
    public function main()
    {
        return B::someMethod();
    }
}

and then class-leak tool doesn't mark class B as unused.

I haven't taken a look in codebase deeply, but I would love to introduce a fix for that, but I want to hear that it is possible/doable without refactoring a whole tool :D

Classes referenced in PHP doc are reported unused

Given you have the following code:

final readonly class MyValue
{
    public function __construct(
        public string $value,
    ) {}
}

final readonly class MyResponse
{
    /**
     * @param list<MyValue> $values
     */
    public function __construct(
        public array $values,
    ) {}
}

It results in MyValue being reported as unused.

This is because class-leak does not check for PHPDocs.

I would like to enhance class-leak so that it is able to do this. But manually parsing the PHPDocs feels a bit contra productive to me.

@TomasVotruba Given your experience with this problem in Rector, you probably have an idea how to achieve it. Could you point me in the right direction so that I can give it a go?

".72" releases are preferred over normal ones

.72 releases include "php": ">=7.2"
Users on PHP 8+ will always install this because its semver is higher than normal releases.
It should include "php": ">=7.2 <8.1" to prevent that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.