symfony2 your way

Post on 15-Jan-2015

1.170 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

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

Symfony2 your wayby Rafał Wrzeszcz, 2014

rafal.wrzeszcz@wrzasq.plhttp://wrzasq.pl/http://chilldev.pl/https://github.com/rafalwrzeszczhttps://linkedin.com/in/rafalwrzeszcz

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

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

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'

);

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

Alles zusammen

my:

prefix: "prod."

sources:

- "srv1"

- "srv2"

$db = $di->get(

'my.datasource'

);

Services definition

sConfiguration schema

DI extensio

n

Overriding services and parameters

services:

form.resolved_type_factory:

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

arguments:

- "@profiler"

parameters:

web_profiler.debug_toolbar.position: "top"

Less invasive way - %*.class%

parameters:

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

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>

DIC compiler

Loading all extensions

Merging them

DIC compiler

Dumping compiled DIC into cache

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

);

}

}

}

Registering compiler pass

class MyBundle extends Bundle

{

public function build(ContainerBuilder $container)

{

parent::build($container);

$container->addCompilerPass(

new MyCompilerPass(),

PassConfig::TYPE_OPTIMIZE

);

}

}

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>

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

]);

}

}

}

}

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.

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

Events

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

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

}

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

);

Subscribing to events

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

MyLibFormEvent::FORM_SUBMITTED,

function (MyLibFormEvent $event)

{

// handle the event

}

);

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.

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>

JMSAopBundle

"jms/aop-bundle": "1.0.1"

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

Pointcut

class MySecurityPointcut implements PointcutInterface

{

public function matchesClass(ReflectionClass $class)

{

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

}

public function matchesMethod(ReflectionMethod $method)

{

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

}

}

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

}

}

}

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

Bundles inheritance

class MySecurityBundle extends Bundle

{

public function getParent()

{

return 'SonataUserBundle';

}

}

Overriding controllers

class AdminSecurityController

extends \Sonata\UserBundle\Controller\AdminSecurityController

{

public function loginAction()

{

// own login handling logic

}

}

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"

Overriding templates

public function loginAction()

{

// action logic

return $this->templating->renderResponse(

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

$params

);

}

Overriding versus extending

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

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

{% block sidebar %}

<h3>Table of Contents</h3>

{# ... #}

{{ parent() }}

{% endblock %}

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.

Symfony2 – ways to customize

● Dependency Injection Container

● Code

● Bundles

● External tools

Composerhttps://getcomposer.org/

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

Scripts

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

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

Autoloading overriding

{

"autoload": {

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

}

}

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();

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

Varnish

templating: esi: true

ESI example

<body>

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

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

['strategy' => 'esi']

); ?>

<h1>My blog</h1>

<?php /* page content */ ?>

</body>

QA tools – CLI

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

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

Scrutinizerhttps://scrutinizer-ci.com/

SensioLabs Insightshttps://insight.sensiolabs.com/

Thank you!

top related