38
MAGENTO 2 LAYOUT AND CODE COMPILATION FOR PERFORMANCE by Ivan Chepurnyi

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

Embed Size (px)

Citation preview

MAGENTO 2LAYOUT 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 LOOPSforeach ($data as $item) { $row = []; foreach ($columns as $column) { $row[] = $column->export($item); } $csv->write($row); }

COMPLEX DEPENDENCY TREEclass ClassOne { public function __construct(ClassTwo $dependency) {} }

class ClassTwo { public function __construct(ClassThree $dependency) {} }

class ClassThree { public function __construct(ClassFour $dependencyOne, ClassFive $dependencyTwo}

// ..

HOW CAN WE SOLVE IT?

REPEATED DATA PROCESSINGTranslate your XML/JSON/YAML file into executable PHPcode and include it when you need processed structure

NESTED LOOPSPre-compile second loop and execute it within the main one

COMPLEX DEPENDENCY TREEResolve dependencies and compile resolution into

executable code

BUT COMPILATION LOOKS UGLY...You need to create PHP code within PHP codeYou need to write it to external fileYou need to include that file inside of your code

I WAS LOOKING FOR A LIBRARYDidn't find one... So I wrote it myself.

ECOMDEV\COMPILERCreated to wrap writing PHP code within PHPAutomatically stores compiled codeAutomatically validates source and re-compiles codewhen neededProvides easy to use API to create parsers, builders andexecutors

INSTALLATION1. 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="Some\ClassName"/> </objects>

PARSERuse EcomDev\Compiler\Statement\Builder;

class Parser implements EcomDev\Compiler\ParserInterface { // .. 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 DATAprivate 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 CODEprivate 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 FILEreturn [ 'object_one' => new Some\ClassName(), 'object_two' => new Some\ClassName(), 'object_three' => new Some\ClassName() ];

NESTED LOOP SIMPLIFYING

YOUR CONSTRUCTORpublic function __construct( EcomDev\Compiler\Builder $builder, EcomDev\Compiler\Compiler $compiler) { $this->builder = $builder; $this->compiler = $compiler; }

EXPORT METHODpublic function export($data, $columns) { $statements = $this->compileColumns($columns, $this->builder);

$source = new \EcomDev\Compiler\Source\StaticData( 'your_id', 'your_checksum', $statements );

$reference = $this->compiler->compile($source); $closure = $this->compiler->interpret($reference);

foreach ($data as $item) { $row = $closure($item, $columns); } }

COMPILATION METHODpublic 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)]); }

RESULTreturn function ($item, $columns) { return [ $columns['id1']->export($item), $columns['id2']->export($item), $columns['id3']->export($item), // ... ]; };

MAIN COMPONENTSCompilerInterface - Compiler instanceStorageInterface - Stores compiled filesSourceInterface - Provider of data (File, String,StaticData)ParserInterface - Parser of dataObjectBuilderInterface - Bound builder for included files

AND SOME SWEET STUFF...

EXPORTABLE OBJECTSclass SomeClass implements EcomDev\Compiler\ExportableInterface { 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.0LAYOUT COMPILATION IS A MUST

WHY? BECAUSE OF ITS ALGORITHM

LAYOUT CACHINGEvery handle that is added to the

Magento\Framework\View\Result changes the cache keyfor the whole generated structure.

LAYOUT GENERATIONScheduled structure is generated from XML object at all

times

SOLUTION1. Make every handle a compiled php code

2. Include compiled handles at loading phase

GOOD NEWSI am already working on it

Will be release in April 2016

GITHUBCOMPILER 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

[email protected]