Symfony2   your way
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Symfony2 your way

on

  • 1,152 views

I'm a computers enthusiast since when I got my first one in age of 3. I contribute to open source initiatives, work on commercial projects and play with new technologies in my spare, trying to get ...

I'm a computers enthusiast since when I got my first one in age of 3. I contribute to open source initiatives, work on commercial projects and play with new technologies in my spare, trying to get best experience from all of these forms.

With such variety of project types came different kinds of requirements, coding rules, architectures and company standards. Symfony2 has proven to be a very flexible framework that can be adapted to all of these needs. I will present to you ways in which you can make Symfony2 woring YOUR way.

Statistics

Views

Total Views
1,152
Views on SlideShare
1,143
Embed Views
9

Actions

Likes
3
Downloads
10
Comments
0

2 Embeds 9

https://twitter.com 5
https://www.linkedin.com 4

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • Ale przedefiniowywanie usług trąci nieco hackowaniem - możemy w ten sposób naruszyć pewien kontrakt. Może się okazać, że z czasem usługa, którą nadpisujemyu zacznie przyjmować dodatkowe parametry. Istnieje bardziej subtelny sposób, który sprowadza się mniej więcej do tego samego - podmienienia klasy, z której będzie instancjowana usługa - lecz przy pomocy samych parametrów, tak, aby nie ryzykować ingerencji w jakiekolwiek interakcje z usługą (takie jak wywołania metod). <br /> <br /> Ten sposob to parametr z sufiksem ".class". Jest to konwencja, dzięki której właśnie mamy możliwość zawsze podmiany implementacji usługi na naszą (oczywiście musi ona być kompatybilna).
  • Jak to działa? Po prostu definiując nasze usługi tworzymy jeden dodatkowy parametr dla każdej z nich z sufiksem ".class" (także dobrze jest o tym pamiętać rónież nazywając parametry - tak, aby unikać nazw parametrów z tym sufiksem). Parametr ten przechowuje nazwę klasy wykorzystywanej przez usługę i umożliwia jej łatwą zmianę (wystarczy nadpisać jego wartość, nie trzeba nadpisywać całej usługi). <br /> <br /> W taki sposób (z użyciem parametru klasy) zdefiniowane są wszystkie usługi samego Symfony2, a także większości popularnych (i tych mniej popularnych też) bundli. Generalnie praktycznie nic nas nie kosztuje dodanie takiego parametru do naszych usług, a potrafi to nieraz uratować tyłek, szczególnie, jeśli tworzymy jakiś bundle ogólnego przeznaczenia, z którego mieliby korzystać inni.
  • Kolejną rzeczą, którą możemy modyfikować względem bazowego bundle’a jest routing. Przy czym aby nasze przesłanianie było możliwe trzeba skorzystać ze specjalnej notacji, która oznacza odniesienie do zasobów względem danego bundle’a. W ten sposób możemy przesłonić dany statyczny plik tworząc plik o takiej samej nazwie i ścieżce.
  • Bardzo podobnie ma się sprawa z przesłanianiem szablonów. Właściwie dosłownie tak samo – aby przesłonić szablon widoku tworzymy plik o takiej samej nazwie i takiej samej ścieżce w naszym bundlu.
  • Ale, w przypadku szablonów należy pamiętać o jednej istotnej rzeczy, a mianowicie różnice pomiędzy przesłanianiem szablonów, a ich rozszerzaniem. Jest to o tyle problematyczne, że angielskojęzyczne nazwy konstrukcji odpowiedzialnych za te poszczególne cechy systemu szablonów są zbieżne z nomenklaturą w programowaniu obiektowym, ale ich działanie jest odmienne, żeby nawet nie powiedzieć odwrotne. <br /> <br /> Przesłaniając szablon całkowicie przesłaniamy go w systemie, nie ma żadnej możliwości dostępu do niego, nasz szablon musi go w całości zastąpić <br /> <br /> Korzystając z mechanizmu rozszerzania definiujemy logiczny ciąg zagnieżdżania szablonów – w tym przypadku nasz widok zostanie osadzony w innym, konkretnie w głównym layoucie aplikacji. Użycie `parent()` w kodzie odnosi się właśnie do tego zagnieżdżenia, nie do przsłoniętego widoku z bundle’a.
  • W przypadku szablonów mamy też możliwość przesłaniania szablonów w jeszcze jeden sposób – przez zasoby samej aplikacji. <br /> <br /> Zasoby aplikacji mają najwyższy priorytet, lecz warto zwrócić uwagę na inną budowę ścieżki.
  • I w końcu ostatnia część, a mianowicie zewnętrzne usługi i narzędzia. Nie samym Symfony człowiek żyje. Na szczęście społeczność Symfony dobrze o tym wie, framework ten jest bardzo otwarty i “przyjazny” w kwestii integracji z zewnętrznymi usługami, API, czy narzędziami. <br /> <br /> W bardzo wielu frameworkach istnieje podejście tworzenia wszystkiego samemu, podczas gdy w Symfony podejście jest zazwyczaj odwrotne – jeśli istnieje już do czegoś rowzwiązanie to w Symfony2 często znajdziemy integrację z takim rozwiązaniem.
  • Tworząc projekt w Symfony praktycznie nie obejdziemy się bez Composera (nie jestem jego wielbicielem, ale jest to obecnie praktycznie de facto standard, więc nie ma co wynajdywać koła na nowo).
  • Jedną z większych, moim zdaniem, ułomności w Composerze jest sposób podpinania się z własną implementacją pod cokolwiek, no ale nie będę się rozwodził o moich wewnętrznych odczuciach… <br /> <br /> W Composerze możemy podpiąć się pod zdarzenia (ich listę można znaleźć w specyfikacji na stronie). Sensio (twórcy Symfony) udostępniają kilka gotowych skryptów, które pozwalają zautomatyzować cykl pracy z aplikacją.
  • Ale Composer daje nam pewną bardzo potężną broń. Chyba najbrzydszy z jakichkolwiek dziś przeze mnie tutaj prezentowanych hacków. Jeśli myśleliście, że przedefiniowywanie definicji usług w DIC było złe, to spójrzcie na to. <br /> <br /> Otóż możemy całkiem przesłonić klasy dowolnej ładowanej przez nas biblioteki naszymi własnymi jeśli tylko nadpiszemy autoloader generowany przez Composera. <br /> <br /> Ale jest jeden warunek – musimy skorzystać z autoloadera typu classmap (niezależnie od tego, że Composer i tak generuje dla nas class-mapę, chodzi o hierarchię loaderów, class-mapa jako najszybsza i generowana docelowo, jest pierwszym miejscem, gdzie wygenerowany autoloader szuka klasy).
  • Kolejnym ciekawym i niezmiernie obiecującym projektem jest React. Implementuje on model przetwarzania request-response znany z wielu innych platform, takich jak chociażby Node.js. <br /> <br /> Co to ma wspólnego z Symfony? <br /> <br /> Oczywiście możemy, jako funkcję obsługującą żądanie zaimplementować bootstrapping naszej aplikacji Symfony, ale jest jeszcz lepiej – coś takiego już istnieje.
  • Kożyści są bardzo duże, przede wszystkim odchodzi konieczność każdorazowego bootstrapowania aplikacji, która nawet przy bardzo zoptymalizowanych projektach wymaga zazwyczaj dużo operacji wejścia/wyjścia (chociażby wczytywanie plików), co jest nieraz największym obciążeniem. <br /> <br /> Możemy również wykorzystać nową architekturę i wyeliminować niektóre inne zależności, na przykład niektórych rzeczy nie trzeba trzymać w cache’u, czy sesji, gdyż można je trzymać bezpośrednio w pamięci procesu. <br /> <br /> Nie ma niestety róży bez kolców. Jest to kompletna zmiana podejścia w przypadku PHP, pamiętajmy, że w normalnym trybie skrypty PHP są zawsze uruchamiane w odrębnym środowisku. W tym przypadku musimy pamiętać, że nasz skrypt działa nieprzerwanie, każde kolejne zapytanie operuje w tym samym środowisku, a więc musimy być świadomi, że na przykład ta sama instancja naszego obiektu będzie wykorzystana podobnie, więc inicjując obiekty musimy być pewni kiedy się ona wykonuje.

