Skip to content

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

Open
phpstan/phpstan-src
#6759

Description

@alfredbez

Bug report

In ClassStatementsGatherer, any 2-element array expression is unconditionally collected into $this->methodCalls as a candidate callable:

if ($itemCount === 2) {
    $this->methodCalls[] = $node;
}

When the enclosing class declares at least one private method, UnusedPrivateMethodRule iterates through all collected methodCalls and calls $arrayType->isCallable() on each one.

For 2-element data arrays whose first item is a class name (e.g. [BigContainer::class, 'packstation'] or string pairs), ConstantArrayType::isCallable() triggers reflection on that target class. However, private methods of the current class can only be called on $this, self, static, or the current class name itself. Collecting unrelated 2-element data arrays causes unnecessary class reflection.

I opened phpstan/phpstan-src#6759 with a proposed fix. Submitting this issue with the reproducer as requested by @staabm.

Code snippet that reproduces the problem

<?php

namespace App;

class BigContainer
{
}

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

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

Expected output

Two-element arrays whose first item cannot refer to the current class should not be gathered into ClassStatementsGatherer::$methodCalls, avoiding unnecessary class reflection during unused private method analysis.

Activity

  1. mergeable commented on Oct 17, 2026

    @mergeable

    This bug report is missing a link to reproduction at phpstan.org/try.

    It will most likely be closed after manual review.

  2. staabm commented on Oct 17, 2026

    @staabm
    Contributor

    Please add a phpstan.org/try example. I guess you have expectations regarding missing errors or false positives?

    if the root problem of your issue is efficiency, you should provide a reproducer which is inefficient and is improved by your fixes

  3. ondrejmirtes commented on Oct 24, 2026

    @ondrejmirtes
    Member

    Why does reflecting unrelated classes in this case bother you?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions