Skip to content

Doctrine is not fully available (proxies not generated) during Twig TemplateCacheWarmer run #18673

@dovys

Description

@dovys

Hi,

we're experiencing issues with warming up twig caches, which require the csrf token storage, which requires a doctrine proxy.

Real life scenario: implementing a memcached version of the TokenStorageInterface and using an entity to determine if the experimentation for it is enabled or not

Reproduction case:
https://github.com/InterNations/cachewarmupbug
A patch to the standard 2.8.4 edition: https://github.com/InterNations/cachewarmupbug/commit/eac6d0d5a2663b60e68577d6a934388cb28466d6

php app/console cache:warmup --no-debug -v
// Warming up the cache for the dev environment with debug false
PHP Warning:  require(app/cache/dev/doctrine/orm/Proxies/__CG__AppBundleEntityTest.php): failed to open stream: No such file or directory in vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209

Warning: require(app/cache/dev/doctrine/orm/Proxies/__CG__AppBundleEntityTest.php): failed to open stream: No such file or directory in vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209
PHP Fatal error:  require(): Failed opening required 'app/cache/dev/doctrine/orm/Proxies/__CG__AppBundleEntityTest.php' (include_path='.:') in vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209

Fatal error: require(): Failed opening required 'app/cache/dev/doctrine/orm/Proxies/__CG__AppBundleEntityTest.php' (include_path='.:') in vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209
# config.yml
doctrine:
    orm:
        auto_generate_proxy_classes: false
# services.xml
<service id="security.csrf.token_storage" class="AppBundle\Security\Csrf\TokenStorage\DatabaseTokenStorage">
     <argument type="service" id="doctrine.orm.entity_manager" />
</service>
# DatabaseTokenStorage
class DatabaseTokenStorage implements TokenStorageInterface
{
    public function __construct(EntityManagerInterface $em)
    {
        $proxy = $em->getReference(Test::class, 123);
    }

The call chain is:
TemplateCacheWarmer -> getTwigService -> getCsrfTokenManager -> getDatabaseTokenStorage -> getEntityManager -> getEntityRepository -> Proxy of Test::class (which is not generated yet)

#0 src/AppBundle/Security/Csrf/TokenStorage/DatabaseTokenStorage.php(12): Doctrine\ORM\EntityManager->getReference('AppBundle\\Entit...', 123)
#1 app/cache/dev/appDevProjectContainer.php(1846): AppBundle\Security\Csrf\TokenStorage\DatabaseTokenStorage->__construct(Object(Doctrine\ORM\EntityManager))
#2 vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php(316): appDevProjectContainer->getSecurity_Csrf_TokenStorageService()
#3 app/cache/dev/appDevProjectContainer.php(1833): Symfony\Component\DependencyInjection\Container->get('security.csrf.t...')
#4 vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php(316): appDevProjectContainer->getSecurity_Csrf_TokenManagerService()
#5 app/cache/dev/appDevProjectContainer.php(2868): Symfony\Component\DependencyInjection\Container->get('security.csrf.t...', 2)
#6 vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php(316): appDevProjectContainer->getTwigService()
#7 app/cache/dev/appDevProjectContainer.php(337): Symfony\Component\DependencyInjection\Container->get('twig')
#8 vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php(316): appDevProjectContainer->getCacheWarmerService()
#9 vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php(62): Symfony\Component\DependencyInjection\Container->get('cache_warmer')
#10 vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php(259): Symfony\Bundle\FrameworkBundle\Command\CacheWarmupCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(860): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#12 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(192): Symfony\Component\Console\Application->doRunCommand(Object(Symfony\Bundle\FrameworkBundle\Command\CacheWarmupCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#13 vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php(92): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#14 vendor/symfony/symfony/src/Symfony/Component/Console/Application.php(123): Symfony\Bundle\FrameworkBundle\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#15 app/console(29): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput))
#16 {main}

The cache warmers in the dumped container:

protected function getCacheWarmerService()
    {
        $a = $this->get('kernel');
        $b = $this->get('templating.filename_parser');

        $c = new \Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinder($a, $b, ($this->targetDirs[2].'/Resources'));

        return $this->services['cache_warmer'] = new \Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate(array(
0 => new \Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplatePathsCacheWarmer($c, $this->get('templating.locator')),
1 => $this->get('kernel.class_cache.cache_warmer'),
2 => new \Symfony\Bundle\FrameworkBundle\CacheWarmer\TranslationsCacheWarmer($this->get('translator')),
3 => new \Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer($this->get('router')), 
4 => new \Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer($this, $c, array()), 
5 => new \Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheWarmer($this->get('twig'), new \Symfony\Bundle\TwigBundle\TemplateIterator($a, $this->targetDirs[2], array())), 
6 => new \Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer($this->get('doctrine'))));
    }

There is a way to circumvent the issue by making the DatabaseTokenStorage service lazy, but my expectation is that Doctrine should be fully available to use once the container is built.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions