Magento 2 Proxies

Proxies is a powerful and easy technique to help you overcome a huge problem of Magento 2. However, its role and importance are still underestimated. Therefore, in this post, I am going to help you to understand what is Magento 2 proxies and its need in Magento 2.

Magento 2 Proxies

What is Proxies in Magento 2

Just similar to design patterns which are created to solve a redundant problem in the project, proxy design pattern solves a particular problem. Proxies work as a surrogate which means it acts on behalf of others, in programming, they are classes which could be used instead of any other class. More specifically, in Magento 2, proxies are used to replace resource hungry classes.

However, when replacing like that, an issue happens in Magento 2

Problem in Magento 2

In Magento 2, dependency injection was introduced by a new concept. Although there are various types of dependency, constructor injection and method injection are the best, and both of them are used by Magento 2. However, there is a problem with constructor injection.

In details, if you want any other class object to be used in your class, instead of instantiating it inside the class, you should inject that object to the constructor and then use it in the class. If you do that, when your class is instantiated, all the dependencies which are injected in your class constructor will also be instantiated. As a result, a chain reaction of object creation will be triggered, which can lead to the process being slow down. Therefore, to prevent the object creation’s chain reaction, the proxy design pattern is used.

How Proxies fix Magento 2’s problem

Dependency injection has influent on the solution for the issue which is mentioned above in some way. This is because the dependency injection configuration file (di.xml) gives you the power to change the object lifestyle also known as constructor params. As a developer, you can understand which class is the culprit easily, and stop its instantiation in the constructor. In order to do that, add a type configuration in the di.xml, please view the below code:

<type name="Magento\Catalog\Model\Product">
 
<arguments>
 
<argument name="catalogProductStatus" xsi:type="object">Magento\Catalog\Model\Product\Attribute\Source\Status\Proxy</argument>
 
<argument name="productLink" xsi:type="object">Magento\Catalog\Model\Product\Link\Proxy</argument>
 
</arguments>
 
</type>

The above entry is from the catalog module di.xml. You can see in this type declaration for Magento\Catalog\Model\Product class, the constructor argumets catalogProductStatus and productLink are replaced with Magento\Catalog\Model\Product\Attribute\Source\Status\Proxy and Magento\Catalog\Model\Product\Link\Proxy. These classes both have the name Proxy, you cannot find these proxy classes in Magento code base if you check your Magento after installing it. They are created automatically by the Magento 2 code generator.

These classes will be generated under two circumstances:

  • When the Magento cli command will be executed:
php bin/magento setup:di:compile

When you execute the command above, all the di.xml files of all the modules installed in Magento 2 will be checked, and if it encounters a Proxy class and it does not exist, it will be generated automatically with the same namespace which is inside ver/generation folder in the Magento 2 root.

  • The second circumstance is when a class which is a Proxy class is requested. this class is not being presented in the current code base and an error or exception will not be thrown. The class will be created inside var/generation folder and its object will be returned.

So from the information above, you can see that these classes are generated on the fly with some fixed convention, which is used to replace the original object. However, how this proxy class is going to help solve the issue is still a question. To answer that question, let’s have a look at the following proxy class:

<?php
 
namespace Magento\Catalog\Model\Product\Attribute\Source\Status;
 
/**
 
* Proxy class for @see \Magento\Catalog\Model\Product\Attribute\Source\Status
 
*/
 
class Proxy extends \Magento\Catalog\Model\Product\Attribute\Source\Status implements \Magento\Framework\ObjectManager\NoninterceptableInterface
 
{
 
/**
 
* Object Manager instance
 
*
 
* @var \Magento\Framework\ObjectManagerInterface
 
*/
 
protected $_objectManager = null;
 
/**
 
* Proxied instance name
 
*
 
* @var string
 
*/
 
protected $_instanceName = null;
 
/**
 
* Proxied instance
 
*
 
* @var \Magento\Catalog\Model\Product\Attribute\Source\Status
 
*/
 
protected $_subject = null;
 
/**
 
* Instance shareability flag
 
*
 
* @var bool
 
*/
 
protected $_isShared = null;
 
/**
 
* Proxy constructor
 
*
 
* @param \Magento\Framework\ObjectManagerInterface $objectManager
 
* @param string $instanceName
 
* @param bool $shared
 
*/
 
public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager, $instanceName = '\\Magento\\Catalog\\Model\\Product\\Attribute\\Source\\Status', $shared = true)
 
