Symfony2 your way

46
Symfony2 y o u r way by Rafał Wrzeszcz, 2014 [email protected] http://wrzasq.pl/ http://chilldev.pl/ https://github.com/rafalwrzeszcz https://linkedin.com/in/rafalwrzeszcz

description

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.

Transcript of Symfony2 your way

Page 1: Symfony2   your way

Symfony2 your wayby Rafał Wrzeszcz, 2014

[email protected]://wrzasq.pl/http://chilldev.pl/https://github.com/rafalwrzeszczhttps://linkedin.com/in/rafalwrzeszcz

Page 2: Symfony2   your way

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

Page 3: Symfony2   your way

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.');

Page 4: Symfony2   your way

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'

);

Page 5: Symfony2   your way

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']);

Page 6: Symfony2   your way

Alles zusammen

my:

prefix: "prod."

sources:

- "srv1"

- "srv2"

$db = $di->get(

'my.datasource'

);

Services definition

sConfiguration schema

DI extensio

n

Page 7: Symfony2   your way

Overriding services and parameters

services:

form.resolved_type_factory:

class: "My\\Bundle\\ApplicationBundle\\Form\\ProfiledTypeFactory"

arguments:

- "@profiler"

parameters:

web_profiler.debug_toolbar.position: "top"

Page 8: Symfony2   your way

Less invasive way - %*.class%

parameters:

form.resolved_type_factory.class: "My\\Bundle\\ApplicationBundle\\Form\\ProfiledTypeFactory"

Page 9: Symfony2   your way

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>

Page 10: Symfony2   your way

DIC compiler

Loading all extensions

Merging them

DIC compiler

Dumping compiled DIC into cache

Page 11: Symfony2   your way

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('My\\Bundle\\ApplicationBundle\\Form\\ProfiledTypeFactory');

$definition->addMethodCall(

'setProfiler',

[new Reference('profiler')]

);

}

}

}

Page 12: Symfony2   your way

Registering compiler pass

class MyBundle extends Bundle

{

public function build(ContainerBuilder $container)

{

parent::build($container);

$container->addCompilerPass(

new MyCompilerPass(),

PassConfig::TYPE_OPTIMIZE

);

}

}

Page 13: Symfony2   your way

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>

Page 14: Symfony2   your way

Own tags handlerclass 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']

]);

}

}

}

}

Page 15: Symfony2   your way

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.

Page 16: Symfony2   your way

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

Page 17: Symfony2   your way

Events

● move your non-core features to event handlers,● keep core logic thin and simple,● provide before and after events.

Page 18: Symfony2   your way

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

}

Page 19: Symfony2   your way

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 */)

);

Page 20: Symfony2   your way

Subscribing to events

$di->get('event_dispatcher')->addListener(

MyLibFormEvent::FORM_SUBMITTED,

function (MyLibFormEvent $event)

{

// handle the event

}

);

Page 21: Symfony2   your way

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.

Page 22: Symfony2   your way

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>

Page 23: Symfony2   your way

JMSAopBundle

"jms/aop-bundle": "1.0.1"

Page 24: Symfony2   your way

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%"/>

Page 25: Symfony2   your way

Pointcut

class MySecurityPointcut implements PointcutInterface

{

public function matchesClass(ReflectionClass $class)

{

return $class->implementsInterface('My\\DataAdapterInterface');

}

public function matchesMethod(ReflectionMethod $method)

{

return $method->getName() == 'read';

}

}

Page 26: Symfony2   your way

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.');

}

}

}

Page 27: Symfony2   your way

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

Page 28: Symfony2   your way

Bundles inheritance

class MySecurityBundle extends Bundle

{

public function getParent()

{

return 'SonataUserBundle';

}

}

Page 29: Symfony2   your way

Overriding controllers

class AdminSecurityController

extends \Sonata\UserBundle\Controller\AdminSecurityController

{

public function loginAction()

{

// own login handling logic

}

}

Page 30: Symfony2   your way

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"

Page 31: Symfony2   your way

Overriding templates

public function loginAction()

{

// action logic

return $this->templating->renderResponse(

'SonataUserBundle:Admin:Security/login.html.php',

$params

);

}

Page 32: Symfony2   your way

Overriding versus extending

{# MySecurityBundle/Resources/views/Admin/Security/login.html.twig #}

{% extends '::layout.html.twig' %}

{% block sidebar %}

<h3>Table of Contents</h3>

{# ... #}

{{ parent() }}

{% endblock %}

Page 33: Symfony2   your way

Application templates● app/Resources/MySecurityBundle/views/Admin/Security/login.html.php

application resource,

● src/MySecurityBundle/Resources/views/Admin/Security/login.html.phpinheriting bundle,

● src/SonataUserBundle/Resources/views/Admin/Security/login.html.phpsource template.

Page 34: Symfony2   your way

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

Page 35: Symfony2   your way

Composerhttps://getcomposer.org/

wget -O - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin

Page 36: Symfony2   your way

Scripts

incenteev/composer-parameter-handler:ScriptHandler::buildParameters

sensio/distribution-bundle:ScriptHandler::buildBootstrapScriptHandler::clearCacheScriptHandler::installAssetsScriptHandler::installRequirementsFileScriptHandler::removeSymfonyStandardFiles

Page 37: Symfony2   your way

Autoloading overriding

{

"autoload": {

"classmap": ["src/Symfony/"]

}

}

Page 38: Symfony2   your way

React(react/http)

$app = function ($request, $response) {

$response->writeHead(200, ['Content-Type' => 'text/plain']);

$response->end("Hello World\n");

};

$loop = React\EventLoop\Factory::create();

$socket = new React\Socket\Server($loop);

$http = new React\Http\Server($socket, $loop);

$http->on('request', $app);

$socket->listen(1337);

$loop->run();

Page 39: Symfony2   your way

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

Page 40: Symfony2   your way

Varnish

templating: esi: true

Page 41: Symfony2   your way

ESI example

<body>

<?php echo $this['actions']->render(

$this['actions']->controller('MySecurityBundle:Panel:sidebar'),

['strategy' => 'esi']

); ?>

<h1>My blog</h1>

<?php /* page content */ ?>

</body>

Page 42: Symfony2   your way

QA tools – CLI

● php -l● phpcs (squizlabs/php_codesniffer)● phpcpd (sebastian/phpcpd)● phpmd (phpmd/phpmd)● phpunit (phpunit/phpunit)● phpdoc (phpdocumentor/phpdocumentor)

Page 43: Symfony2   your way

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/).

Page 44: Symfony2   your way

Scrutinizerhttps://scrutinizer-ci.com/

Page 45: Symfony2   your way

SensioLabs Insightshttps://insight.sensiolabs.com/

Page 46: Symfony2   your way

Thank you!