Slide 1

Slide 1 text

Nette Framework How to compile an extensible DI Container @ProchazkaFilip

Slide 2

Slide 2 text

Co si povíme? - Ujasníme si pojmy - Koukneme jak funguje DI v Nette - Ukážeme si jak napsat rozšíření

Slide 3

Slide 3 text

Pojmy - Dependency Injection Container - služba - rozšíření - neon

Slide 4

Slide 4 text

Jak to funguje v Nette? Ale než postoupíme, je všechno jasné?

Slide 5

Slide 5 text

DI\Container class Container { function getParameters(); function addService($name, $service); function getService($name); function getByType($class, $need = TRUE);

Slide 6

Slide 6 text

Jak se to používá? $container = new Container(); $container->addService('app, new Application); $app = $container->getService('app');

Slide 7

Slide 7 text

To asi nebude ideální Vytvořit instance 500 tříd na každý request? To nechceš.

Slide 8

Slide 8 text

Řešením je kompilace DIC!

Slide 9

Slide 9 text

app/bootstrap.php $c = new Nette\Configurator; $c->setTempDirectory(__DIR__ . '/../temp'); $c->addConfig(__DIR__ . '/config/config.neon'); $c->addConfig(__DIR__ . '/config/config.local.neon'); $container = $configurator->createContainer();

Slide 10

Slide 10 text

new Nette\Configurator - vyřeší výchozí parametry

Slide 11

Slide 11 text

->setTempDirectory() - nastaví temp složku - nastaví parametr tempDir

Slide 12

Slide 12 text

->addConfig() - přidá neon/ini/php config

Slide 13

Slide 13 text

->createContainer()

Slide 14

Slide 14 text

->createContainer() Zkusí jestli už zkompilovaný container existuje - pokud ano, tak vytvoří instanci. - pokud ne, tak zkompiluje container a vytvoří instanci

Slide 15

Slide 15 text

Kompilace pohledem z dálky - nakonfiguruje se ContainerBuilder - Nette vygeneruje PHP třídu - uloží do tempu - profit

Slide 16

Slide 16 text

Co se vygeneruje? class SystemContainer extends Nette\DI\Container { protected $meta = array( 'types' => array(), 'services' => array(), 'tags' => array() ); public function __construct() { parent::__construct(array(/* parameters */)); }

Slide 17

Slide 17 text

Co se vygeneruje? class SystemContainer extends Nette\DI\Container { public function createServiceApplication() { $service = new Nette\Application\Application( $this->getService('nette.presenterFactory'), ... ); $service->catchExceptions = FALSE; $service->errorPresenter = 'Front:Error'; return $service; }

Slide 18

Slide 18 text

Jak se to používá? $app = $container ->getService('application'); use Nette\Application\Application; $app = $container ->getByType(Application::class);

Slide 19

Slide 19 text

Proces kompilace Ale než postoupíme, je všechno jasné?

Slide 20

Slide 20 text

Kompilace DI Containeru - načte configy a všechny je spojí do pole - expandne parametry parameters: imageDir: %wwwDir%/images

Slide 21

Slide 21 text

Kompilace DI Containeru - zavolá všechna rozšíření - Nette samo sebe konfiguruje rozšířenímy $ext->loadConfiguration()

Slide 22

Slide 22 text

Kompilace DI Containeru - projde služby z configu services: - App\UserManager

Slide 23

Slide 23 text

Kompilace DI Containeru - zavolá všechna rozšíření $ext->beforeCompile()

Slide 24

Slide 24 text

Kompilace DI Containeru - autowiring - vygeneruje PHP třídu SystemContainer

Slide 25

Slide 25 text

Kompilace DI Containeru - zavolá všechna rozšíření $ext->afterCompile($class)

Slide 26

Slide 26 text

Kompilace DI Containeru - uložit do tempu - profit

Slide 27

Slide 27 text

Jak fungují rozšíření Ale než postoupíme, je všechno jasné?

Slide 28

Slide 28 text

Každé rozšíření - pracuje s ContainerBuilder - dědí od Nette\DI\CompilerExtension - má “dynamické” jméno - má vlastní sekci v configu

Slide 29

Slide 29 text

ContainerBuilder class ContainerBuilder { /** @return ServiceDefinition */ function addDefinition($name, $definition = NULL); /** @return ServiceDefinition */ function getDefinition($name); /** @return ServiceDefinition[] */ function getDefinitions(); function removeDefinition($name);

Slide 30

Slide 30 text

ContainerBuilder class ContainerBuilder { /** @return string service name or NULL */ function getByType($class); /** @return string[] */ function findByType($class, $autowired = TRUE); /** @return array of [service name => tag attributes] */ function findByTag($tag);

Slide 31

Slide 31 text

ContainerBuilder class ContainerBuilder { public $parameters = array(); function expand($value); function addExcludedClasses(array $classes); function addDependency($file); /** @return Nette\PhpGenerator\PhpLiteral */ static function literal($phpCode);

Slide 32

Slide 32 text

ServiceDefinition class ServiceDefinition extends Nette\Object { public $class; public $factory; public $setup = array(); public $parameters = array(); public $tags = array(); public $autowired = TRUE; public $dynamic = FALSE; public $implement; public $implementType;

Slide 33

Slide 33 text

Registrace rozšíření extensions: doctrine: Kdyby\Doctrine\DI\OrmExtension doctrine: host: 127.0.0.1 user: root password: heslo

Slide 34

Slide 34 text

Extension points of extensions - loadConfiguration() - pouze na zpracování configu a přidávání služeb - beforeCompiler() - modifikace existujících služeb, nepřidávat nové! - afterCompile() - modifikace vygenerovaného kódu

Slide 35

Slide 35 text

Rozumné výchozí hodnoty public $defaults = array( 'catchExceptions' => "%productionMode%", );

Slide 36

Slide 36 text

loadConfiguration() public function loadConfiguration() { $builder = $this->getContainerBuilder(); $config = $this->getConfig($this->defaults); // ...

Slide 37

Slide 37 text

loadConfiguration() Validators::assertField($config, 'catchExceptions', 'bool'); $builder ->addDefinition($this->prefix('application')) ->setClass('Nette\Application\Application') ->addSetup('$catchExceptions', array($config['catchExceptions']));

Slide 38

Slide 38 text

beforeCompile() public function beforeCompile() { $builder = $this->getContainerBuilder(); // ...

Slide 39

Slide 39 text

beforeCompile() $manager = $builder->getDefinition($this->prefix('manager')); $services = $builder->findByTag(self::TAG_SUBSCRIBER); foreach ($services as $serviceName => $meta) { $manager->addSetup('addEventSubscriber', array('@' . $serviceName)); }

Slide 40

Slide 40 text

afterCompile($class) use Nette\PhpGenerator as Code; public function afterCompile(Code\ClassType $class) { $config = $this->getConfig(); $initialize = $class->methods['initialize']; $initialize->addBody('date_default_timezone_set(?);', array($config['date.timezone']));

Slide 41

Slide 41 text

Závislosti rozšíření - například BlogExtension, který potřebuje OrmExtension - vyřešilo se to změnou přístupu - dva přístupy - ask (starý) - tell (lepší)

Slide 42

Slide 42 text

Závislosti rozšíření - ask use Kdyby\Doctrine\DI\IDatabaseTypeProvider; $exts = $this->compiler ->getExtensions(IDatabaseTypeProvider::class); foreach ($exts as $ext) { $types = $ext->getDatabaseTypes(); // ...

Slide 43

Slide 43 text

Závislosti rozšíření - tell use Kdyby\Doctrine\DI\OrmExtension; $exts = $this->compiler ->getExtensions(OrmExtension::class); $exts[0]->addDatabaseType($metadata);

Slide 44

Slide 44 text

Dotazy?

Slide 45

Slide 45 text

Díky za pozornost! filip-prochazka.com Follow me maybe? @ProchazkaFilip