Proxy class in Magento 2
Proxy is one of the important design pattern of Magento 2. This design pattern is solve performance related huge problems in Magento 2. Therefore I have decided to create post for proxy in Magento 2 and We are going to discuss about below points.

Proxy in Magento 2


What is the Proxy class in Magento 2?


The Proxy is design pattern and the goal of design pattern is solve repeatable or redundant problems in the project.
In terms of technically or programmatically , proxy is a class that can be use for other class, same like surrogate. Surrogate that means to act on behalf of others.

Now, Lets understand  Why We use proxy design pattern in Magento 2 and problem comes in magento2.

What is the Problem in Magento 2.


As we all know that, Magento 2 introduced new concept that is dependency injection. There are different different types of dependency injection like constructor injection and method injection...etc. Magento 2 use both this type of injection but the actual problem comes with constructor injection.

If you want any other class object in your class, you just insert that object in your constructor and then use that object in your class.
Once you do this, when your class will be instantiated, it will also instantiate all the dependencies of your class constructor.

That's why chain reaction of object creation will be processed, that can slow down the process or performance. Therefore, Magento 2 use proxy design pattern for solve chain reaction of object creation problem.


How Proxy solved Magento 2 Problem ?

Using di.xml(Dependency injection file), we can easily change object params in class constructor. We have to decide as a developer and understand that which class is wrong, stop that class instantiate from the constructor and add that class in di.xml as type configuration and create proxy class for that.
Please check below code.


       
<type name="Magento\Catalog\Model\ResourceModel\Product\Collection">
    <arguments>
        <argument name="catalogUrl" xsi:type="object">Magento\Catalog\Model\ResourceModel\Url\Proxy</argument>
        <argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument>
   </arguments>
</type>


The above code is from the catalog.xml.  In this xml, the type declaration for class is Magento\Catalog\Model\ResourceModel\Product\Collection.
In this Magento\Catalog\Model\ResourceModel\Product\Collection class, the constructor argument catalogUrl and customerSession are replaced with Magento\Catalog\Model\ResourceModel\Url\Proxy and Magento\Customer\Model\Session\Proxy.

These both class have same name "Proxy" but you can not find this proxy class in our magento 2 code base. This Proxy class is created automatically in generated folder by Magento.
The Proxy class can be generated in two situations :

  • When we execute below magento CLI command.
  •  php bin/magento setup:di:compile
    When we run this command, it will check proxy class in all the di.xml files of all the modules installed in magento 2. If this class does not exit then it will generate automatically in generated folder in magento 2 root.

  • In second case, when proxy class is requested and it's not available in magento 2 code base will not throw any exception. it will simply create this class in generated folder in magento 2 root.

Magento 2 Proxy example


let's have look proxy class real time example:

<?php
namespace Magento\Customer\Model\Session;

/**
 * Proxy class for @see \Magento\Customer\Model\Session
 */
