8000 [DependencyInjection] Request for anonymous function factories · Issue #28992 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
[DependencyInjection] Request for anonymous function factories #28992
Closed
@unkind

Description

@unkind

It was already proposed few years ago: #12115, but I want to revisit this issue again.

I'm OK if you still don't accept

return function (ContainerConfigurator $c) {
    $s = $c->services()
        ->defaults()
        ->autoconfigure(true)
        ->autowire(true)
        ->private();

    $s->set('doctrine_pdo', \Doctrine\DBAL\Connection::class)
        ->factory(function () {
            return DriverManager::getConnection(...);
        });

};

, but I want it at least for my projects.

Major issues with this proposal were:

  1. use may hold reference to object and cannot be inlined to the generated static factory.
  2. it might be actually closed by (to be a closure) some object ($this) and unfortunately, PHP documentation makes no difference between anonymous functions and closures. But this one could be solved by requirement to prefix anonymous functions with "static" and throw exception in case of violation:
     ->factory(
         static function (...) {
             // ...
         }
     )
    
  3. this one by @stof, but I still don't get it, probably it was about inlining?

Btw, I see a big drawback with this approach: any time your factory references another of its methods to get a dependency, it will create its own instance of the services, which will not be the same than the shared service used by the container.

  1. It required to have dependency on SuperClosure.

But since you have symfony/contracts repo, I think we can add something like Symfony\Closures\ClosureDumper interface and make corresponding changes in the PhpDumper. It allows you to avoid dependency on 3rd party libraries like SuperClosure, but opens extension points for users like me:

namespace Symfony\Component\BoostedReflection;

interface AnonymousFunctionDumper
{
    /**
     * @throws AnonymousFunctionScopeContainsReferences foo () use (&a, $object)
     * @throws AnonymousFunctionIsClosureForReal Holds reference to $this
     *                    i.e. is not an anonymous function, strictly speaking
     * @return PHP code
     */
    public function dump(\Closure $closure): string;
}

btw, Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface could be moved to the symfony/contracts too.

So 1., 2. are not problem for me at all; 3. is weird for me. 4. not a problem anymore with symfony/contracts.

Another possible solution is to allow injecting custom container configurators. I can collect all closures, generate static factories for each of them and replace factory definition with it.


Real example: I had a need to integrate Doctrine migrations to the project, but without ORM. I had to translate all these instructions into this Symfony fluent PHP dialect of the plain PHP:

    $s->set(Application::class)
        ->call('setDispatcher', [ref(EventDispatcherInterface::class)])
        ->call('setHelperSet', [ref(HelperSet::class)])
        ->call('addCommands', [[
            inline(ExecuteCommand::class),
            inline(GenerateCommand::class),
            inline(LatestCommand::class),
            inline(MigrateCommand::class),
            inline(DiffCommand::class)->args([ref(MigrationsConfigurator::class)]),
            inline(UpToDateCommand::class),
            inline(StatusCommand::class),
            inline(VersionCommand::class)
        ]]);
    $s->set(Symfony\Component\Console\Helper\HelperSet::class)->args([
        [
            'db' => inline(ConnectionHelper::class)->args([ref('doctrine_pdo')]),
            'question' => inline(QuestionHelper::class),
            'configuration' => inline(ConfigurationHelper::class)
                ->args([
                    ref('doctrine_pdo'),
                    inline(Configuration::class)->factory(
                        [ref(MigrationsConfigurator::class), 'createConfiguration']
                    )
                ])
        ]
    ]);

instead of copy-pasting:

$s->setApplication::class)
    ->factory(function (PDO $pdo) {
        $cli = new Application();
        $cli->setHelperSet(
            new \Symfony\Component\Console\Helper\HelperSet(array(
                'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($pdo),
                'question' => new \Symfony\Component\Console\Helper\QuestionHelper(),
            ))
        );
        $cli->addCommands([
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand(),
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand(),
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\LatestCommand(),
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand(),
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand(
                // blah blah with $pdo
            ),
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\UpToDateCommand(),
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand(),
            new \Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand()
        ]);
        return $cli;
    });

Yes, the last example is awful, but the last one at least is real PHP.
Yes, I could create class with static method, but sometimes you're in position like "argh, I'm lazy for both variants, just, just be cool, OK?".

It is possible to add some extension point for this feature?

Metadata

Metadata

Assignees

No one assigned

    Labels

    DependencyInjectionFeatureRFCRFC = Request For Comments (proposals about features that you want to be discussed)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0