This article was originally written in 2018. I decided to republish it here as part of my personal archive. Some technical details may be outdated, but the ideas and approaches reflect how Magento worked at that time.
So, let’s start 😎
Introduction
In a production environment, Magento should run on a read-only file system; this is a security recommendation.
Only the following folders should be writable:
- app/etc
- pub/static
- pub/media
- var
Important note: the folder for generated classes was moved from var/generation to generated/code. Currently, this folder is read-only in production environments. For example, folders have similar permissions on Magento Cloud.
So, if you add a dependency incorrectly, Magento can break.
A class at an entry point
A developer wrote a class at an entry point and added a dependency on a generated factory to the constructor of this class.
<?php
use YourVendor\SomeModule\Model\GeneratedFactory;
require realpath(__DIR__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
class SomeClass
{
private $generatedFactory;
public function __construct(GeneratedFactory $generatedFactory)
{
$this->generatedFactory = $generatedFactory;
}
// Some code here...
}
$someObject = $bootstrap->getObjectManager()->create(SomeClass::class);
// There is some code that uses $someObject
This example will work correctly in developer mode — GeneratedFactory will be generated on the fly. But in production mode, on a read-only file system, you will face an error saying that GeneratedFactory cannot be saved in the generated/code folder.
This happens because Magento does not scan entry points during the execution of the bin/magento setup:di:compile command. If entry points contain definitions of generated classes, they will be ignored.
How to fix it?
The first way is to create a real factory present in the file system.
The second way is to move the class SomeClass into the app/code/YourVendor/SomeModule folder. Then Magento code sniffer will find the dependency on the generated class GeneratedFactory in SomeClass, and the code generator will generate this factory.
The example of SomeClass:
<?php
namespace YourVendor\SomeModule;
use YourVendor\SomeModule\Model\GeneratedFactory;
class SomeClass
{
private $generatedFactory;
public function __construct(GeneratedFactory $generatedFactory)
{
$this->generatedFactory = $generatedFactory;
}
// Some code here...
}
Then, edit the my_api/index.php entry point.
<?php
use YourVendor\SomeModule\SomeClass;
require realpath(__DIR__) . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
$someObject = $bootstrap->getObjectManager()->create(SomeClass::class);
// There is some code that uses $someObject
Wrong dependency injection
Incorrectly adding a dependency on a generated class to an existing class. For example, the following code will work correctly in developer mode but will fail in production mode on a read-only file system.
<?php
namespace YourVendor\SomeModule;
use YourVendor\SomeModule\Model\GeneratedFactory;
use Magento\Framework\App\ObjectManager;
class SomeClass
{
private $generatedFactory;
private $someParam;
public function __construct($someParam)
{
$this->someParam = $someParam;
$this->generatedFactory = ObjectManager::getInstance()->get(GeneratedFactory::class);
}
// Some code here...
}
How to fix it?
A similar approach is sometimes used to add dependencies and keep backward compatibility. However, this approach has the same problem as the example above. During the execution of the bin/magento setup:di:compile command, the generated class will not be created.
There are two ways to fix this as well.
The first way is to create a real factory present in the file system.
The second way is to slightly change the constructor of SomeClass.
An example of SomeClass:
<?php
namespace YourVendor\SomeModule;
use YourVendor\SomeModule\Model\GeneratedFactory;
use Magento\Framework\App\ObjectManager;
class SomeClass
{
private $generatedFactory;
private $someParam;
public function __construct($someParam, GeneratedFactory $generatedFactory = null)
{
$this->someParam = $someParam;
$this->generatedFactory = $generatedFactory ?: ObjectManager::getInstance()->get(GeneratedFactory::class);
}
// Some code here...
}
In this way:
- we added a new dependency
- we kept backward compatibility
- the generated class will be generated
- Magento works correctly in production mode on a read-only file system
If you have any questions or comments, feel free to leave them 😉
You can find an example of incorrect dependency injection in a Magento module in this repository: https://github.com/BaDos/example-wrong-di

Leave a Reply