(Meine) Wahrheit über Symfony
Timon!= Timon
Schulung, Coaching, Beratung
Version 4.2 (2002) OOP? register_globals?
http://doitandhow.com/2012/07/16/fun-colored-spaghetti/
OOP
C++
C++ HPC
C++ fortran HPC
C++ fortran HPC Matlab
professionell entwickeln rumbasteln
TDD
TDD unit
TDD unit OOP + DI
Version 2.0 (2011)
professionell entwickeln rumbasteln
Forms Validation TWIG uvm.
Forms Validation TWIG Dependency Injection Container uvm. Event Dispatcher
Was sind die Kennzeichen dieser Paradigmen? Imperativ Prozedural Objektorientiert 23
Was sind die Kennzeichen dieser Paradigmen? Imperativ Sequenz, Schleife, Verzweigung 24
Was sind die Kennzeichen dieser Paradigmen? Imperativ Sequenz, Schleife, Verzweigung Prozedural Funktionen die alle global verfügbar sind 25
http://doitandhow.com/2012/07/16/fun-colored-spaghetti/
Was sind die Kennzeichen dieser Paradigmen? Imperativ Prozedural Sequenz, Schleife, Verzweigung Funktionen die alle global verfügbar sind Objektorientiert Objekte, Eigenschaften, Methoden usw. 27
Was sind die Kennzeichen dieser Paradigmen? Imperativ Prozedural Sequenz, Schleife, Verzweigung Funktionen die alle global verfügbar sind Objektorientiert Objekte, Eigenschaften, Methoden usw. Funktionen in abgegrenzten Kontexten verfügbar 28
http://www.spieltischshop.de/lego-soft.html
Was sind die Kennzeichen dieser Paradigmen? Imperativ Prozedural Sequenz, Schleife, Verzweigung Funktionen die alle global verfügbar sind Objektorientiert Objekte, Eigenschaften, Methoden usw. Funktionen in abgegrenzten Kontexten verfügbar 30
Was sind die Kennzeichen dieser Paradigmen? Imperativ Prozedural Sequenz, Schleife, Verzweigung Funktionen die alle global verfügbar sind Objektorientiert Objekte, Eigenschaften, Methoden usw. Funktionen in abgegrenzten Kontexten verfügbar feste Abhängigkeiten zwischen Klassen 31
Was sind die Kennzeichen dieser Paradigmen? Imperativ Prozedural Sequenz, Schleife, Verzweigung Funktionen die alle global verfügbar sind Objektorientiert Objekte, Eigenschaften, Methoden usw. Funktionen in abgegrenzten Kontexten verfügbar feste Abhängigkeiten zwischen Klassen OOP mit Dependency Injection 32 Instanzen variabel kombinierbar (Schraubendreher)
33
wiederverwendbar unit-testbar Großteil der Codebasis 34
nicht wiederverwendbar nicht unit-testbar Minimum an Code wiederverwendbar unit-testbar Großteil der Codebasis 35
Dependency Injection 36
DI Dependency Injection 37
Structure of the next slides Code example: DI for generic PHP classes Code example: DI in Symfony 2 38 You are here
<?php namespace Acme\FeedBundle\Service\FeedAggregator Why is this class use Guzzle\Http\Client; difficult to unit test? use Acme\Logger\XmlLogger; class FeedAggregator { private $client; private $logger; public function construct () { $this->client = new Client(); $this->logger = new XmlLogger(); public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 39
<?php namespace Acme\FeedBundle\Service\FeedAggregator Why is this class use Guzzle\Http\Client; difficult to unit test? use Acme\Logger\XmlLogger; class FeedAggregator { private $client; private $logger; public function construct () { What if we want unit tests to run fast without waiting for the network? $this->client = new Client(); $this->logger = new XmlLogger(); public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 40
<?php namespace Acme\FeedBundle\Service\FeedAggregator What if we Why is this class use Guzzle\Http\Client; ever want to difficult to unit test? use Acme\Logger\XmlLogger; class FeedAggregator { private $client; private $logger; use a different HTTP client? public function construct () { What if we want unit tests to run fast without waiting for the network? $this->client = new Client(); $this->logger = new XmlLogger(); public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 41
<?php namespace Acme\FeedBundle\Service\FeedAggregator What if we Why is this class use Guzzle\Http\Client; ever want to difficult to unit test? use Acme\Logger\XmlLogger; class FeedAggregator { What if we $client; private private ever want to$logger; usepublic a different function logger class? use a different HTTP client? construct () { What if we want unit tests to run fast without waiting for the network? $this->client = new Client(); $this->logger = new XmlLogger(); public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 42
<?php namespace Acme\FeedBundle\Service\FeedAggregator What if we Why is this class use Guzzle\Http\Client; ever want to difficult to unit test? use Acme\Logger\XmlLogger; class FeedAggregator { What if we What if we $client; private private ever want to ever want to$logger; use a different HTTP client? use a construct different usepublic a different function log format? logger class? () { What if we want unit tests to run fast without waiting for the network? $this->client = new Client(); $this->logger = new XmlLogger(); public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 43
<?php namespace Acme\FeedBundle\Service\FeedAggregator What if we Why is this class use Guzzle\Http\Client; ever want to difficult to unit test? use Acme\Logger\XmlLogger; class FeedAggregator { What if we What if we $client; private private ever want to ever want to$logger; use a different HTTP client? use a construct different usepublic a different function log format? logger class? () { What if we want unit tests to run fast without waiting for the network? $this->client = new Client(); $this->logger = new XmlLogger(); if we want unit tests to run fast public function retrievefeed What ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); without logging? $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 44
<?php namespace Acme\FeedBundle\Service\FeedAggregator What if we Why is this class use Guzzle\Http\Client; ever want to difficult to unit test? use Acme\Logger\XmlLogger; class FeedAggregator { What if we What if we $client; private private ever want to ever want to$logger; use a different HTTP client? use a construct different are () usepublic a different function { What if we want unit tests to run fast Dependencies pulled. logreplacing format? requires refactoring logger class? without waiting for the network? => $this->client = new Client(); => Dynamic replacing (e.g. for $this->logger = new XmlLogger(); testing) is impossible if we want unit tests to run fast public function retrievefeed What ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); without logging? $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 45
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\Client; use Acme\Logger\XmlLogger; are pulled. class FeedAggregator { private $client; private $logger; public function construct () { $this->client = new Client(); $this->logger = new XmlLogger(); public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 46
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; are pushed. class FeedAggregator { private $client; private $logger; public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 47
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; Class private $logger; on are pushed. only depends interfaces public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 48
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; Class private $logger; on only depends interfaces are pushed. Implementations are injected at runtime public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 49
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; Class private $logger; on only depends interfaces are pushed. Implementations are injected at runtime public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; Easy to replace, even dynamically (for testing) public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$baseurl.$path); return null; return $response->getbody(); //... 50
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; Class private $logger; on only depends interfaces are pushed. Implementations are injected at runtime public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; Easy to replace, even dynamically (for testing) public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) On the level of the class, { $this->logger->log('could not get: '.$baseurl.$path); You are now experts for return null; Dependency Injection. return $response->getbody(); //... 51
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; Class private $logger; on only depends interfaces are pushed. Implementations are injected at runtime public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; Easy to replace, even dynamically (for testing) public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) On the level of the class, { $this->logger->log('could not get: '.$baseurl.$path); You are now experts for return null; Dependency Injection. return $response->getbody(); //... Any questions? 52
<?php namespace Acme\FeedBundle\Service\FeedAggregator Dependencies use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; Class private $logger; on only depends interfaces are pushed. Implementations are injected at runtime public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; Easy to replace, even dynamically (for testing) public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) On the level of the class, { $this->logger->log('could not get: '.$baseurl.$path); You are now experts for return null; Dependency Injection. return $response->getbody(); //... Who constructs and pushes all the dependencies? 53
54
Dependency Injection Container DI Container, DIC, Service Container, the Container DIC 55
56
Very Many Frameworks support Dependency Injection 57
DI is very easy in Symfony 2 58
DI is very easy in Symfony 2 Any ordinary PHP class can be managed by DIC Only 2 lines of configuration per class are needed Any class managed by the DIC is called a Service 59
DI is very easy in Symfony 2 Any ordinary PHP class can be managed by DIC Only 2 lines of configuration per class are needed Any class managed by the DIC is called a Service # app/config/config.yml #... services: my_service: class: Acme\MyBundle\Service\AwesomeClass 60
DI is very easy in Symfony 2 Any ordinary PHP class can be managed by DIC Only 2 lines of configuration per class are needed Any class managed by the DIC is called a Service # app/config/config.yml Class to manage #... services: my_service: class: Acme\MyBundle\Service\AwesomeClass Name of the new service 61
# php app/console container:debug 62
DI is very easy in Symfony 2 Any ordinary PHP class can be managed by DIC Only 2 lines of configuration per class are needed Any class managed by the DIC is called a Service # app/config/config.yml #... services: my_service: class: Acme\MyBundle\Service\AwesomeClass 63
DI is very easy in Symfony 2 Any ordinary PHP class can be managed by DIC Only 2 lines of configuration per class are needed Any class managed by the DIC is called a Service # app/config/config.yml #... services: my_service: class: Acme\MyBundle\Service\AwesomeClass arguments: some_arg: string another: - array_member - array_member even_more: @another_service 64
DI is very easy in Symfony 2 Any ordinary PHP class can be managed by DIC Only 2 lines of configuration per class are needed Any class managed by the DIC is called a Service # app/config/config.yml Arguments can be #... strings, numbers, services: my_service: arrays, placeholders, class: Acme\MyBundle\Service\AwesomeClass and many more... arguments: some_arg: string another: Any other service - array_member can be injected as - array_member even_more: @another_service as argument 65
<?php use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; private $logger; public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); if (200!= $response->getstatuscode()) { $this->logger->log('could not get: '.$host.$path); return null; return $response->getbody(); //... 66
<?php use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; private $logger; public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); 67 if (200!= $response->getstatuscode()) {
# app/config/config.yml #... services: feed_aggregator: class: Acme\FeedBundle\Service\FeedAggregator arguments: client: @http_client logger: @logger <?php use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; private $logger; public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); 68 if (200!= $response->getstatuscode()) {
# app/config/config.yml #... services: feed_aggregator: class: Acme\FeedBundle\Service\FeedAggregator arguments: client: @http_client logger: @logger <?php use Guzzle\Http\ClientInterface; use Acme\Logger\LoggerInterface; class FeedAggregator { private $client; private $logger; public function construct(clientinterface $client, LoggerInterface $logger) { $this->client = $client; $this->logger = $logger; public function retrievefeed ($baseurl, $path) { $request = $this->client->setbaseurl($baseurl)->get($path); $response = $request->send(); 69 if (200!= $response->getstatuscode()) {
Different Config needed for Functional Testing? app/config/config.yml app/config/config_dev.yml app/config/config_test.yml 70 Put it here here
71
wiederverwendbar unit-testbar Großteil der Codebasis 72
nicht wiederverwendbar nicht unit-testbar Minimum an Code wiederverwendbar unit-testbar Großteil der Codebasis 73
Dependency Injection 74
Dependency Injection Services 75
Controller bekommen Instanzen der Services vom DIC, z.b. so: $this->get( logger ) Services 76
Nachhaltige Architektur für große Web-Projekte Modularität Erweiterbarkeit Wartbarkeit Testbarkeit Performance 77
Further Reading http://fabien.potencier.org/article/11/what-is-dependency-injection http://symfony.com/doc/current/components/dependency_injection/compilation.html http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html Use the source... 78
79
Commonly used Types of Events Kernel events Form events Doctrine events 80
Kernel Events 81
Kernel Events Used in SensioFrameworkExtraBundle to act on the @Template annotation. 82
To act on a Kernel Event Create a Listener Class Let the DIC do all remaining work for you 83
To act on a Kernel Event Create a Listener Class Let the DIC do all remaining work for you Create a service definition 84
To act on a Kernel Event # app/config/config.yml services: your_listener_name: class: Acme\DemoBundle\EventListener\AcmeExceptionListener Create a Listener Class Let the DIC do all remaining work for you Create a service definition 85
To act on a Kernel Event # app/config/config.yml services: your_listener_name: class: Acme\DemoBundle\EventListener\AcmeExceptionListener Create a Listener Class Let the DIC do all remaining work for you Create a service definition Add a tag labeling the service as a listener 86
To act on a Kernel Event # app/config/config.yml services: your_listener_name: class: Acme\DemoBundle\EventListener\AcmeExceptionListener tags: - { name: kernel.event_listener, event: kernel.exception, method: onkernelexception Create a Listener Class Let the DIC do all remaining work for you Create a service definition Add a tag labeling the service as a listener 87
How to listen to Kernel events listener class + service tag Form events Doctrine events 88
How to listen to Kernel events listener class + service tag Form events listener class + service tag closure inside form type class Doctrine events 89
How to listen to Kernel events listener class + service tag Form events listener class + service tag closure inside form type class Doctrine events listener class + service tag callback method inside entity class 90
Time Checkpoint Skip? 91
Which listeners are active? Your answer? 92
Which listeners are active? Kernel events listener class + service tag Form events listener class + service tag closure inside form type class Doctrine events listener class + service tag callback method inside entity class Your own custom events listener class + service tag closure etc. 93
Which listeners are active? Symfony profiler > Events 94
Which listeners are active? Symfony profiler > Events 95
Which listeners are active? Symfony profiler > Events 96
Which listeners are active? Symfony profiler > Events Check in your own Command oder Controller 97
Which listeners are active? Symfony profiler > Events Check in your own Command oder Controller $listeners = $this->getcontainer()->get('event_dispatcher')->getlisteners(); 98
Which listeners are active? Symfony profiler > Events Check in your own Command oder Controller $listeners = $this->getcontainer()->get('event_dispatcher')->getlisteners(); print_r (array_keys($listeners)); print_r (array_keys($listeners['kernel.view'])); 99
Which listeners are active? Symfony profiler > Events Check in your own Command oder Controller $listeners = $this->getcontainer()->get('event_dispatcher')->getlisteners(); print_r (array_keys($listeners)); print_r (array_keys($listeners['kernel.view'])); $entitymanager = $this->getcontainer()->get('doctrine.orm.entity_manager'); Doctrine Entity Manager uses an instance of the Doctrine Event Manager (injected by the DIC). 100
Which listeners are active? Symfony profiler > Events Check in your own Command oder Controller $listeners = $this->getcontainer()->get('event_dispatcher')->getlisteners(); print_r (array_keys($listeners)); print_r (array_keys($listeners['kernel.view'])); $entitymanager = $this->getcontainer()->get('doctrine.orm.entity_manager'); $listeners = $entitymanager->geteventmanager()->getlisteners(); print_r (array_keys($listeners)); //... 101
Which listeners are active? Symfony profiler > Events Check in your own Command oder Controller ListenersDebugCommandBundle 102
ListenersDebugCommandBundle https://github.com/egulias/listenersdebugcommandbundle app/console container:debug:listeners --event=event.name: if issued will filter to show only the listeners listening to the given name (ordered by descending priority, unless you use: --order-asc) --show-private : if issued will show also private services... 103
Nachhaltige Architektur für große Web-Projekte Modularität Erweiterbarkeit Wartbarkeit Testbarkeit Performance 104
Gute Performance machbar? Deal Finder Bundle Deal Lookup Bundle Product Search Bundle Shop Crawler Bundle Shop Aggregator Bundle Product Bundle DIC Tag: shop_service Sol-r Bundle Client Lib. Sol-r Sol-r Server Elastic Search Bundle Client Lib. Elastic S. Elastic S. Server Doctrine Bundle Datenbank Guzzle Bundle Guzzle HTTP Client Generischer Webserver 105 ShopAAA Bundle ShopBBB Bundle ShopCCC Bundle
Gute Performance machbar! Deal Finder Bundle Danke Opcache: Byte-Code im Deal Lookup Bundle RAM Product Search Bundle Shop Crawler Bundle Shop Aggregator Bundle Product Bundle DIC Tag: shop_service Sol-r Bundle Client Lib. Sol-r Sol-r Server Elastic Search Bundle Client Lib. Elastic S. Elastic S. Server Doctrine Bundle Datenbank Guzzle Bundle Guzzle HTTP Client Generischer Webserver 106 ShopAAA Bundle ShopBBB Bundle ShopCCC Bundle
Nachhaltige Architektur für große Web-Projekte Modularität Erweiterbarkeit Wartbarkeit Testbarkeit Performance 107
Further Reading: Dependency Injection http://fabien.potencier.org/article/11/what-is-dependency-injection http://symfony.com/doc/current/components/dependency_injection/compilation.html http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html Use the source... 108
Further Reading: Event Dispatcher Symfony Event Dispatcher http://symfony.com/doc/current/components/event_dispatcher/ http://symfony.com/doc/current/cookbook/event_dispatcher/ http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html http://api.symfony.com/2.4/symfony/component/eventdispatcher/eventdispatcher.html Doctrine Event Manager http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/event-listeners.html http://www.whitewashing.de/2013/07/24/doctrine_and_domainevents.html http://www.doctrine-project.org/api/orm/2.4/class-doctrine.orm.entitymanager.html http://www.doctrine-project.org/api/common/2.4/class-doctrine.common.eventmanager.h 109
Vielen Dank für Eure Aufmerksamkeit! 110
Fragen? Ideen, Wünsche, Anmerkungen? 111