class Proxy extends \Magento\Customer\Model\Session 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\Customer\Model\Session
     */
    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\\Customer\\Model\\Session', $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\Customer\Model\Session
     */
    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 getCustomerConfigShare()
    {
        return $this->_getSubject()->getCustomerConfigShare();
    }

    /**
     * {@inheritdoc}
     */
    public function setCustomerData(\Magento\Customer\Api\Data\CustomerInterface $customer)
    {
        return $this->_getSubject()->setCustomerData($customer);
    }

    /**
     * {@inheritdoc}
     */
    public function getCustomerData()
    {
        return $this->_getSubject()->getCustomerData();
    }

    /**
     * {@inheritdoc}
     */
    public function getCustomerDataObject()
    {
        return $this->_getSubject()->getCustomerDataObject();
    }

    /**
     * {@inheritdoc}
     */
    public function setCustomerDataObject(\Magento\Customer\Api\Data\CustomerInterface $customerData)
    {
        return $this->_getSubject()->setCustomerDataObject($customerData);
    }

    /**
     * {@inheritdoc}
     */
    public function setCustomer(\Magento\Customer\Model\Customer $customerModel)
    {
        return $this->_getSubject()->setCustomer($customerModel);
    }

    /**
     * {@inheritdoc}
     */
    public function getCustomer()
    {
        return $this->_getSubject()->getCustomer();
    }

    /**
     * {@inheritdoc}
     */
    public function setCustomerId($id)
    {
        return $this->_getSubject()->setCustomerId($id);
    }

    /**
     * {@inheritdoc}
     */
    public function getCustomerId()
    {
        return $this->_getSubject()->getCustomerId();
    }

    /**
     * {@inheritdoc}
     */
    public function getId()
    {
        return $this->_getSubject()->getId();
    }

    /**
     * {@inheritdoc}
     */
    public function setId($customerId)
    {
        return $this->_getSubject()->setId($customerId);
    }

    /**
     * {@inheritdoc}
     */
    public function setCustomerGroupId($id)
    {
        return $this->_getSubject()->setCustomerGroupId($id);
    }

    /**
     * {@inheritdoc}
     */
    public function getCustomerGroupId()
    {
        return $this->_getSubject()->getCustomerGroupId();
    }

    /**
     * {@inheritdoc}
     */
    public function isLoggedIn()
    {
        return $this->_getSubject()->isLoggedIn();
    }

    /**
     * {@inheritdoc}
     */
    public function checkCustomerId($customerId)
    {
        return $this->_getSubject()->checkCustomerId($customerId);
    }

    /**
     * {@inheritdoc}
     */
    public function setCustomerAsLoggedIn($customer)
    {
        return $this->_getSubject()->setCustomerAsLoggedIn($customer);
    }

    /**
     * {@inheritdoc}
     */
    public function setCustomerDataAsLoggedIn($customer)
    {
        return $this->_getSubject()->setCustomerDataAsLoggedIn($customer);
    }

    /**
     * {@inheritdoc}
     */
    public function loginById($customerId)
    {
        return $this->_getSubject()->loginById($customerId);
    }

    /**
     * {@inheritdoc}
     */
    public function logout()
    {
        return $this->_getSubject()->logout();
    }

    /**
     * {@inheritdoc}
     */
    public function authenticate($loginUrl = null)
    {
        return $this->_getSubject()->authenticate($loginUrl);
    }

    /**
     * {@inheritdoc}
     */
    public function setBeforeAuthUrl($url)
    {
        return $this->_getSubject()->setBeforeAuthUrl($url);
    }

    /**
     * {@inheritdoc}
     */
    public function setAfterAuthUrl($url)
    {
        return $this->_getSubject()->setAfterAuthUrl($url);
    }

    /**
     * {@inheritdoc}
     */
    public function regenerateId()
    {
        return $this->_getSubject()->regenerateId();
    }

    /**
     * {@inheritdoc}
     */
    public function writeClose()
    {
        return $this->_getSubject()->writeClose();
    }

    /**
     * {@inheritdoc}
     */
    public function __call($method, $args)
    {
        return $this->_getSubject()->__call($method, $args);
    }

    /**
     * {@inheritdoc}
     */
    public function start()
    {
        return $this->_getSubject()->start();
    }

    /**
     * {@inheritdoc}
     */
    public function isSessionExists()
    {
        return $this->_getSubject()->isSessionExists();
    }

    /**
     * {@inheritdoc}
     */
    public function getData($key = '', $clear = false)
    {
        return $this->_getSubject()->getData($key, $clear);
    }

    /**
     * {@inheritdoc}
     */
    public function getSessionId()
    {
        return $this->_getSubject()->getSessionId();
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return $this->_getSubject()->getName();
    }

    /**
     * {@inheritdoc}
     */
    public function setName($name)
    {
        return $this->_getSubject()->setName($name);
    }

    /**
     * {@inheritdoc}
     */
    public function destroy(?array $options = null)
    {
        return $this->_getSubject()->destroy($options);
    }

    /**
     * {@inheritdoc}
     */
    public function clearStorage()
    {
        return $this->_getSubject()->clearStorage();
    }

    /**
     * {@inheritdoc}
     */
    public function getCookieDomain()
    {
        return $this->_getSubject()->getCookieDomain();
    }

    /**
     * {@inheritdoc}
     */
    public function getCookiePath()
    {
        return $this->_getSubject()->getCookiePath();
    }

    /**
     * {@inheritdoc}
     */
    public function getCookieLifetime()
    {
        return $this->_getSubject()->getCookieLifetime();
    }

    /**
     * {@inheritdoc}
     */
    public function setSessionId($sessionId)
    {
        return $this->_getSubject()->setSessionId($sessionId);
    }

    /**
     * {@inheritdoc}
     */
    public function getSessionIdForHost($urlHost)
    {
        return $this->_getSubject()->getSessionIdForHost($urlHost);
    }

    /**
     * {@inheritdoc}
     */
    public function isValidForHost($host)
    {
        return $this->_getSubject()->isValidForHost($host);
    }

    /**
     * {@inheritdoc}
     */
    public function isValidForPath($path)
    {
        return $this->_getSubject()->isValidForPath($path);
    }

    /**
     * {@inheritdoc}
     */
    public function expireSessionCookie()
    {
        return $this->_getSubject()->expireSessionCookie();
    }
}


Now let's understand this class code.

  • The proxy class namespace same with original class Magento\Customer\Model\Session.
  • The original class is extended by the proxy class Magento\Customer\Model\Session so we can use it's public methods in proxy class.
  • Proxy class implements \Magento\Framework\ObjectManager\NoninterceptableInterface interface. This is a marker interface which has no methods and no member variable that means this class cannot be intercepted.
  • As we can see in above proxy class's constructor, it's only one dependency which is ObjectManager class. That is the reason it will improve performance or load speed significantly.
  • __sleep and __wakeup are PHP magic functions is called, When a class is destroyed or invoked for perform some actions.
  • The _getSubject method is use for return the original class object when it is asked or requested.
  • The others functions are public functions of parent class which is overridden in the proxy class.
That's it. I hope this post helps you for better understand and ideas about proxy class in Magento 2.