black magic of code generation in magento 2
TRANSCRIPT
#mm15de
@SergiiShymkoSenior Software EngineerMagento, an eBay Inc. company
Code Generationin Magento 2
#mm15de
Introduction to Code Generation
• Automatic programming – generation of computer program• Source code generation
– Generation based on template• Allows to write code at higher abstraction level• Enables aspect-oriented programming (AOP)• Enables generic programming – parameterization over types• Avoids writing boilerplate code
#mm15de#mm15de
Code Generation in Magento 1.x
#mm15de#mm15de
Code Generation in Magento 2
#mm15de
Code Generation in Magento 2
• Code is generated:– On the fly (development)
• Autoload non-existing class that follows naming pattern– Beforehand (production)
• Run CLI toolsphp dev/tools/Magento/Tools/Di/compiler.php
• Location of generated code:var/generation/
#mm15de
Factories
• Factory creates objects• Single method – create()• Used for non-injectables, i.e. entities• Isolation from Object Manager• Type safety• IDE auto-completion• Class name pattern:
\Namespace\ClassFactory
#mm15de
Factory Usage
namespace Magento\Catalog\Model\Product;
class Copier{ public function __construct( \Magento\Catalog\Model\ProductFactory $productFactory ) { $this->productFactory = $productFactory; }
public function copy(\Magento\Catalog\Model\Product $product) { $duplicate = $this->productFactory->create(); // ... }}
app/code/Magento/Catalog/Model/Product/Copier.php
#mm15de
Generated Factory (Simplified)
namespace Magento\Catalog\Model;
class ProductFactory{ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager ) { $this->objectManager = $objectManager; }
public function create(array $data = array()) { return $this->objectManager->create( '\\Magento\\Catalog\\Model\\Product', $data ); }}
var/generation/Magento/Catalog/Model/ProductFactory.php
#mm15de
Proxies
• Implementation of GoF pattern• Follows interface of subject• Delays creation of subject
– Delays creation of dependencies• Forwards calls to subject• Used for optional dependencies of DI• Class name pattern:
\Namespace\Class\Proxy
#mm15de
Proxy Usage in DI Config
<config> <type name="Magento\Catalog\Model\Resource\Product\Collection"> <arguments> <argument name="customerSession" xsi:type="object"> Magento\Customer\Model\Session\Proxy </argument> </arguments> </type></config>
app/code/Magento/Catalog/etc/di.xml
#mm15de
Generated Proxy (Simplified)
namespace Magento\Customer\Model\Session;
class Proxy extends \Magento\Customer\Model\Session{ protected function getSubject() { if (!$this->subject) { $this->subject = $this->objectManager->get( '\\Magento\\Customer\\Model\\Session' ); } return $this->subject; }
public function getCustomerId() { return $this->getSubject()->getCustomerId(); }
// ...}
var/generation/Magento/Customer/Model/Session/Proxy.php
#mm15de
Interception
• Primary customization approach• AOP-like mechanism• Used for plugins• Attach behavior to public methods
– Before– After– Around
• Plugins declared in DI config
#mm15de
Plugin Implementation
namespace Magento\Store\App\Action\Plugin;
class StoreCheck{ public function aroundDispatch( \Magento\Framework\App\Action\Action $subject, \Closure $proceed, \Magento\Framework\App\RequestInterface $request ) { if (!$this->storeManager->getStore()->getIsActive()) { throw new \Magento\Framework\App\InitException( 'Current store is not active.' ); } return $proceed($request); }}
app/code/Magento/Store/App/Action/Plugin/StoreCheck.php
#mm15de
Plugin Declaration in DI Config
<config> <type name="Magento\Framework\App\Action\Action"> <plugin name="storeCheck" type="Magento\Store\App\Action\Plugin\StoreCheck" sortOrder="10"/> </type></config>
app/code/Magento/Store/etc/di.xml
#mm15de
Generated Interceptor (Simplified)
namespace Magento\Framework\App\Action\Action;
class Interceptor extends \Magento\Framework\App\Action\Action{ public function dispatch( \Magento\Framework\App\RequestInterface $request ) { $pluginInfo = $this->pluginList->getNext( '\\Magento\\Framework\\App\\Action\\Action', 'dispatch' ); if (!$pluginInfo) { return parent::dispatch($request); } else { return $this->___callPlugins( 'dispatch', func_get_args(), $pluginInfo ); } }}
var/generation/Magento/Framework/App/Action/Action/Interceptor.php
#mm15de
Code Generation for Service Layer
• Service layer – ultimate public API• Services implement stateless operations• Generated code:
– Repository*– Persistor*– Search Results– Extension Attributes
* – may be removed in future releases
#mm15de
Generated Repository (Simplified)
namespace Magento\Sales\Api\Data\Order;
class Repository implements \Magento\Sales\Api\OrderRepositoryInterface{ public function __construct( \Magento\Sales\Api\Data\OrderInterfacePersistor $orderPersistor, \Magento\Sales\Api\Data\OrderSearchResultInterfaceFactory $searchResultFactory ) { $this->orderPersistor = $orderPersistor; $this->searchResultFactory = $searchResultFactory; }
public function get($id); public function create(\Magento\Sales\Api\Data\OrderInterface $entity); public function getList(\Magento\Framework\Api\SearchCriteria $criteria); public function remove(\Magento\Sales\Api\Data\OrderInterface $entity); public function flush();}
var/generation/Magento/Sales/Api/Data/Order/Repository.php
#mm15de
Extension Attributes
• Extension to data interfaces from 3rd party modules• Attributes declared in configuration• Attribute getters/setters generated• Type-safe attribute access• IDE auto-completion• Class name pattern:
\Namespace\ClassExtensionInterface\Namespace\ClassExtension
#mm15de
Declaration of Extension Attributes
<config> <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface"> <attribute code="price_type" type="integer" /> </custom_attributes></config>
app/code/Magento/Bundle/etc/data_object.xml
#mm15de
Entity with Extension Attributes
namespace Magento\Catalog\Api\Data;
interface ProductInterface extends \Magento\Framework\Api\CustomAttributesDataInterface{ /** * @return \Magento\Catalog\Api\Data\ProductExtensionInterface|null */ public function getExtensionAttributes();
public function setExtensionAttributes( \Magento\Catalog\Api\Data\ProductExtensionInterface $attributes );
// ...}
app/code/Magento/Catalog/Api/Data/ProductInterface.php
#mm15de
Generated Interface of Extension Attributes
namespace Magento\Catalog\Api\Data;
interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface{ /** * @return integer */ public function getPriceType();
/** * @param integer $priceType * @return $this */ public function setPriceType($priceType);
// ...}
var/generation/Magento/Catalog/Api/Data/ProductExtensionInterface.php
#mm15de
Generated Implementation of Extension Attributes
namespace Magento\Catalog\Api\Data;
class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements \Magento\Catalog\Api\Data\ProductExtensionInterface{ /** * @return integer */ public function getPriceType() { return $this->_get('price_type'); } /** * @param integer $priceType * @return $this */ public function setPriceType($priceType) { return $this->setData('price_type', $priceType); }
// ...}
var/generation/Magento/Catalog/Api/Data/ProductExtension.php
#mm15de
Loggers
• Implementation of GoF pattern Decorator• Activated with the profiler
– Object Manager wraps instances with loggers• Tracks method call stack• Forwards calls to original methods• Class name pattern:
\Namespace\Class\Logger
#mm15de
Summary of Code Generation
• DI– Factory– Proxy
• Interception• Service Layer
– Repository– Persistor– Search Results– Extension Attributes
• Logger