{
 
$this->_objectManager = $objectManager;
 
$this->_instanceName = $instanceName;
 
$this->_isShared = $shared;
 
}
 
/**
 
* @return array
 
*/
 
public function __sleep()
 
{
 
return ['_subject', '_isShared', '_instanceName'];
 
}
 
/**
 
* Retrieve ObjectManager from global scope
 
*/
 
public function __wakeup()
 
{
 
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
 
}
 
/**
 
* Clone proxied instance
 
*/
 
public function __clone()
 
{
 
$this->_subject = clone $this->_getSubject();
 
}
 
/**
 
* Get proxied instance
 
*
 
* @return \Magento\Catalog\Model\Product\Attribute\Source\Status
 
*/
 
protected function _getSubject()
 
{
 
if (!$this->_subject) {
 
$this->_subject = true === $this->_isShared
 
? $this->_objectManager->get($this->_instanceName)
 
: $this->_objectManager->create($this->_instanceName);
 
}
 
return $this->_subject;
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getVisibleStatusIds()
 
{
 
return $this->_getSubject()->getVisibleStatusIds();
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getSaleableStatusIds()
 
{
 
return $this->_getSubject()->getSaleableStatusIds();
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getAllOptions()
 
{
 
return $this->_getSubject()->getAllOptions();
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getOptionText($optionId)
 
{
 
return $this->_getSubject()->getOptionText($optionId);
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function addValueSortToCollection($collection, $dir = 'asc')
 
{
 
return $this->_getSubject()->addValueSortToCollection($collection, $dir);
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function setAttribute($attribute)
 
{
 
return $this->_getSubject()->setAttribute($attribute);
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getAttribute()
 
{
 
return $this->_getSubject()->getAttribute();
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getOptionId($value)
 
{
 
return $this->_getSubject()->getOptionId($value);
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getFlatColumns()
 
{
 
return $this->_getSubject()->getFlatColumns();
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getFlatIndexes()
 
{
 
return $this->_getSubject()->getFlatIndexes();
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getFlatUpdateSelect($store)
 
{
 
return $this->_getSubject()->getFlatUpdateSelect($store);
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function getIndexOptionText($value)
 
{
 
return $this->_getSubject()->getIndexOptionText($value);
 
}
 
/**
 
* {@inheritdoc}
 
*/
 
public function toOptionArray()
 
{
 
return $this->_getSubject()->toOptionArray();
 
}
 
}

Now we’re going to analyze this class to understand it:

  • The proxy class’s namespace is the same with the original one Magento\Catalog\Model\Product\Attribute\Source\Status. This would allow you easily replace it with the original object.
  • The original class is extended by the proxy class \Magento\Catalog\Model\Product\Attribute\Source\Status, which would allow the proxy class to use its public methods.
  • Proxy class implements \Magento\Framework\ObjectManager\NoninterceptableInterface interface. This is a marker interface which has no methods, no member variables. It is only used to signal that this class cannot be intercepted.
  • As you can see in its constructor, it only has one object dependency which is ObjectManager class. As a result, the load speed will be improved significantly, which would help you save a lot of resources such as space and time. Also, you will not see a parent constructor call.
  • When a class is destroyed or invoked to perform some actions, the php magic functions which are __sleep, __wakeup and __sleep executes.
  • The _getSubject method allows you to return the original class object when it is asked. As a result, an object will only be provided if it is requested.
  • Other functions are parent (or original) class public functions which are overridden in the proxy class so that on the proxy class object, they can be called. In order to get the parent object and call the same function, inside these functions the _getSubject is being called.

Conclusion

To sum up, using proxy classes can offer you various benefits:

  • Firstly, it is easy to use, instead of doing various customizations, you only need to entry in di.xml file, your problem will be solved.
  • It can save you a considerable amount of time, which can help improve the application’s performance.
  • Writing test cases for the generated classes are not required.
  • As all the proxies have a similar structure, you will be able to understand it easily. If you have any questions or new ideas about Magento 2 Proxies, feel free to leave a comment below.

Enjoyed the tutorial? Spread it to your friends!