Kamil
Kamil

Reputation: 424

Symfony Dependency Injection: Maximum function nesting level

I have project based on Symfony 4. I wanted to start writing functional tests based on WebTestCase. I enabled framework.test: true in configuration and provided APP_ENV=test in phpunit.

There is issue with services container being built for test env. I literally didn't change anything expect APP_ENV and framework.test.

When I fetch service from test cached container I end up with:

Maximum function nesting level of '256' reached, aborting!

In stack trace I can see that symfony's DI keeps trying to fetch the same service:

 ...
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getPanel_Model_EventService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:483
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getDefaultEventRepositoryService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:525
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getDbReachingEventTranslationProviderService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:509
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getCachingEventTranslationProviderService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:541
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getEventContextTakingTranslatorService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:402
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getModelConfiguratorService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:1089
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getPanel_Model_EventService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:483
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getDefaultEventRepositoryService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:525
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getDbReachingEventTranslationProviderService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:509
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getCachingEventTranslationProviderService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:541
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getEventContextTakingTranslatorService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:402
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getModelConfiguratorService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:1089
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getPanel_Model_EventService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:483
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getDefaultEventRepositoryService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:525
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getDbReachingEventTranslationProviderService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:509
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getCachingEventTranslationProviderService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:541
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getEventContextTakingTranslatorService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:402
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getModelConfiguratorService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:1089
 ContainerE6ODQnH\srcApp_KernelTestDebugContainer->getPanel_Model_EventService() at /var/www/html/panel/var/cache/test/ContainerE6ODQnH/srcApp_KernelTestDebugContainer.php:483
...

It's weird because I don't have circular reference in my definitions. In APP_ENV=dev everything is OK.

It looks like for some reason test container cannot remember references to existing services in $this->services property.

Do you know what are building differences of dev and test containers?

When I compare test container php file with dev version. They are indeed different. No reason why...

UPDATE

Here is example of generated service that is in the loop of invocations:

DEV

    protected function getDefaultEventRepositoryService()
    {
        $a = \ClassRegistry::init('Event');

        $this->services['Panel\\Events\\Repository\\DefaultEventRepository'] = $instance = new \Panel\Events\Repository\DefaultEventRepository($a, ($this->services['Panel\\Events\\Repository\\EventMapper'] ?? ($this->services['Panel\\Events\\Repository\\EventMapper'] = new \Panel\Events\Repository\EventMapper())), ($this->privates['timeProvider'] ?? ($this->privates['timeProvider'] = new \Panel\Core\Utils\CurrentTimeProvider())));

        ($this->services['CakeFramework\\ModelConfigurator'] ?? $this->getModelConfiguratorService())->configure($a);

        return $instance;
    }

TEST

    protected function getDefaultEventRepositoryService()
    {
        $a = $this->getPanel_Model_EventService();

        if (isset($this->services['Panel\\Events\\Repository\\DefaultEventRepository'])) {
            return $this->services['Panel\\Events\\Repository\\DefaultEventRepository'];
        }

        return $this->services['Panel\\Events\\Repository\\DefaultEventRepository'] = new \Panel\Events\Repository\DefaultEventRepository($a, ($this->services['Panel\\Events\\Repository\\EventMapper'] ?? ($this->services['Panel\\Events\\Repository\\EventMapper'] = new \Panel\Events\Repository\EventMapper())), ($this->privates['timeProvider'] ?? ($this->privates['timeProvider'] = new \Panel\Core\Utils\CurrentTimeProvider())));
    }

As you can see above there is slight difference. Test environment is using service method getPanel_Model_EventService. But in development environment it is injected directly $a = \ClassRegistry::init('Event');

This is causing circural reference despite service definition is the same. There is no additional service_test files. Any idea why?

Upvotes: 2

Views: 462

Answers (1)

Kamil
Kamil

Reputation: 424

I investigated this problem. It's caused by Symfony's configurator mechanism and lack of reporting circular dependencies.

I described this problem with code examples in public repo: https://github.com/kamilwylegala/symfony-configurator-circular-dependency

Switching to factory helped to figure out that there indeed is circular dependency. Using factory raises:

PHP Fatal error:  Uncaught Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException: Circular reference detected for service ...

Upvotes: 1

Related Questions