Giter VIP home page Giter VIP logo

unused-public's Introduction

Find Unused Public Elements in Your Code



It's easy to find unused private class elements, because they're not used in the class itself. But what about public methods/properties/constants?

 final class Book
 {
     public function getTitle(): string
     {
         // ...
     }

-    public function getSubtitle(): string
-    {
-        // ...
-    }
}

How can we detect unused public element?

  • find a public method
  • find all public method calls in code and templates
  • if the public method is not found, it probably unused

That's exactly what this package does.


This technique is very useful for private projects and to detect accidentally used public modifier that should be changed to private as called locally only.


Install

composer require tomasvotruba/unused-public --dev

The package is available for PHP 7.2+ version.


Usage

With PHPStan extension installer, everything is ready to run.

Enable each item on their own with simple configuration:

# phpstan.neon
parameters:
    unused_public:
        methods: true
        properties: true
        constants: true

Do you have hundreds of reported public method? You don't have time to check them all, but want to handle them gradually?

Set maximum allowed % configuration instead:

# phpstan.neon
parameters:
    unused_public:
        methods: 2.5

This means maximum 2.5 % of all public methods is allowed as unused:

  • If it's 5 %, you'll be alerted.
  • If it's 1 %, it will be skipped as tolerated.

Do you want to check local-only method calls that should not be removed, but be turned into private/protected instead?

# phpstan.neon
parameters:
    unused_public:
        local_methods: true

Exclude methods called in templates

Some methods are used only in TWIG or Blade templates, and could be reported false positively as unused.

{{ book.getTitle() }}

How can we exclude them? Add your TWIG or Blade template directories in config to exclude methods names:

# phpstan.neon
parameters:
    unused_public:
        template_paths:
            - templates

Known Limitations

In some cases, the rules report false positives:

  • when used only in templates, apart Twig paths, it's not possible to detect them

Skip Public-Only Methods

Open-source vendors design public API to be used by projects. Is element reported as unused, but it's actually designed to be used public?

Mark the class or element with @api annotation to skip it:

final class Book
{
    /**
     * @api
     */
    public function getName()
    {
        return $this->name;
    }
}

unused-public's People

Contributors

leovie avatar ngmy avatar staabm avatar tomasvotruba avatar transistive 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  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  avatar

unused-public's Issues

tests folder is missing = does not work without phpstan extensions installer

I have issue with extension installer not working correctly on my project, therefore I include your extension directly using

includes:
	- phpstan-rules.neon
	- data/vendor/tomasvotruba/unused-public/phpstan.neon

but then I get error


Note: Using configuration file dev\phpstan.neon.
Path dev\vendor\tomasvotruba\unused-public\tests does not exist

Creating the tests folder fixes the issue.

Can you create empty folder? So I wont have to fork it :)

relax symfony constraint

I wanted to run this extension on the phpstan-src codebase.

it is not installable there because of the symfony/finder 6.1 constraint.
could the dependency be relaxed to support ^5.4|^6?

$ composer up -W
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires tomasvotruba/unused-public 0.0.28 -> satisfiable by tomasvotruba/unused-public[0.0.28].
    - tomasvotruba/unused-public 0.0.28 requires symfony/finder ^6.2 -> found symfony/finder[v6.2.0-BETA1, ..., 6.3.x-dev] but it conflicts with your root composer.json require (^5.4.3).

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.

Class 'Nette\Utils\Arrays' not found in UnusedPublicClassMethodRule

Im getting this. What am I doing wrong?

Exception: Class 'Nette\Utils\Arrays' not found in [/development/vendor/tomasvotruba/unused-public/src/Rules/UnusedPublicClassMethodRule.php, line 86]
2024-04-27 14:05:53 Error: [Error] Class 'Nette\Utils\Arrays' not found in /development/vendor/tomasvotruba/unused-public/src/Rules/UnusedPublicClassMethodRule.php on line 86
Stack Trace:
#0 phar:///development/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseApplication.php(172): TomasVotruba\UnusedPublic\Rules\UnusedPublicClassMethodRule->processNode()
#1 phar:///development/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseApplication.php(136): PHPStan\Command\AnalyseApplication->getCollectedDataErrors()
#2 phar:///development/vendor/phpstan/phpstan/phpstan.phar/src/Command/AnalyseCommand.php(202): PHPStan\Command\AnalyseApplication->analyse()
#3 phar:///development/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Command/Command.php(259): PHPStan\Command\AnalyseCommand->execute()
#4 phar:///development/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(870): _PHPStan_7961f7ae1\Symfony\Component\Console\Command\Command->run()
#5 phar:///development/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(261): _PHPStan_7961f7ae1\Symfony\Component\Console\Application->doRunCommand()
#6 phar:///development/vendor/phpstan/phpstan/phpstan.phar/vendor/symfony/console/Application.php(157): _PHPStan_7961f7ae1\Symfony\Component\Console\Application->doRun()
#7 phar:///development/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(124): _PHPStan_7961f7ae1\Symfony\Component\Console\Application->run()
#8 phar:///development/vendor/phpstan/phpstan/phpstan.phar/bin/phpstan(125): _PHPStan_7961f7ae1\{closure}()

