This is the last blog on code generation, in my previous articles we understood factory and interceptor code generation, in this blog we will try to understand magento2 proxy design pattern and code generation and its need in magento2.
What is proxy design pattern :
As we know design pattrens are meant for solving a redundant problem in the whole project , proxy design pattern also solves a specific problem. Proxies works as surrogate(means to act on behalf of others) , in programming proxies are classes that can be used in place of any other class, and in magento2 proxies are used in place resource hungry classes.
Lets understand why this problem comes in magento2.
Problem:
In magento2 a new concept introduced dependency injection, there are many types of dependency injection but the best one is constructor injection and method injection and magento2 uses both of them, but there is problem with constructor injection, constructor injection says if you want to use any other class object in your class, don’t instantiate it inside the class, inject the object to the constructor and then use the object in the class, so when your class will be instantiated all the dependencies injected in your class constructor will also get instantiated, and it will trigger a chain reaction of object creation, this can really slow down the process, so to stop chiain reaction of object creation magento2 uses proxy design pattern.
Solution:
Solution of this problem also depends somehow on dependency injection, Since the dependency injection configuration file(di.xml) gives you the control that you can change the object life style(constructor params). So as a developer you can easily understand which class is the culprit, and you need to stop its instantiation in the constructor, So you need to simply add a type configuration in di.xml like below:
1 2 3 4 5 6 7 8 9 10 11 |
<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 catalog module di.xml, as we can see in this type declaration for class “Magento\Catalog\Model\Product” , it replaces constructor argumets catalogProductStatus and productLink with Magento\Catalog\Model\Product\Attribute\Source\Status\Proxy and “Magento\Catalog\Model\Product\Link\Proxy” as you can see both these classes have name “Proxy”, if you will check your magento after installing it, you will not be able to found these proxy classes in magento2 code base, these proxy classes are generated automatically by magento2 code generator. These classes are genrated in two circumstances :
- when you will execute the magento cli command :
1php bin/magento setup:di:compile
when you will execute the above command, it will check all the di.xml files of all the modules installed in magento2 and if it encounters a class named Proxy, and it does not exist, then it will generate it automatically with same namespace inside ver/generation folder in the magento2 root . - In this case when a class is requested and it is a Proxy class and it is not present in the current code base magento will not throw an error or exception it will simply create that class inside var/generation folder and retun its object .
So you can see that magento2 generate these proxy classes on the fly with some fixed convention and replaces the original object with proxy, but we still don’t know how this proxy class is going to solve the problem, we can understand this by simply analysing a proxy class, lets have a look on the below proxy class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
<?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(); } } |
Lets understand the proxy class:
- the namespace of the proxy class is same as the original class “Magento\Catalog\Model\Product\Attribute\Source\Status” so that it can be easily replaced with the original object .
- Proxy class extends the original class “\Magento\Catalog\Model\Product\Attribute\Source\Status” so that it can use its public methods.
- Proxy classes implements “\Magento\Framework\ObjectManager\NoninterceptableInterface” interface, this is a marker interface menas it is blank, it has no methods and no member variables, it for only signaling that this class clannot be intercepted .
- And the most important its constructor, as we can see it only has one object dependency that ObjectManager class, so this makes sense as there is only one dependency in the class it will load very quickly and will save a lot of resources(space and time), you will not see a parent constructor call too .
- __sleep, __wakeup and __sleep these are the php magic functions that executes when a class is invoked or destroyed to perform some actions.
- _getSubject this method will return the original class object whenever it will be asked for, so it will simply work like a lazy loding process an object will be only provided when it will be requested.
- And all the other functions are parent(original) class public functions that are overriden in the proxy class so that they can be called on proxy class object, inside these functions it is calling the _getSubject to get the parent object and then call the same function .
So now I hope you understand how powerfull and easy technique is this to overcome a potentially huge problem .
There are some of the benifits of using proxy classes:
- Easy to use, you don’t need to do any thing just a simple entry in di.xml file will solve the problem.
- Performance of the application improves, since it saves a lot of time.
- No need to write test cases for the generated classes.
- Easy to understand the structure, since all the proxies will have the same structure .
Thats all in this blog, I hope it is easy to understood, but if you have any questions regarding this blog you can ask me in the comments below, I will surely try to answer your questions.
Thanks 🙂 .
10 comments
class Example extends Crazy\SimpleSolution\Model\Test\Proxy
if this possible ?? if not why
there is abc class and i need to call some function from xyz class.
Normal practice is xyz add in constructor and make a variable in constructor and use it. so xyz also will load while initialize abc object. but with the help of proxy xyz class will not load while initialize abc object and although we can use xyz class function in abc class if we define xyz class as proxy in di.xml. am i right?