elie29 / zend-di-config

PSR-11 PHP-DI container configurator for Laminas, Mezzio, ZF, Expressive applications or any framework that needs a PSR-11 container
MIT License
19 stars 4 forks source link

Compile with Delegators #45

Closed elbakerino closed 4 years ago

elbakerino commented 4 years ago

Is your feature request related to a problem? Please describe. When a delegator is in use, the container can not be compiled: PHP Fatal error: Uncaught DI\\Definition\\Exception\ \InvalidDefinition: Cannot compile closures which import variables using theusekeyword in.... When disabling the used delegator, it works.

Describe the solution you'd like Even when using delegators, it would be fantastic for performance to be able to compile/cache it, the closure with use crashes the caching. Hints on what can be used otherwise would help also a lot! (Or if i'm again using something in the wrong part) Moving the routes in a central definition is not wanted in the architecture.

Additional context I'm using the ApplicationConfigInjectionDelegator to be able to have the route definitions in the corresponding module ConfigProvider, like explained here.

public function getDependencies(): array {
    return [
        'delegators' => [
            \Mezzio\Application::class => [
                \Mezzio\Container\ApplicationConfigInjectionDelegator::class,
            ],
        ],
    ];
}

Thanks! michael

elie29 commented 4 years ago

@elbakerino thank you for the issue.

As delegators are assigned by a closure, the fatal error came from PHP-DI as follow: Fatal error: Uncaught DI\Definition\Exception\InvalidDefinition: Cannot compile closures which import variables using theusekeyword in vendor\php-di\php-di\src\Compiler\Compiler.php on line 391

However, in order to preserve previous service and delegators, we cannot bypass the 'use' keyword.

You can add a factory that return \Mezzio\Application if you want to use the cache:

class MyApplicationFactory
{

    public function __invoke(ContainerInterface $container): \Mezzio\Application
    {
        $factory = new \Mezzio\Container\ApplicationConfigInjectionDelegator();
        $callable = function () use ($container) {
            return $container->get(\Mezzio\Application:class);
        };
        return $factory($container, \Mezzio\Application:class, $callable);
    }
}

And then in your ConfigProvider, you add the following:

public function getDependencies(): array {
    return [
        'factories' => [
            \Mezzio\Application::class => MyApplicationFactory::class,
        ],
    ];
}
elbakerino commented 4 years ago

Thanks for you help!

Adding it leaded to Circular dependency detected while trying to resolve entry 'Mezzio\\Application', should come from getting the Application from the container inside the factory that defines it.

For me it worked with using the \Mezzio\Container\ApplicationFactory inside:

class MyApplicationFactory {
    public function __invoke(ContainerInterface $container): \Mezzio\Application {
        $factory = new \Mezzio\Container\ApplicationConfigInjectionDelegator();
        $callable = static function() use ($container) {
            $app_factory = $container->get(\Mezzio\Container\ApplicationFactory::class);
            return $app_factory($container);
        };
        return $factory($container, \Mezzio\Application::class, $callable);
    }
}
elie29 commented 4 years ago

@elbakerino exactly. My code was just an example to help you. I did not test it.