Description
Symfony version(s) affected
5.3
Description
The dependency injection container is usually updated automatically whenever a change has been made that requires an update to the container cache (when the debug mode is enabled).
However, when the service container is dumped into a single file (via the container.dumper.inline_factories
parameter) and lazy services are used this does not work under some conditions.
In our case, we have been seeing this error whenever we deleted a class (and often implicitly by switching branches):
Attempted to load class "EntityManager_9a5be93" from the global namespace. Did you forget a "use" statement?
The only way to fix this was to manually delete the cache. It's probably an edge case, but this bug has been hunting us for months.
How to reproduce
I've created a little demo project that is based on the Symfony demo application: https://github.com/digilist/symfony-class-loading-bug
These are basically the changes (composer.lock and symfony.lock ignored):
diff --git a/composer.json b/composer.json
index c00de53..2e38c71 100644
--- a/composer.json
+++ b/composer.json
@@ -28,8 +28,9 @@
"symfony/mailer": "^5.3",
"symfony/monolog-bundle": "^3.1",
"symfony/polyfill-intl-messageformatter": "^1.12",
- "symfony/security-bundle": "^5.3",
+ "symfony/proxy-manager-bridge": "5.3.*",
"symfony/runtime": "^5.3",
+ "symfony/security-bundle": "^5.3",
"symfony/string": "^5.3",
"symfony/translation": "^5.3",
"symfony/twig-pack": "^1.0",
diff --git a/config/services.yaml b/config/services.yaml
index 2c46868..3f102ef 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -8,6 +8,7 @@ parameters:
# This parameter defines the codes of the locales (languages) enabled in the application
app_locales: ar|en|fr|de|es|cs|nl|ru|uk|ro|pt_BR|pl|it|ja|id|ca|sl|hr|zh_CN|bg|tr|lt|bs|sr_Cyrl|sr_Latn
app.notifications.email_sender: anonymous@example.com
+ container.dumper.inline_factories: true
services:
# default configuration for services in *this* file
@@ -29,6 +30,11 @@ services:
- '../src/Kernel.php'
- '../src/Tests/'
+ App\Utils\:
+ resource: '../src/Utils'
+ public: true # prevent dropping unused dependencies
+ lazy: true
+
# when the service definition only contains arguments, you can omit the
# 'arguments' key and define the arguments just below the service class
App\EventSubscriber\CommentNotificationSubscriber:
diff --git a/src/Utils/SomeDependency.php b/src/Utils/SomeDependency.php
new file mode 100644
index 0000000..6c61fc8
--- /dev/null
+++ b/src/Utils/SomeDependency.php
@@ -0,0 +1,10 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Utils;
+
+class SomeDependency
+{
+
+}
After applying the changes and opening the application (i.e. warming up the cache) you need to delete the SomeDependency
class to see this error on the next request:
Attempted to load class "EntityManager_9a5be93" from the global namespace.
Did you forget a "use" statement?
Possible Solution
No response
Additional Context
The App_KernelDevDebugContainer.php
file is generated like this:
/**
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
*/
class App_KernelDevDebugContainer extends Container
{
// [...]
}
class SomeDependency_91c8bfe extends \App\Utils\SomeDependency implements \ProxyManager\Proxy\VirtualProxyInterface
{
// [...]
}
if (!\class_exists('SomeDependency_91c8bfe', false)) {
\class_alias(__NAMESPACE__.'\\SomeDependency_91c8bfe', 'SomeDependency_91c8bfe', false);
}
class EntityManager_9a5be93 extends \Doctrine\ORM\EntityManager implements \ProxyManager\Proxy\VirtualProxyInterface
{
// [...]
}
if (!\class_exists('EntityManager_9a5be93', false)) {
\class_alias(__NAMESPACE__.'\\EntityManager_9a5be93', 'EntityManager_9a5be93', false);
}
When the SomeDependency
class is deleted the cache file results in an error when it is included by the Kernel class: Class "App\Utils\SomeDependency" not found
The container cache file is included here:
symfony/src/Symfony/Component/HttpKernel/Kernel.php
Lines 452 to 465 in 3b71d1a
As you can see, the include in line 453 is wrapped in a try..catch block that catches any error and therefore, the error of the missing class is ignored.
Then, in line 465 is checked if the container class has been defined. This is actually the case, because the container class is defined at the very top of the file.
I am not entirely sure about the code that follows in the next block, but I think it considers the container valid and doesn't consider a new cache warmup, because it returns in line 483:
symfony/src/Symfony/Component/HttpKernel/Kernel.php
Lines 465 to 489 in 3b71d1a