Detect usages from PhpUnit TestCases

If I understand correctly, phpunit testcases are ignored.

However, method calls of classes that are placed in my tests dir from within phpunit testcases seems to be ignored as well. Therefore those classes' methods are reported as unused.

Is there a way to work around it?

E.g.

namespace Foo\Bar\Tests;

class X {
    public static function foo() {
    }
}
namespace Foo\Bar\Tests;

class MyTest {
    public function testSth() {
        X::foo(); 
    }
}

X::foo is reported as unused.

false positve on interface calls

when a class implements a interface, calls to the interface type should prevent a "is never used" error on concrete implementations

interface CmsUrlProvider
{
    /**
     * Build the public url for the given physical filepath.
     *
     * @param string $path
     *
     * @return string the public url
     */
    public function publicUrl($path);
}

class UrlProvider implements CmsUrlProvider
{
    public function publicFormatUrl($path, $type): string
    {
        return '';
    }
}

class FooContext {
    /**
     * @var CmsUrlProvider|null
     */
    public $urlProvider;
}

function doFoo(FooContext $context) {
  if ($context === null) return '';

  return $context->publicFormatUrl('lala', 'lulu');
}

false positve on interface calls

when a class implements a interface, a method from said interface should not be suggested to be reduced in visibility.
the code needs to implement the interface contract which dictates the visibility

interface CmsUrlProvider
{
    /**
     * @param string $path
     * @return string
     */
    public function publicUrl($path);
}

class UrlProvider implements CmsUrlProvider
{
    public function publicFormatUrl($path, $type): string
    {
        return '';
    }

    public function publicUrl($path): string
    {
        return $this->publicFormatUrl($path, 'lulu');
    }
}

false positive

Public method "publicFormatUrl()" is used only locally and should turned protected/private

detect unused parameters

I think this extension has all the information available to decide that optional method parameters are never used from a caller point of view and therefore mark them as obsolete.

e.g.

Class X {
  public function doFoo($a, $b, $c = null, $d = 123) {
  }
}

$x = new X();
$x->doFoo(1, 2);
$x->doFoo(3, 4);

-> since no calls exist which provide a value for $c and/or $d, both parameters are "unused" and could be turned into local variables or even removed altogether.

is this something you would accept as a PR?

crash

running unused-public on

https://github.com/rectorphp/rector-src/blob/7b7b4fb61bc2a7088823e7cfabf8a29b4fec516c/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/skip_string_variable.php.inc

leads to a crash

Internal error: Internal error: Internal error. in file                                                                                                                        
     /Users/staabm/workspace/rector-src/rules-tests/CodingStyle/Rector/FuncCall/CallUserFuncToMethodCallRector/Fixture/skip_string_variable.php.inc                                 
                                                                                                                                                                                    
     Post the following stack trace to https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml:                                                                      
     #0 /Users/staabm/workspace/rector-src/vendor/tomasvotruba/unused-public/src/Collectors/Callable_/CallUserFuncCollector.php(88):                                                
     PHPStan\Type\Constant\ConstantArrayTypeAndMethod->getType()                                                                                                                    
     #1 phar:///Users/staabm/workspace/rector-src/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/FileAnalyser.php(128):                                                           
     TomasVotruba\UnusedPublic\Collectors\Callable_\CallUserFuncCollector->processNode(Object(PhpParser\Node\Expr\FuncCall), Object(PHPStan\Analyser\MutatingScope))                
     #2 phar:///Users/staabm/workspace/rector-src/vendor/phpstan/phpstan/phpstan.phar/src/Node/ClassStatementsGatherer.php(108):                                                    
     PHPStan\Analyser\FileAnalyser->PHPStan\Analyser\{closure}(Object(PhpParser\Node\Expr\FuncCall), Object(PHPStan\Analyser\MutatingScope))                                        
     #3 phar:///Users/staabm/workspace/rector-src/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(539):                                                      
     PHPStan\Node\ClassStatementsGatherer->__invoke(Object(PhpParser\Node\Expr\FuncCall), Object(PHPStan\Analyser\MutatingScope))                                                   
     #4 phar:///Users/staabm/workspace/rector-src/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(2631):                                                     
     PHPStan\Analyser\NodeScopeResolver::PHPStan\Analyser\{closure}(Object(PhpParser\Node\Expr\FuncCall), Object(PHPStan\Analyser\MutatingScope))                                   
     #5 phar:///Users/staabm/workspace/rector-src/vendor/phpstan/phpstan/phpstan.phar/src/Analyser/NodeScopeResolver.php(1570):                                                     
     PHPStan\Analyser\NodeScopeResolver->callNodeCallbackWithExpression(Object(Closure), Object(PhpParser\Node\Expr\FuncCall), Object(PHPStan\Analyser\MutatingScope),              
     Object(PHPStan\Analyser\ExpressionContext))       

