symfony2 your way
Post on 15-Jan-2015
1.170 Views
Preview:
DESCRIPTION
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