Date post: | 20-Mar-2017 |
Category: |
Software |
Upload: | filip-prochazka |
View: | 417 times |
Download: | 1 times |
Nette FrameworkHow to compile an extensible DI Container
@ProchazkaFilip
Co si povíme?
- Ujasníme si pojmy- Koukneme jak funguje DI v Nette- Ukážeme si jak napsat rozšíření
Pojmy
- Dependency Injection Container- služba- rozšíření- neon
Jak to funguje v Nette?Ale než postoupíme,
je všechno jasné?
DI\Containerclass Container
{
function getParameters();
function addService($name, $service);
function getService($name);
function getByType($class, $need = TRUE);
Jak se to používá?
$container = new Container();
$container->addService('app, new Application);
$app = $container->getService('app');
To asi nebude ideální
Vytvořit instance 500 tříd na každý request?To nechceš.
Řešenímje kompilace DIC!
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();
new Nette\Configurator
- vyřeší výchozí parametry
->setTempDirectory()
- nastaví temp složku- nastaví parametr tempDir
->addConfig()
- přidá neon/ini/php config
->createContainer()
->createContainer()
Zkusí jestli už zkompilovaný container existuje
- pokud ano, tak vytvoří instanci.- pokud ne, tak zkompiluje container
a vytvoří instanci
Kompilace pohledem z dálky
- nakonfiguruje se ContainerBuilder- Nette vygeneruje PHP třídu- uloží do tempu- profit
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 */));
}
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;
}
Jak se to používá?$app = $container
->getService('application');
use Nette\Application\Application;
$app = $container
->getByType(Application::class);
Proces kompilaceAle než postoupíme,
je všechno jasné?
Kompilace DI Containeru
- načte configy a všechny je spojí do pole- expandne parametry
parameters:
imageDir: %wwwDir%/images
Kompilace DI Containeru
- zavolá všechna rozšíření- Nette samo sebe konfiguruje rozšířenímy
$ext->loadConfiguration()
Kompilace DI Containeru
- projde služby z configu
services:
- App\UserManager
Kompilace DI Containeru
- zavolá všechna rozšíření
$ext->beforeCompile()
Kompilace DI Containeru
- autowiring- vygeneruje PHP třídu SystemContainer
Kompilace DI Containeru
- zavolá všechna rozšíření
$ext->afterCompile($class)
Kompilace DI Containeru
- uložit do tempu- profit
Jak fungují rozšířeníAle než postoupíme,
je všechno jasné?
Každé rozšíření
- pracuje s ContainerBuilder- dědí od Nette\DI\CompilerExtension- má “dynamické” jméno- má vlastní sekci v configu
ContainerBuilderclass ContainerBuilder
{
/** @return ServiceDefinition */
function addDefinition($name, $definition = NULL);
/** @return ServiceDefinition */
function getDefinition($name);
/** @return ServiceDefinition[] */
function getDefinitions();
function removeDefinition($name);
ContainerBuilderclass 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);
ContainerBuilderclass ContainerBuilder
{
public $parameters = array();
function expand($value);
function addExcludedClasses(array $classes);
function addDependency($file);
/** @return Nette\PhpGenerator\PhpLiteral */
static function literal($phpCode);
ServiceDefinitionclass 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;
Registrace rozšířeníextensions:
doctrine: Kdyby\Doctrine\DI\OrmExtension
doctrine:
host: 127.0.0.1
user: root
password: heslo
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
Rozumné výchozí hodnoty
public $defaults = array(
'catchExceptions' => "%productionMode%",
);
loadConfiguration()
public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
$config = $this->getConfig($this->defaults);
// ...
loadConfiguration()
Validators::assertField($config,
'catchExceptions', 'bool');
$builder
->addDefinition($this->prefix('application'))
->setClass('Nette\Application\Application')
->addSetup('$catchExceptions',
array($config['catchExceptions']));
beforeCompile()
public function beforeCompile()
{
$builder = $this->getContainerBuilder();
// ...
beforeCompile()
$manager = $builder->getDefinition($this->prefix('manager'));
$services = $builder->findByTag(self::TAG_SUBSCRIBER);
foreach ($services as $serviceName => $meta) {
$manager->addSetup('addEventSubscriber',
array('@' . $serviceName));
}
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']));
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ší)
Závislosti rozšíření - askuse Kdyby\Doctrine\DI\IDatabaseTypeProvider;
$exts = $this->compiler
->getExtensions(IDatabaseTypeProvider::class);
foreach ($exts as $ext) {
$types = $ext->getDatabaseTypes();
// ...
Závislosti rozšíření - telluse Kdyby\Doctrine\DI\OrmExtension;
$exts = $this->compiler
->getExtensions(OrmExtension::class);
$exts[0]->addDatabaseType($metadata);
Dotazy?
Díky za pozornost!filip-prochazka.com
Follow me maybe? @ProchazkaFilip