$30 off During Our Annual Pro Sale. View Details »

Nette framework - How to compile an extensible di container

Nette framework - How to compile an extensible di container

Everything you should know, before you start writing your own (DI compiler) extensions for Nette Framework.

Filip Procházka

December 21, 2015
Tweet

More Decks by Filip Procházka

Other Decks in Technology

Transcript

  1. Nette Framework How to compile an extensible DI Container @ProchazkaFilip

  2. Co si povíme? - Ujasníme si pojmy - Koukneme jak

    funguje DI v Nette - Ukážeme si jak napsat rozšíření
  3. Pojmy - Dependency Injection Container - služba - rozšíření -

    neon
  4. Jak to funguje v Nette? Ale než postoupíme, je všechno

    jasné?
  5. DI\Container class Container { function getParameters(); function addService($name, $service); function

    getService($name); function getByType($class, $need = TRUE);
  6. Jak se to používá? $container = new Container(); $container->addService('app, new

    Application); $app = $container->getService('app');
  7. To asi nebude ideální Vytvořit instance 500 tříd na každý

    request? To nechceš.
  8. Řešením je kompilace DIC!

  9. 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();
  10. new Nette\Configurator - vyřeší výchozí parametry

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

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

  13. ->createContainer()

  14. ->createContainer() Zkusí jestli už zkompilovaný container existuje - pokud ano,

    tak vytvoří instanci. - pokud ne, tak zkompiluje container a vytvoří instanci
  15. Kompilace pohledem z dálky - nakonfiguruje se ContainerBuilder - Nette

    vygeneruje PHP třídu - uloží do tempu - profit
  16. 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 */)); }
  17. 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; }
  18. Jak se to používá? $app = $container ->getService('application'); use Nette\Application\Application;

    $app = $container ->getByType(Application::class);
  19. Proces kompilace Ale než postoupíme, je všechno jasné?

  20. Kompilace DI Containeru - načte configy a všechny je spojí

    do pole - expandne parametry parameters: imageDir: %wwwDir%/images
  21. Kompilace DI Containeru - zavolá všechna rozšíření - Nette samo

    sebe konfiguruje rozšířenímy $ext->loadConfiguration()
  22. Kompilace DI Containeru - projde služby z configu services: -

    App\UserManager
  23. Kompilace DI Containeru - zavolá všechna rozšíření $ext->beforeCompile()

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

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

  26. Kompilace DI Containeru - uložit do tempu - profit

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

  28. Každé rozšíření - pracuje s ContainerBuilder - dědí od Nette\DI\CompilerExtension

    - má “dynamické” jméno - má vlastní sekci v configu
  29. ContainerBuilder class ContainerBuilder { /** @return ServiceDefinition */ function addDefinition($name,

    $definition = NULL); /** @return ServiceDefinition */ function getDefinition($name); /** @return ServiceDefinition[] */ function getDefinitions(); function removeDefinition($name);
  30. 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);
  31. ContainerBuilder class ContainerBuilder { public $parameters = array(); function expand($value);

    function addExcludedClasses(array $classes); function addDependency($file); /** @return Nette\PhpGenerator\PhpLiteral */ static function literal($phpCode);
  32. 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;
  33. Registrace rozšíření extensions: doctrine: Kdyby\Doctrine\DI\OrmExtension doctrine: host: 127.0.0.1 user: root

    password: heslo
  34. 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
  35. Rozumné výchozí hodnoty public $defaults = array( 'catchExceptions' => "%productionMode%",

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

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

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

  39. beforeCompile() $manager = $builder->getDefinition($this->prefix('manager')); $services = $builder->findByTag(self::TAG_SUBSCRIBER); foreach ($services as

    $serviceName => $meta) { $manager->addSetup('addEventSubscriber', array('@' . $serviceName)); }
  40. 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']));
  41. 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ší)
  42. Závislosti rozšíření - ask use Kdyby\Doctrine\DI\IDatabaseTypeProvider; $exts = $this->compiler ->getExtensions(IDatabaseTypeProvider::class);

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

    $exts[0]->addDatabaseType($metadata);
  44. Dotazy?

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