ignore getter/setter methods

I can see devs spending an enourmous amount of time re-validating whether unused-public really is correct about a certain super simple setter and/or getter method is really unused (because e.g. some framework magic might be involved unused-public cannot know about).

IMO hunting within a codebase for a unused simple getter and/or setter is not worth the time doing so.

what do you think about ignoring trivial setters/getters like

class X {
  public function setY($y) {
    $this->y = $y;
  }
  public function getY() {
    return $this->y;
  }
}

?

it could either be a new option or maybe done by default... wdyt?

Unused interface methods not considered unused?

Hi,

This looks like a very promising project to clean up some legacy project. But it seems like it doesn't consider a method defined in an interface to be "unused" as long as that interface is implemented? In other words, if I drop the method from the interface it will consider the implementation to be an unused public method. But with the method declared in the interface it doesn't do so.

If this feature is considered it might need to be an option though? As an interface can also be used for some public API in which case the methods declared in the API should be considered the same as /** @api */ marked methods I assume.

Detecting unused parameter

Hello!

I've been playing with your library, and it has discovered quite a big amount of cases on our codebase! Thanks!

I was wondering if the detection of unused method/function argument (in the body of this method/function) could be considered as an addition to this library?
Or is it out of scope?

Let me show you an example to illustrate: https://phpstan.org/r/d7b91f7c-9bfe-4ea3-b6cf-2a89d34e05a7 (here the $uppercased argument would be detected as unused).

I'd be happy to work on a patch if that's so.

Does `@phpstan-ignore-line` not work?

Does @phpstan-ignore-line not work?

Code:

<?php

class Foo // @phpstan-ignore-line
{
    public const FOO = 'foo'; // @phpstan-ignore-line

    public string $property = 'foo';

    public function foo(): string // @phpstan-ignore-line
    {
        return 'foo';
    }
}

Result:

 ------ ------------------------------------------------
  Line   Foo.php
 ------ ------------------------------------------------
  3      Public property "Foo::$property" is never used
  5      Public constant "Foo::FOO" is never used
  9      Public method "Foo::foo()" is never used
 ------ ------------------------------------------------

Callbacks

Hi,

your tool is awesome. I have only issues with callbacks - https://www.php.net/manual/en/language.types.callable.php.
It seems does not detect them.

Example:

//collection object
final class ObjectCollection implements Collection
{
    public static function jsonDeserialize(array $data): self
    {
        // Does not detect
        return new self(...array_map(Object::jsonDeserialize(...), $data));
    }

    private function __construct(Object...$items)
    {
        $this->items = array_values($items);
    }
}

//object
final class Object
{
    public static function jsonDeserialize(array $data): self
    {
        ....
    }
    ...
}

//execute
$items = ObjectCollection::jsonDeserialize([]);

Result:

Line   Object.php
 ------ -------------------------------------------------------------------------------------------
  xx     Public method "Object::jsonDeserialize()" is never used
         💡 Either reduce visibility or annotate it or its class with @api

It does not detect usage of Object in ObjectCollection.
Object is used in new self(...array_map(Object::jsonDeserialize(...), $data)).
Same result also with new self(...array_map([Object::class, 'jsonDeserialize'], $data));.

Similar result with anonymous function.
Example:

/**
 * Self-called anonymous function that creates its own scope and keep the global namespace clean.
 */
(static function (): void {
    $container = ContainerBuilder::build();

    /** @var Provisioner $provisioner */
    $provisioner = $container->get(Provisioner ::class);
    assert($provisioner instanceof Provisioner);
    
    // Does not detect
    $provisioner->provision();
})();

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.