MAGENTO 2
LAYOUT AND CODE COMPILATION
FOR PERFORMANCE
 
by Ivan Chepurnyi
WHAT? COMPILATION?
COMPLEX ALGORITHMSSIMPLE
WHAT MAKES THEM COMPLEX?
REPEATED DATA PROCESSING
// ... some xml/json/yaml file initialization
foreach ($loadedData as $item) {
$this->process($item);
}
NESTED LOOPS
foreach ($data as $item) {
$row = [];
foreach ($columns as $column) {
$row[] = $column->export($item);
}
$csv->write($row);
}
COMPLEX DEPENDENCY TREE
class ClassOne
{
public function __construct(ClassTwo $dependency) {}
}
class ClassTwo
{
public function __construct(ClassThree $dependency) {}
}
class ClassThree
{
public function __construct(ClassFour $dependencyOne, ClassFive $dependen
}
// ..
HOW CAN WE SOLVE IT?
REPEATED DATA PROCESSING
Translate your XML/JSON/YAML file into executable PHP
code and include it when you need processed structure
NESTED LOOPS
Pre-compile second loop and execute it within the main one
COMPLEX DEPENDENCY TREE
Resolve dependencies and compile resolution into
executable code
BUT COMPILATION LOOKS UGLY...
You need to create PHP code within PHP code
You need to write it to external file
You need to include that file inside of your code
I WAS LOOKING FOR A LIBRARY
Didn't find one... So I wrote it myself.
ECOMDEVCOMPILER
Created to wrap writing PHP code within PHP
Automatically stores compiled code
Automatically validates source and re-compiles code
when needed
Provides easy to use API to create parsers, builders and
executors
INSTALLATION
1. Available as a composer dependency
2. Instantiate it directly or via DI container.
composer require "ecomdev/compiler"
SOME EXAMPLES
COMPILE XML INTO PHP
XML FILE
<objects>
<item id="object_one" type="object" />
<item id="object_two" type="object" />
<item id="object_three" type="object" />
<type id="object" class="SomeClassName"/>
</objects>
PARSER
use EcomDevCompilerStatementBuilder;
class Parser implements EcomDevCompilerParserInterface
{
// .. constructor with builder as dependency
public function parse($value)
{
$xml = simplexml_load_string($value);
$info = $this->readXml($xml);
return $this->getPhpCode($info, $this->builder);
}
// .. other methods
}
PARSE XML DATA
private function readXml($xml)
{
$info = [];
foreach ($xml->children() as $node) {
if ($node->getName() === 'type') {
$info['types'][(string)$node->id] = (string)$node->class;
} elseif ($node->getName() === 'object') {
$info['objects'][(string)$node->id] = (string)$node->type;
}
}
return $info;
}
CREATE PHP CODE
private function getPhpCode($info, $builder) {
$compiledArray = [];
foreach ($info['objects'] as $objectId => $type) {
$compiledArray[$objectId] = $builder->instance($info['types'][
}
return $builder->container(
$builder->returnValue($compiledArray)
);
}
COMPILED PHP FILE
return [
'object_one' => new SomeClassName(),
'object_two' => new SomeClassName(),
'object_three' => new SomeClassName()
];
NESTED LOOP SIMPLIFYING
YOUR CONSTRUCTOR
public function __construct(
EcomDevCompilerBuilder $builder,
EcomDevCompilerCompiler $compiler)
{
$this->builder = $builder;
$this->compiler = $compiler;
}
EXPORT METHOD
public function export($data, $columns)
{
$statements = $this->compileColumns($columns, $this->builder);
$source = new EcomDevCompilerSourceStaticData(
'your_id', 'your_checksum', $statements
);
$reference = $this->compiler->compile($source);
$closure = $this->compiler->interpret($reference);
foreach ($data as $item) {
$row = $closure($item, $columns);
}
}
COMPILATION METHOD
public function compileColumns($columns, $builder)
{
$item = $builder->variable('item');
$compiledArray = [];
foreach ($columns as $id => $column) {
$compiledArray[] = $builder->chainVariable('columns')[$id]
->export($item);
}
$closure = $builder->closure(
[$item, $builder->variable('columns')],
$builder->container([$builder->returnValue($compiledArray)])
);
return $builder->container([$builder->returnValue($closure)]);
}
RESULT
return function ($item, $columns) {
return [
$columns['id1']->export($item),
$columns['id2']->export($item),
$columns['id3']->export($item),
// ...
];
};
MAIN COMPONENTS
CompilerInterface - Compiler instance
StorageInterface - Stores compiled files
SourceInterface - Provider of data (File, String,
StaticData)
ParserInterface - Parser of data
ObjectBuilderInterface - Bound builder for included files
AND SOME SWEET STUFF...
EXPORTABLE OBJECTS
class SomeClass implements EcomDevCompilerExportableInterface
{
public function __construct($foo, $bar) { /* */ }
public function export() {
return [
'foo' => $this->foo,
'bar' => $this->bar
];
}
}
Will be automatically compiled into:
new SomeClass('fooValue', 'barValue');
MAGENTO 2.0
LAYOUT COMPILATION IS A MUST
WHY? BECAUSE OF ITS ALGORITHM
LAYOUT CACHING
Every handle that is added to the
MagentoFrameworkViewResult changes the cache key
for the whole generated structure.
LAYOUT GENERATION
Scheduled structure is generated from XML object at all
times
SOLUTION
1. Make every handle a compiled php code
2. Include compiled handles at loading phase
GOOD NEWS
I am already working on it
Will be release in April 2016
GITHUB
COMPILER LIBRARY FOR M2
https://github.com/EcomDev/compiler
LAYOUT COMPILER FOR M1
https://github.com/EcomDev/EcomDev_LayoutCompiler
LAYOUT COMPILER FOR M2
Coming soon
Q&A
@IvanChepurnyi
ivan@ecomdev.org

Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance

  • 1.
    MAGENTO 2 LAYOUT ANDCODE COMPILATION FOR PERFORMANCE   by Ivan Chepurnyi
  • 2.
  • 3.
  • 4.
  • 5.
    REPEATED DATA PROCESSING //... some xml/json/yaml file initialization foreach ($loadedData as $item) { $this->process($item); }
  • 6.
    NESTED LOOPS foreach ($dataas $item) { $row = []; foreach ($columns as $column) { $row[] = $column->export($item); } $csv->write($row); }
  • 7.
    COMPLEX DEPENDENCY TREE classClassOne { public function __construct(ClassTwo $dependency) {} } class ClassTwo { public function __construct(ClassThree $dependency) {} } class ClassThree { public function __construct(ClassFour $dependencyOne, ClassFive $dependen } // ..
  • 8.
    HOW CAN WESOLVE IT?
  • 9.
    REPEATED DATA PROCESSING Translateyour XML/JSON/YAML file into executable PHP code and include it when you need processed structure
  • 10.
    NESTED LOOPS Pre-compile secondloop and execute it within the main one
  • 11.
    COMPLEX DEPENDENCY TREE Resolvedependencies and compile resolution into executable code
  • 12.
    BUT COMPILATION LOOKSUGLY... You need to create PHP code within PHP code You need to write it to external file You need to include that file inside of your code
  • 13.
    I WAS LOOKINGFOR A LIBRARY Didn't find one... So I wrote it myself.
  • 14.
    ECOMDEVCOMPILER Created to wrapwriting PHP code within PHP Automatically stores compiled code Automatically validates source and re-compiles code when needed Provides easy to use API to create parsers, builders and executors
  • 15.
    INSTALLATION 1. Available asa composer dependency 2. Instantiate it directly or via DI container. composer require "ecomdev/compiler"
  • 16.
  • 17.
  • 18.
    XML FILE <objects> <item id="object_one"type="object" /> <item id="object_two" type="object" /> <item id="object_three" type="object" /> <type id="object" class="SomeClassName"/> </objects>
  • 19.
    PARSER use EcomDevCompilerStatementBuilder; class Parserimplements EcomDevCompilerParserInterface { // .. constructor with builder as dependency public function parse($value) { $xml = simplexml_load_string($value); $info = $this->readXml($xml); return $this->getPhpCode($info, $this->builder); } // .. other methods }
  • 20.
    PARSE XML DATA privatefunction readXml($xml) { $info = []; foreach ($xml->children() as $node) { if ($node->getName() === 'type') { $info['types'][(string)$node->id] = (string)$node->class; } elseif ($node->getName() === 'object') { $info['objects'][(string)$node->id] = (string)$node->type; } } return $info; }
  • 21.
    CREATE PHP CODE privatefunction getPhpCode($info, $builder) { $compiledArray = []; foreach ($info['objects'] as $objectId => $type) { $compiledArray[$objectId] = $builder->instance($info['types'][ } return $builder->container( $builder->returnValue($compiledArray) ); }
  • 22.
    COMPILED PHP FILE return[ 'object_one' => new SomeClassName(), 'object_two' => new SomeClassName(), 'object_three' => new SomeClassName() ];
  • 23.
  • 24.
    YOUR CONSTRUCTOR public function__construct( EcomDevCompilerBuilder $builder, EcomDevCompilerCompiler $compiler) { $this->builder = $builder; $this->compiler = $compiler; }
  • 25.
    EXPORT METHOD public functionexport($data, $columns) { $statements = $this->compileColumns($columns, $this->builder); $source = new EcomDevCompilerSourceStaticData( 'your_id', 'your_checksum', $statements ); $reference = $this->compiler->compile($source); $closure = $this->compiler->interpret($reference); foreach ($data as $item) { $row = $closure($item, $columns); } }
  • 26.
    COMPILATION METHOD public functioncompileColumns($columns, $builder) { $item = $builder->variable('item'); $compiledArray = []; foreach ($columns as $id => $column) { $compiledArray[] = $builder->chainVariable('columns')[$id] ->export($item); } $closure = $builder->closure( [$item, $builder->variable('columns')], $builder->container([$builder->returnValue($compiledArray)]) ); return $builder->container([$builder->returnValue($closure)]); }
  • 27.
    RESULT return function ($item,$columns) { return [ $columns['id1']->export($item), $columns['id2']->export($item), $columns['id3']->export($item), // ... ]; };
  • 28.
    MAIN COMPONENTS CompilerInterface -Compiler instance StorageInterface - Stores compiled files SourceInterface - Provider of data (File, String, StaticData) ParserInterface - Parser of data ObjectBuilderInterface - Bound builder for included files
  • 29.
  • 30.
    EXPORTABLE OBJECTS class SomeClassimplements EcomDevCompilerExportableInterface { public function __construct($foo, $bar) { /* */ } public function export() { return [ 'foo' => $this->foo, 'bar' => $this->bar ]; } } Will be automatically compiled into: new SomeClass('fooValue', 'barValue');
  • 31.
  • 32.
    WHY? BECAUSE OFITS ALGORITHM
  • 33.
    LAYOUT CACHING Every handlethat is added to the MagentoFrameworkViewResult changes the cache key for the whole generated structure.
  • 34.
    LAYOUT GENERATION Scheduled structureis generated from XML object at all times
  • 35.
    SOLUTION 1. Make everyhandle a compiled php code 2. Include compiled handles at loading phase
  • 36.
    GOOD NEWS I amalready working on it Will be release in April 2016
  • 37.
    GITHUB COMPILER LIBRARY FORM2 https://github.com/EcomDev/compiler LAYOUT COMPILER FOR M1 https://github.com/EcomDev/EcomDev_LayoutCompiler LAYOUT COMPILER FOR M2 Coming soon
  • 38.