Skip to content

File analysis/performance issue when file defines a global list of closures. #13933

@GrzegorzDrozd

Description

@GrzegorzDrozd

Bug report

tl;dr

This takes 20+ minutes:

require_once 'vendor/autoload.php';

$list = [];

// list of static closures 
$list['alpha-1'] = static function (): App\Alpha\AlphaClass1 { return new App\Alpha\AlphaClass1(); };

This takes 8 seconds:

require_once 'vendor/autoload.php';

// $list = [];

// list of static closures 
$list['alpha-1'] = static function (): App\Alpha\AlphaClass1 { return new App\Alpha\AlphaClass1(); };

Detailed explanation

In my project we don't use a full Symfony framework. We use a lot of components. One of the components that we don't use is a service locator and its compiler pass. That is why we add items to a Console component manually. Until now our code looked like this:

$application->add(new FooCommand(
    $container->get(Dependency1::class),
    $container->get(Dependency2::class),
    $container->get(Dependency3::class),
));

That was not efficient because all commands were created upfront even when not used.
We decided to do some refactoring and use FactoryCommandLoader that accepts an array where key is a command and value is a closure to create that command.

Finally, our code looked like this:

$list = [];

$list['foo:command'] = static function () :Command {
    return new FooCommand(
        $container->get(Dependency1::class),
        $container->get(Dependency2::class),
        $container->get(Dependency3::class),
    );
};
// and so on for 460+ commands

And this basic change made phpstan to run in 20+ minutes instead of the previous 8 seconds.

I have tried to use ContainerCommandLoader and that also worked fine but that adds 460 entries into dependency injection container that are used only in this one place.

$list = []

$di->set(FooCommand::class, function (Container $container) : FooCommand {
    return new FooCommand(
        $container->get(Dependency1::class),
        $container->get(Dependency2::class),
        $container->get(Dependency3::class),
    );
});
$list['foo:command'] = FooCommand::class;
// and so on for 460+ commands

This has no negative effect on performance - it is similar to initial time. Difference is that here closure is provided as an argument instead value in an array.

I have prepared a repository that reproduces this issue:
https://github.com/GrzegorzDrozd/phpstan-performance-issue-reproduction

I am not sure that this is phpstan issue.

Code snippet that reproduces the problem

https://github.com/GrzegorzDrozd/phpstan-performance-issue-reproduction

Expected output

Scanning should take similar amount of time.

Did PHPStan help you today? Did it make you happy in any way?

No response

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