(This post is the fifth in the “Upgrade your Magento 1 knowledge to Magento 2”; A series of posts about the major differences between Magento 1 and Magento 2. You can read the other posts in the series here.)
In this post we round-off our look at Magento 2’s Dependency Injection (DI) implementation by looking at its limitations and how to use it for class rewrites. Finally we take a look at the performance implications of DI and how Magento gets around these with the use of object proxies.
You are encouraged to use the di.xml to specify your dependencies and usually this will be enough. However, there are exceptions. What happens, however, when you require a specific object, e.g. A specific product object with specific data in it? The object manager can only instantiate generic instances of objects – it can’t provide loaded entities, so this kind of object is non-injectable. An injectable object is any object that can be instantiated by the object manager. Non-injectables cannot be instantiated by the object manager and these include database objects, objects loaded through ORM and objects which need to be instantiated with specific data which can’t be passed in through the object manager.
Non-injectable objects cannot specify other injectables in their constructors, as the recursive loading which allows the ‘automatic’ DI to work on injectable objects is not triggered. If an injectable object needs to produce non-injectable objects, the non-injectable objects’ factory class must be passed in the injectors’ constructor. The object factory class can then be used to create an instance of the non-injectable object.
Preferences as rewrites
Earlier I suggested that because Magento 2 specifies interfaces rather than concrete classes as the parameters of constructors, this allows us to specify any concrete class to implement that interface. This gives us a massive amount of flexibility when it comes to customising Magento 2. You can specify pretty much any concrete class, just so long as it implements the same interface.
In Magento 1, class rewrites took effect on a global level. If one module defined a class rewrite, then all instances of the rewritten class were replaced with an instance of the modules’ class.
In Magento 2, however, we can be more granular. If we have a Product class which specifies a StockManagementInterface class in its’ constructor, there are two ways of telling Magento 2 which class will be substituted for that interface.
We can define a preference which will tell Magento 2 to use our class whenever an instance of the StockManagementInterface is requested. This is closest to the Magento 1-style class rewrite, as it takes effect globally.
Alternatively, we can define a preference specifically for the Product object, which will tell Magento 2 to use our class only when it is requested in the constructor of the Product class. Now, whenever an instance of the Product class is requested, StockManagementInterface will be instantiated with an instance of our class. If an instance of StockManagementInterface is requested outside of the Product class, our class will not be used.
The auto-instantiation process works recursively. As soon as an object is requested, the object manager determines its’ dependencies and then goes through that chain and recursively instantiates the object and all the objects it depends on and all the objects that they depend on and so on. This can quickly become a very resource-intensive process, especially if there are dozens of objects defined. In order to reduce this burden, Magento uses a combination of reflection, code generation and object proxying to speed up this process and make it more efficient.
An object proxy is an instance of a wrapper class which extends from another base class. The benefit of using proxy classes is that they can be instantiated without instantiating a base class. If the base class takes a long time to instantiate, then this can have a negative impact on performance. The base class of a proxy class is only instantiated when any of the its’ methods are called, so if they are only used during some execution paths, this can have a marked improvement on performance.
You don’t need to write the wrapping proxy class and override all the methods yourself, fortunately. Whenever you specify a proxy class using type hinting in the parameters of a constructor list, Magento 2 will generate the proxy class for you. Proxy classes are generated alongside other classes, including factories, interceptors and builders and then stored in the
When running in developer mode, the class
Magento\Framework\ObjectManager\Definition\Runtime uses Reflection to read constructor signatures, which are then used by the object manager to auto-instantiate objects and all their dependencies every time they are requested. In production mode, this process of reading constructor signatures is only done once and the results are then cached in the
var/di directory. The class
Magento\Framework\ObjectManager\Definition\Runtime is then used by the object manager to auto-instantiate classes, resulting in performance gains.
Both code generation and definitions are generated either by switching into production mode or by executing the
setup:di:compile console tool command.
Well, I think that’s enough for this post. Next time we’ll continue our journey through the new features of Magento 2 and the major changes between both versions.