Description
Preconditions (*)
Have the following in your project composer.json
as recommended since #16435 was merged:
"psr-0": {
"": [
"app/code/",
"generated/code/"
]
},
Reproduced on Magento 2.3.1, but if those lines are in your composer.json
file the issue will apply to many older versions as well since the relevant code hasn't changed for a long time.
Steps to reproduce (*)
Run the following commands in sequence:
bin/magento setup:di:compile
composer dump-autoload -o
bin/magento setup:di:compile
Note that this is a minimal sequence to reproduce the issue. Normally this would be seen with steps #1-2
run during one deploy and then step #3
run the next deploy.
Expected result (*)
Compilation succeeds both times.
Actual result (*)
Second compilation command fails with an error like this:
Warning: include(/var/www/html/vendor/composer/../../generated/code/Magento/Framework/App/ResourceConnection/Proxy.php):
failed to open stream: No such file or directory in /var/www/html/vendor/composer/ClassLoader.php on line 444
Analysis
The issue is that the core Cli
module includes magic early behavior to clear out the environment when a compilation command is being run, including deleting the generated code directories (see method Cli::assertCompilerPreparation()
and note it's called before Cli::initObjectManager()
). However, when the object manager is then initialized it tries to load classes from the Composer classmap that no longer exist, since they were in the deleted generated directories.
The method assertCompilerPreparation()
has the following comment:
/**
* Temporary workaround until the compiler is able to clear the generation directory
* @todo remove after MAGETWO-44493 resolved
*/
I don't know what MAGETWO-44493 is, but it appears that the DiCompileCommand
class does indeed already clear the generation directory itself (futilely since it's already cleared by that point), so the comment is at a minimum misleading.
Possible solutions:
- Refactor the magic deletions out of the generic
Cli
class and into the relevant command classes after the object manager is initialized. This would be ideal, but I assume part of the goal is to prevent the object manager from loading old generated code, so I'm not sure how feasible this is. - Leave the existing magic as it is and add additional magic to remove or replace the Composer autoloader when compiling to avoid trying to access deleted generated code, thereby skipping straight to dynamic generation.
Workaround
Until this is fixed the error can be avoided by manually deleting the generated
directory contents and then triggering a temporary classmap that excludes generated
(either by running composer install
or a simple composer dump-autoload
) before running DI compilation. (Credit for the workaround and helping along my investigation to jalogut/magento2-deployer-plus#29, which I came across when Googling this problem.)