Skip to content

ClassStatementsGatherer blindly collects all 2-element arrays into methodCalls, causing UnusedPrivateMethodRule to evaluate isCallable() and trigger class reflection for data arrays - #6759

Open
alfredbez wants to merge 1 commit into
phpstan:2.3.xfrom
alfredbez:fix/unused-private-method-gatherer-callable-array

Conversation

@alfredbez

@alfredbez alfredbez commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes phpstan/phpstan#15494

In ClassStatementsGatherer, any 2-element array is unconditionally added to ->methodCalls. When a class defines at least one unused private method, UnusedPrivateMethodRule evaluates ->isCallable() on every gathered array in the class. For 2-element data arrays whose first element names a class, ConstantArrayType::isCallable() triggers reflection on that class, even though private methods of the current class can only be called on ``, self, `static`, or the declaring class name.

Reproducer

See phpstan/phpstan#15494

class BigContainer
{
}

class Service
{
    private function unusedMethod(): void
    {
    }

    public function process(string ): bool
    {
         = ['BigContainer', 'packstation'];
        return in_array(, , true);
    }
}

Expected Behavior

Two-element arrays whose first item is not ``, self::class, `static::class`, `ClassName::class` (matching the current class), or `::class` should not be gathered into `ClassStatementsGatherer::`. This avoids reflecting unrelated classes as callable targets during unused private method analysis.

@alfredbez
alfredbez force-pushed the fix/unused-private-method-gatherer-callable-array branch from c8ec7bb to 21c42f6 Compare October 17, 2026 04:35
@staabm

staabm commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Please report a issue with a reproducer showcasing the problem you are trying to fix.

@alfredbez

Copy link
Copy Markdown
Contributor Author

Reported with reproducer as requested: phpstan/phpstan#15494. Thank you!


public function test(): void
{
$a = [$this, 'method'];

@staabm staabm Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't work for

$a = [$this, rand(0, 1) ? 'method' : 'someOther'];

or

$m = rand(0, 1) ? 'method' : 'someOther';
$a = [$this, $m];

@staabm

staabm commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

methodCalls is passed into ClassMethodsNode which is @api .. so there might be some rules or extensions which need methodCalls for non private methods.

just because phpstan-src itself is fine just with the private-method-calls, this might not apply in general.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ClassStatementsGatherer gathers all 2-element arrays into methodCalls, triggering class reflection in UnusedPrivateMethodRule

2 participants