#mm15de
@SergiiShymko
Senior Software Engineer
Magento, an eBay Inc. company
Code Generation
in 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 tools
php 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:
NamespaceClassFactory
#mm15de
Factory Usage
namespace MagentoCatalogModelProduct;
class Copier
{
public function __construct(
MagentoCatalogModelProductFactory $productFactory
) {
$this->productFactory = $productFactory;
}
public function copy(MagentoCatalogModelProduct $product) {
$duplicate = $this->productFactory->create();
// ...
}
}
app/code/Magento/Catalog/Model/Product/Copier.php
#mm15de
Generated Factory (Simplified)
namespace MagentoCatalogModel;
class ProductFactory
{
public function __construct(
MagentoFrameworkObjectManagerInterface $objectManager
) {
$this->objectManager = $objectManager;
}
public function create(array $data = array()) {
return $this->objectManager->create(
'MagentoCatalogModelProduct',
$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:
NamespaceClassProxy
#mm15de
Proxy Usage in DI Config
<config>
<type name="MagentoCatalogModelResourceProductCollection">
<arguments>
<argument name="customerSession" xsi:type="object">
MagentoCustomerModelSessionProxy
</argument>
</arguments>
</type>
</config>
app/code/Magento/Catalog/etc/di.xml
#mm15de
Generated Proxy (Simplified)
namespace MagentoCustomerModelSession;
class Proxy extends MagentoCustomerModelSession
{
protected function getSubject() {
if (!$this->subject) {
$this->subject = $this->objectManager->get(
'MagentoCustomerModelSession'
);
}
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 MagentoStoreAppActionPlugin;
class StoreCheck
{
public function aroundDispatch(
MagentoFrameworkAppActionAction $subject,
Closure $proceed,
MagentoFrameworkAppRequestInterface $request
) {
if (!$this->storeManager->getStore()->getIsActive()) {
throw new MagentoFrameworkAppInitException(
'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="MagentoFrameworkAppActionAction">
<plugin name="storeCheck"
type="MagentoStoreAppActionPluginStoreCheck"
sortOrder="10"/>
</type>
</config>
app/code/Magento/Store/etc/di.xml
#mm15de
Generated Interceptor (Simplified)
namespace MagentoFrameworkAppActionAction;
class Interceptor extends MagentoFrameworkAppActionAction
{
public function dispatch(
MagentoFrameworkAppRequestInterface $request
) {
$pluginInfo = $this->pluginList->getNext(
'MagentoFrameworkAppActionAction', '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 MagentoSalesApiDataOrder;
class Repository implements MagentoSalesApiOrderRepositoryInterface
{
public function __construct(
MagentoSalesApiDataOrderInterfacePersistor $orderPersistor,
MagentoSalesApiDataOrderSearchResultInterfaceFactory $searchResultFactory
) {
$this->orderPersistor = $orderPersistor;
$this->searchResultFactory = $searchResultFactory;
}
public function get($id);
public function create(MagentoSalesApiDataOrderInterface $entity);
public function getList(MagentoFrameworkApiSearchCriteria $criteria);
public function remove(MagentoSalesApiDataOrderInterface $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:
NamespaceClassExtensionInterface
NamespaceClassExtension
#mm15de
Declaration of Extension Attributes
<config>
<custom_attributes for="MagentoCatalogApiDataProductInterface">
<attribute code="price_type" type="integer" />
</custom_attributes>
</config>
app/code/Magento/Bundle/etc/data_object.xml
#mm15de
Entity with Extension Attributes
namespace MagentoCatalogApiData;
interface ProductInterface
extends MagentoFrameworkApiCustomAttributesDataInterface
{
/**
* @return MagentoCatalogApiDataProductExtensionInterface|null
*/
public function getExtensionAttributes();
public function setExtensionAttributes(
MagentoCatalogApiDataProductExtensionInterface $attributes
);
// ...
}
app/code/Magento/Catalog/Api/Data/ProductInterface.php
#mm15de
Generated Interface of Extension Attributes
namespace MagentoCatalogApiData;
interface ProductExtensionInterface
extends MagentoFrameworkApiExtensionAttributesInterface
{
/**
* @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 MagentoCatalogApiData;
class ProductExtension
extends MagentoFrameworkApiAbstractSimpleObject
implements MagentoCatalogApiDataProductExtensionInterface
{
/**
* @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:
NamespaceClassLogger
#mm15de
Summary of Code Generation
• DI
– Factory
– Proxy
• Interception
• Service Layer
– Repository
– Persistor
– Search Results
– Extension Attributes
• Logger
#mm15de
Thank You!
Q & A
@SergiiShymko
sshymko@ebay.com
sergey@shymko.net

Black Magic of Code Generation in Magento 2

  • 1.
    #mm15de @SergiiShymko Senior Software Engineer Magento,an eBay Inc. company Code Generation in Magento 2
  • 2.
    #mm15de Introduction to CodeGeneration • 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
  • 3.
  • 4.
  • 5.
    #mm15de Code Generation inMagento 2 • Code is generated: – On the fly (development) • Autoload non-existing class that follows naming pattern – Beforehand (production) • Run CLI tools php dev/tools/Magento/Tools/Di/compiler.php • Location of generated code: var/generation/
  • 6.
    #mm15de Factories • Factory createsobjects • Single method – create() • Used for non-injectables, i.e. entities • Isolation from Object Manager • Type safety • IDE auto-completion • Class name pattern: NamespaceClassFactory
  • 7.
    #mm15de Factory Usage namespace MagentoCatalogModelProduct; classCopier { public function __construct( MagentoCatalogModelProductFactory $productFactory ) { $this->productFactory = $productFactory; } public function copy(MagentoCatalogModelProduct $product) { $duplicate = $this->productFactory->create(); // ... } } app/code/Magento/Catalog/Model/Product/Copier.php
  • 8.
    #mm15de Generated Factory (Simplified) namespaceMagentoCatalogModel; class ProductFactory { public function __construct( MagentoFrameworkObjectManagerInterface $objectManager ) { $this->objectManager = $objectManager; } public function create(array $data = array()) { return $this->objectManager->create( 'MagentoCatalogModelProduct', $data ); } } var/generation/Magento/Catalog/Model/ProductFactory.php
  • 9.
    #mm15de Proxies • Implementation ofGoF 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: NamespaceClassProxy
  • 10.
    #mm15de Proxy Usage inDI Config <config> <type name="MagentoCatalogModelResourceProductCollection"> <arguments> <argument name="customerSession" xsi:type="object"> MagentoCustomerModelSessionProxy </argument> </arguments> </type> </config> app/code/Magento/Catalog/etc/di.xml
  • 11.
    #mm15de Generated Proxy (Simplified) namespaceMagentoCustomerModelSession; class Proxy extends MagentoCustomerModelSession { protected function getSubject() { if (!$this->subject) { $this->subject = $this->objectManager->get( 'MagentoCustomerModelSession' ); } return $this->subject; } public function getCustomerId() { return $this->getSubject()->getCustomerId(); } // ... } var/generation/Magento/Customer/Model/Session/Proxy.php
  • 12.
    #mm15de Interception • Primary customizationapproach • AOP-like mechanism • Used for plugins • Attach behavior to public methods – Before – After – Around • Plugins declared in DI config
  • 13.
    #mm15de Plugin Implementation namespace MagentoStoreAppActionPlugin; classStoreCheck { public function aroundDispatch( MagentoFrameworkAppActionAction $subject, Closure $proceed, MagentoFrameworkAppRequestInterface $request ) { if (!$this->storeManager->getStore()->getIsActive()) { throw new MagentoFrameworkAppInitException( 'Current store is not active.' ); } return $proceed($request); } } app/code/Magento/Store/App/Action/Plugin/StoreCheck.php
  • 14.
    #mm15de Plugin Declaration inDI Config <config> <type name="MagentoFrameworkAppActionAction"> <plugin name="storeCheck" type="MagentoStoreAppActionPluginStoreCheck" sortOrder="10"/> </type> </config> app/code/Magento/Store/etc/di.xml
  • 15.
    #mm15de Generated Interceptor (Simplified) namespaceMagentoFrameworkAppActionAction; class Interceptor extends MagentoFrameworkAppActionAction { public function dispatch( MagentoFrameworkAppRequestInterface $request ) { $pluginInfo = $this->pluginList->getNext( 'MagentoFrameworkAppActionAction', '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
  • 16.
    #mm15de Code Generation forService Layer • Service layer – ultimate public API • Services implement stateless operations • Generated code: – Repository* – Persistor* – Search Results – Extension Attributes * – may be removed in future releases
  • 17.
    #mm15de Generated Repository (Simplified) namespaceMagentoSalesApiDataOrder; class Repository implements MagentoSalesApiOrderRepositoryInterface { public function __construct( MagentoSalesApiDataOrderInterfacePersistor $orderPersistor, MagentoSalesApiDataOrderSearchResultInterfaceFactory $searchResultFactory ) { $this->orderPersistor = $orderPersistor; $this->searchResultFactory = $searchResultFactory; } public function get($id); public function create(MagentoSalesApiDataOrderInterface $entity); public function getList(MagentoFrameworkApiSearchCriteria $criteria); public function remove(MagentoSalesApiDataOrderInterface $entity); public function flush(); } var/generation/Magento/Sales/Api/Data/Order/Repository.php
  • 18.
    #mm15de Extension Attributes • Extensionto data interfaces from 3rd party modules • Attributes declared in configuration • Attribute getters/setters generated • Type-safe attribute access • IDE auto-completion • Class name pattern: NamespaceClassExtensionInterface NamespaceClassExtension
  • 19.
    #mm15de Declaration of ExtensionAttributes <config> <custom_attributes for="MagentoCatalogApiDataProductInterface"> <attribute code="price_type" type="integer" /> </custom_attributes> </config> app/code/Magento/Bundle/etc/data_object.xml
  • 20.
    #mm15de Entity with ExtensionAttributes namespace MagentoCatalogApiData; interface ProductInterface extends MagentoFrameworkApiCustomAttributesDataInterface { /** * @return MagentoCatalogApiDataProductExtensionInterface|null */ public function getExtensionAttributes(); public function setExtensionAttributes( MagentoCatalogApiDataProductExtensionInterface $attributes ); // ... } app/code/Magento/Catalog/Api/Data/ProductInterface.php
  • 21.
    #mm15de Generated Interface ofExtension Attributes namespace MagentoCatalogApiData; interface ProductExtensionInterface extends MagentoFrameworkApiExtensionAttributesInterface { /** * @return integer */ public function getPriceType(); /** * @param integer $priceType * @return $this */ public function setPriceType($priceType); // ... } var/generation/Magento/Catalog/Api/Data/ProductExtensionInterface.php
  • 22.
    #mm15de Generated Implementation ofExtension Attributes namespace MagentoCatalogApiData; class ProductExtension extends MagentoFrameworkApiAbstractSimpleObject implements MagentoCatalogApiDataProductExtensionInterface { /** * @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
  • 23.
    #mm15de Loggers • Implementation ofGoF pattern Decorator • Activated with the profiler – Object Manager wraps instances with loggers • Tracks method call stack • Forwards calls to original methods • Class name pattern: NamespaceClassLogger
  • 24.
    #mm15de Summary of CodeGeneration • DI – Factory – Proxy • Interception • Service Layer – Repository – Persistor – Search Results – Extension Attributes • Logger
  • 25.
    #mm15de Thank You! Q &A @SergiiShymko sshymko@ebay.com sergey@shymko.net

Editor's Notes

  • #3 Automatic programming – some mechanism generates a computer program AOP – separation of cross-cutting concerns Generic programming – style of computer programming in which algorithms are written in terms of types to-be-specified-later Generics in Java, C#, Delphi, Ada Templates in C++
  • #5 Magento 2 employs code generation for a number of core mechanisms
  • #6 Application modes: default, developer, production In addition to what “compiler.php” does “singletenant_compiler.php” also generates caches
  • #7 GoF creation pattern
  • #9 Code has been simplified
  • #12 Code has been simplified
  • #13 APO – Aspect-Oriented Programming
  • #16 Code has been simplified
  • #17 Repository – pattern of Domain-Driven Design Repository – imitates in-memory entity collection Persistor – persists entities in Repository Search Results – subset of entities in Repository Extension Attributes – extension to data interfaces
  • #18 Method create($entity) adds entity to repository
  • #19 Explicit, safe, type-safe attribute access Replacement for free-format Varien_Object