Symfony2 your way Presentation Transcript

  • 1. Symfony2 your way by Rafał Wrzeszcz, 2014 rafal.wrzeszcz@wrzasq.pl http://wrzasq.pl/ http://chilldev.pl/ https://github.com/rafalwrzeszcz https://linkedin.com/in/rafalwrzeszcz
  • 2. Symfony2 – ways to customize ● Dependency Injection Container ● Code ● Bundles ● External tools
  • 3. Dependency Injection (yet without “Container”) class DataSource { protected $adapter; protected $prefix; public function __construct( DataAdapter $adapter, $prefix ) { $this->adapter = $adapter; $this->prefix = $prefix; } } class DataAdapter { protected $sources; public function __construct( array $sources ) { $this->sources = $sources; } } $conn = new DataAdapter( ['srv1', 'srv2'] );$db = new DataSource( $conn, 'prod.' );
  • 4. Putting dependencies into Container class MyExtension extends Extension { public function load( array $configs, ContainerBuilder $container ) { $loader = new XmlFileLoader( $container, new FileLocator(__DIR__ . '/../Resources/config') ); $loader->load('services.xml'); } } <parameter key="my.prefix">prod.</parameter> <parameter key="my.sources" type="collection"> <parameter>srv1</parameter> <parameter>srv2</parameter> </parameter> <service id="my.dataadapter" class="DataAdapter"> <argument>%my.sources%</argument> </service> <service id="my.datasource" class="DataSource"> <argument type="service" id="my.dataadapter"/> <argument>%my.prefix%</argument> </service> $db = $di->get( 'my.datasource' );
  • 5. Semantic configuration my: prefix: "prod." sources: - "srv1" - "srv2" class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $treeBuilder->root('my') ->children() ->scalarNode('prefix')->end() ->arrayNode('sources') ->prototype('scalar')->end() ->end() ->end(); return $treeBuilder; } } //MyExtension::load() $config = $this->processConfiguration( new Configuration(), $configs); $container->setParameter( 'my.prefix', $config['prefix']); $container->setParameter( 'my.sources', $config['sources']);
  • 6. Alles zusammen my: prefix: "prod." sources: - "srv1" - "srv2" $db = $di->get( 'my.datasource' ); Services definitions Configuration schema DI extension
  • 7. Overriding services and parameters services: form.resolved_type_factory: class: "MyBundleApplicationBundleFormProfiledTypeFactory" arguments: - "@profiler" parameters: web_profiler.debug_toolbar.position: "top"
  • 8. Less invasive way - %*.class% parameters: form.resolved_type_factory.class: "MyBundleApplicationBundleFormProfiledTypeFactory"
  • 9. Adding .class parameter <service id="my.dataadapter" class="DataAdapter"> <argument>%my.sources%</argument> </service> <service id="my.datasource" class="DataSource"> <argument type="service" id="my.dataadapter"/> <argument>%my.prefix%</argument> </service> <parameter key="my.dataadapter.class">DataAdapter</parameter> <parameter key="my.datasource.class">DataSource</parameter> <service id="my.dataadapter" class="%my.dataadapter.class%"> <argument>%my.sources%</argument> </service> <service id="my.datasource" class="%my.datasource.class%"> <argument type="service" id="my.dataadapter"/> <argument>%my.prefix%</argument> </service>
  • 10. DIC compiler Loading all extensions Merging them DIC compiler Dumping compiled DIC into cache
  • 11. Creating own compiler phase class MyPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { if ($container->hasDefinition('form.resolved_type_factory')) { $definition = $container->getDefinition('form.resolved_type_factory'); $definition->setClass('MyBundleApplicationBundleFormProfiledTypeFactory'); $definition->addMethodCall( 'setProfiler', [new Reference('profiler')] ); } } }
  • 12. Registering compiler pass class MyBundle extends Bundle { public function build(ContainerBuilder $container) { parent::build($container); $container->addCompilerPass( new MyCompilerPass(), PassConfig::TYPE_OPTIMIZE ); } }
  • 13. Adding tags to services <parameter key="my.dataadapter.class">DataAdapter</parameter> <parameter key="my.datasource.class">DataSource</parameter> <service id="my.dataadapter" class="%my.dataadapter.class%"> <argument>%my.sources%</argument> <tag name="my.datasource" alias="data" prefix="%my.prefix_data%"/> <tag name="my.datasource" alias="meta" prefix="%my.prefix_meta%"/> </service>
  • 14. Own tags handler class MyPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { $class = $container->getParameter('my.datasource.class'); foreach ($container->findTaggedServiceIds('my.datasource') as $id => $tags) { foreach ($tags as $tag) { $definition = $container->register($tag['alias'], $class); $definition->setArguments([ new Reference($id), $tag['prefix'] ]); } } } }
  • 15. Most important pre-defined tags ● twig.extension/templating.helper – registering templating helpers, ● security.voter – custom security access logic, ● monolog.logger – marking specific channels for the logger, ● kernel.event_listener/kernel.event_subscriber – subscribing to events, ● data_collector – web profiler data collector. More on http://symfony.com/doc/current/reference/dic_tags.html.
  • 16. Symfony2 – ways to customize ● Dependency Injection Container ● Code ● Bundles ● External tools
  • 17. Events ● move your non-core features to event handlers, ● keep core logic thin and simple, ● provide before and after events.
  • 18. Custom event object class MyLibFormEvent extends Event { const FORM_SUBMITTED = 'my_lib.form.event.submitted'; const FORM_HANDLED = 'my_lib.form.event.handled'; // properties public function __construct(/* custom event params */) { // assign properties } // getters and setters }
  • 19. Dispatching the event $eventDispatcher = $di->get('event_dispatcher'); $eventDispatcher->dispatch( MyLibFormEvent::FORM_SUBMITTED, new MyLibFormEvent(/* custom data */) ); // process form $eventDispatcher->dispatch( MyLibFormEvent::FORM_HANDLED, new MyLibFormEvent(/* custom data */) );
  • 20. Subscribing to events $di->get('event_dispatcher')->addListener( MyLibFormEvent::FORM_SUBMITTED, function (MyLibFormEvent $event) { // handle the event } );
  • 21. Symfony kernel events ● kernel.requestnew request is being dispatched, ● kernel.responseresponse is generated by request handler (like controller), ● kernel.finish_requestfired after request is handled – regardless of operation status, ● kernel.terminateresponse is already sent – we can perform some heavy tasks that won’t afect page load, ● kernel.exceptionunhandled exception occured during request handling.
  • 22. DI tag event_listener <service id="my.lib.response_listener" class="%my.lib.response_listener.class%"> <tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse"/> </service>
  • 23. JMSAopBundle "jms/aop-bundle": "1.0.1"
  • 24. Writing own aspects <service id="my.lib.security.pointcut" class="%my.lib.security.pointcut.class%"> <tag name="jms_aop.pointcut" interceptor="my.lib.security.interceptor"/> </service> <service id="my.lib.security.interceptor" class="%my.lib.security.interceptor.class%"/>
  • 25. Pointcut class MySecurityPointcut implements PointcutInterface { public function matchesClass(ReflectionClass $class) { return $class->implementsInterface('MyDataAdapterInterface'); } public function matchesMethod(ReflectionMethod $method) { return $method->getName() == 'read'; } }
  • 26. Interceptor class MySecurityInterceptor implements MethodInterceptorInterface { public function intercept(MethodInvocation $invocation) { if (/* security checks */) { // you can modify $invocation->arguments array $result = $invocation->proceed(); // you can post-process results return $result; } else { throw new SecurityException('Unauthorized access attempt.'); } } }
  • 27. Symfony2 – ways to customize ● Dependency Injection Container ● Code ● Bundles ● External tools
  • 28. Bundles inheritance class MySecurityBundle extends Bundle { public function getParent() { return 'SonataUserBundle'; } }
  • 29. Overriding controllers class AdminSecurityController extends SonataUserBundleControllerAdminSecurityController { public function loginAction() { // own login handling logic } }
  • 30. Overriding routing _admin_security: # cascade checks: # MySecurityBundle/Resources/config/routing/admin_security.xml # SonataUserBundle/Resources/config/routing/admin_security.xml resource: "@SonataUserBundle/Resources/config/routing/admin_security.xml" prefix: "/admin"
  • 31. Overriding templates public function loginAction() { // action logic return $this->templating->renderResponse( 'SonataUserBundle:Admin:Security/login.html.php', $params ); }
  • 32. Overriding versus extending {# MySecurityBundle/Resources/views/Admin/Security/login.html.twig #} {% extends '::layout.html.twig' %} {% block sidebar %} <h3>Table of Contents</h3> {# ... #} {{ parent() }} {% endblock %}
  • 33. Application templates ● app/Resources/MySecurityBundle/views/Admin/Security/login.html.php application resource, ● src/MySecurityBundle/Resources/views/Admin/Security/login.html.php inheriting bundle, ● src/SonataUserBundle/Resources/views/Admin/Security/login.html.php source template.
  • 34. Symfony2 – ways to customize ● Dependency Injection Container ● Code ● Bundles ● External tools
  • 35. Composer https://getcomposer.org/ wget -O - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin
  • 36. Scripts incenteev/composer-parameter-handler: ScriptHandler::buildParameters sensio/distribution-bundle: ScriptHandler::buildBootstrap ScriptHandler::clearCache ScriptHandler::installAssets ScriptHandler::installRequirementsFile ScriptHandler::removeSymfonyStandardFiles
  • 37. Autoloading overriding { "autoload": { "classmap": ["src/Symfony/"] } }
  • 38. React (react/http) $app = function ($request, $response) { $response->writeHead(200, ['Content-Type' => 'text/plain']); $response->end("Hello Worldn"); }; $loop = ReactEventLoopFactory::create(); $socket = new ReactSocketServer($loop); $http = new ReactHttpServer($socket, $loop); $http->on('request', $app); $socket->listen(1337); $loop->run();
  • 39. PHP ProcessManager (marcj/php-pm) http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html ./bin/ppm start ~/my/path/to/symfony/ --bridge=httpkernel
  • 40. Varnish templating: esi: true
  • 41. ESI example <body> <?php echo $this['actions']->render( $this['actions']->controller('MySecurityBundle:Panel:sidebar'), ['strategy' => 'esi'] ); ?> <h1>My blog</h1> <?php /* page content */ ?> </body>
  • 42. QA tools – CLI ● php -l ● phpcs (squizlabs/php_codesniffer) ● phpcpd (sebastian/phpcpd) ● phpmd (phpmd/phpmd) ● phpunit (phpunit/phpunit) ● phpdoc (phpdocumentor/phpdocumentor)
  • 43. QA tools – in the cloud ● CIfor open source – Travis (https://travis-ci.org/), ● Version Eyedependencies tracking (https://www.versioneye.com/), ● Coverallscode coverage tracking (https://coveralls.io/).
  • 44. Scrutinizer https://scrutinizer-ci.com/
  • 45. SensioLabs Insights https://insight.sensiolabs.com/
  • 46. Thank you!