Download - Symfony2 your way

Transcript
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!