symfony2 revealed

Post on 06-May-2015

35.808 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Symfony2 revealed

TRANSCRIPT

Fabien Potencier

A bit of history

symfony 1.0 – January 2007

•  Started as a glue between existing Open-Source libraries:

– Mojavi (heavily modified), Propel, Prado i18n, …

•  Borrowed concepts from other languages and frameworks:

– Routing, CLI, functional tests, YAML, Rails helpers…

•  Added new concepts to the mix

– Web Debug Toolbar, admin generator, configuration cascade, …

symfony 1.2 – November 2008 •  Based on decoupled but cohesive components

–  Forms, Routing, Cache, YAML, ORMs, …

•  Controller still based on Mojavi

–  View, Filter Chain, …

symfony 1.4 – November 2009 •  Added some polish on existing features

•  Removed the support for deprecated features

•  Current LTS release, maintained until late 2012

Symfony Components YAML Dependency Injection Container Event Dispatcher Templating Routing Console Output Escaper Request Handler …

What is Symfony 2?

Symfony 2 is the next version of the symfony framework…

except Symfony now takes a S instead of a s

Talk about Symfony 2

or symfony 1

To make it clear: Symfony 1

does not make any sense

symfony 2 does not make more sense

Symfony 2

Same philosophy, just better

MVC

hmmm, now that I think about it…

…it’s now probably more a Fabien’s style framework

than anything else

Highly configurable Highly extensible

Same Symfony Components Same great developer tools

Full-featured

Ok, but why a major version then?

Symfony 2 has a brand new

low-level architecture

PHP 5.3

A Quick Tour

<?php

require_once __DIR__.'/../blog/BlogKernel.php';

$kernel = new BlogKernel('prod', false); $kernel->run();

<?php

namespace Application\HelloBundle\Controller;

use Symfony\Framework\WebBundle\Controller;

class HelloController extends Controller { public function indexAction($name) { return $this->render('HelloBundle:Hello:index', array('name' => $name)); } }

Everything is namespaced

Template name Variables to pass to the template

Variables come from the routing

<?php $view->extend('HelloBundle::layout') ?>

Hello <?php echo $name ?>!

Layout

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <?php $view->slots->output('_content') ?> </body> </html> Helpers are objects

hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index

hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index

namespace Application\HelloBundle\Controller;

class HelloController extends Controller { public function indexAction($name) { // ... } }

hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index

namespace Application\HelloBundle\Controller;

class HelloController extends Controller { public function indexAction($name) { // ... } }

hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index

namespace Application\HelloBundle\Controller;

class HelloController extends Controller { public function indexAction($name) { // ... } }

hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index

namespace Application\HelloBundle\Controller;

class HelloController extends Controller { public function indexAction($name) { // ... } }

hello: pattern: /hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index

namespace Application\HelloBundle\Controller;

class HelloController extends Controller { public function indexAction($name) { // ... } }

hello: pattern: /hello/:year/:month/:slug defaults: _bundle: HelloBundle _controller: Hello _action: index

namespace Application\HelloBundle\Controller;

class HelloController extends Controller { public function indexAction($slug, $year) { // ... } }

Extremely Configurable

Dependency Injection Container

Replaces a lot of symfony 1 “things” sfConfig

All config handlers sfProjectConfiguration / sfApplicationConfiguration

sfContext (No Singleton anymore) The configuration cache system

… and some more

in one easy-to-master

unified and cohesive package

Thanks to the DIC, Configuration has never been

so easy and so flexible

Name your configuration files the way you want

Store them where you want

Use PHP, XML, YAML, or INI

$configuration = new BuilderConfiguration(); $configuration->addResource(new FileResource(__FILE__));

$configuration ->mergeExtension('web.user', array('default_culture' => 'fr', 'session' => array('name' => 'SYMFONY', 'type' => 'Native', 'lifetime' => 3600)))

->mergeExtension('doctrine.dbal', array('dbname' => 'sfweb', 'username' => 'root'))

->mergeExtension('web.templating', array('escaping' => 'htmlspecialchars', 'assets_version' => 'SomeVersionScheme'))

->mergeExtension('swift.mailer', array('transport' => 'gmail', 'username' => 'fabien.potencier', 'password' => 'xxxxxx')) ;

PHP  

web.user: default_culture: fr

session: { name: SYMFONY, type: Native, lifetime: 3600 }

web.templating: escaping: htmlspecialchars assets_version: SomeVersionScheme

doctrine.dbal: { dbname: sfweb, username: root, password: null }

swift.mailer: transport: gmail username: fabien.potencier password: xxxxxxxx

YAML  

<web:user default_culture="fr"> <web:session name="SYMFONY" type="Native" lifetime="3600" /> </web:user>

<web:templating escaping="htmlspecialchars" assets_version="SomeVersionScheme" />

<doctrine:dbal dbname="sfweb" username="root" password="" />

<swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" />

XML  

$configuration->mergeExtension('swift.mailer', array( 'transport' => 'gmail', 'username' => 'fabien.potencier', 'password' => 'xxxxxx', ));

PHP  

swift.mailer: transport: gmail username: fabien.potencier password: xxxxxxxx

YAML  

<swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" />

XML  

<?xml version="1.0" ?>

<container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance » xmlns:doctrine="http://www.symfony-project.org/schema/dic/doctrine" xmlns:zend="http://www.symfony-project.org/schema/dic/zend" xmlns:swift="http://www.symfony-project.org/schema/dic/swiftmailer" >

XML  

<?xml version="1.0" ?>

<container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance » xmlns:doctrine="http://www.symfony-project.org/schema/dic/doctrine" xmlns:zend="http://www.symfony-project.org/schema/dic/zend" xmlns:swift="http://www.symfony-project.org/schema/dic/swiftmailer" xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd http://www.symfony-project.org/schema/dic/doctrine http://www.symfony-project.org/schema/dic/doctrine/doctrine-1.0.xsd http://www.symfony-project.org/schema/dic/zend http://www.symfony-project.org/schema/dic/zend/zend-1.0.xsd http://www.symfony-project.org/schema/dic/swiftmailer http://www.symfony-project.org/schema/dic/swiftmailer/swiftmailer-1.0.xsd »>

XML  

Inherit them as much as you want

Mix and match configuration files written in any

format

useful when using third-party plugins

<imports> <import resource="parent.xml" /> <import resource="config.yml" /> <import resource="parameters.ini" /> </imports>

<zend:logger priority="debug" path="%kernel.logs_dir%/%kernel.environment%.log" />

<web:debug exception="%kernel.debug%" toolbar="%kernel.debug%" ide="textmate" />

Mix and match formats

You choose the format you want

Pros Cons XML validation

IDE completion & help verbose (not that much)

YAML concise simple to read easy to change

needs the YAML component no validation no IDE auto-completion

PHP flexible more expressive

no validation

Store sensitive settings outside of your project

<doctrine:dbal dbname="sfweb" username="root" password="SuperSecretPasswordThatAnyoneCanSee" />

SetEnv SYMFONY__DOCTRINE__DBAL__PASSWORD "foobar"

in a .htaccess or httpd.conf file

%doctrine.dbal.password%

Semantic Configuration

<swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" />

XML  

<swift:mailer transport="smtp" encryption="ssl" auth_mode="login" host="smtp.gmail.com" username="fabien.potencier" password="xxxxxxxx" />

XML  

<parameters>

<parameter key="swiftmailer.class">Swift_Mailer</parameter> <parameter key="swiftmailer.transport.smtp.class">Swift_Transport_EsmtpTransport</parameter>

<parameter key="swiftmailer.transport.smtp.host">smtp.gmail.com</parameter> <parameter key="swiftmailer.transport.smtp.port">25</parameter> <parameter key="swiftmailer.transport.smtp.encryption">ssl</parameter> <parameter key="swiftmailer.transport.smtp.username">fabien.potencier</parameter> <parameter key="swiftmailer.transport.smtp.password">xxxxxx</parameter> <parameter key="swiftmailer.transport.smtp.auth_mode">login</parameter> <parameter key="swiftmailer.init_file">swift_init.php</parameter> </parameters>

<services>

<service id="swiftmailer.mailer" class="%swiftmailer.class%"> <argument type="service" id="swiftmailer.transport" /> <file>%swiftmailer.init_file%</file> </service> <service id="swiftmailer.transport.smtp" class="%swiftmailer.transport.smtp.class%"> <argument type="service" id="swiftmailer.transport.buffer" /> <argument type="collection"> <argument type="service" id="swiftmailer.transport.authhandler" /> </argument> <argument type="service" id="swiftmailer.transport.eventdispatcher" />

<call method="setHost"><argument>%swiftmailer.transport.smtp.host%</argument></call> <call method="setPort"><argument>%swiftmailer.transport.smtp.port%</argument></call> <call method="setEncryption"><argument>%swiftmailer.transport.smtp.encryption%</argument></call> <call method="setUsername"><argument>%swiftmailer.transport.smtp.username%</argument></call> <call method="setPassword"><argument>%swiftmailer.transport.smtp.password%</argument></call> <call method="setAuthMode"><argument>%swiftmailer.transport.smtp.auth_mode%</argument></call> </service>

<service id="swiftmailer.transport.buffer" class="Swift_Transport_StreamBuffer"> <argument type="service" id="swiftmailer.transport.replacementfactory" /> </service>

<service id="swiftmailer.transport.authhandler" class="Swift_Transport_Esmtp_AuthHandler"> <argument type="collection"> <argument type="service"><service class="Swift_Transport_Esmtp_Auth_CramMd5Authenticator" /></argument> <argument type="service"><service class="Swift_Transport_Esmtp_Auth_LoginAuthenticator" /></argument> <argument type="service"><service class="Swift_Transport_Esmtp_Auth_PlainAuthenticator" /></argument> </argument> </service>

<service id="swiftmailer.transport.eventdispatcher" class="Swift_Events_SimpleEventDispatcher" />

<service id="swiftmailer.transport.replacementfactory" class="Swift_StreamFilters_StringReplacementFilterFactory" />

<service id="swiftmailer.transport" alias="swiftmailer.transport.smtp" /> </services>

XML  

Creating DIC extensions is insanely simple

Very Fast thanks to a Smart

Caching mechanism it always knows when to flush the cache

/** * Gets the 'swiftmailer.mailer' service. * * This service is shared. * This method always returns the same instance of the service. * * @return Swift_Mailer A Swift_Mailer instance. */ protected function getSwiftmailer_MailerService() { if (isset($this->shared['swiftmailer.mailer'])) return $this->shared['swiftmailer.mailer'];

$instance = new Swift_Mailer($this->getSwiftmailer_Transport_SmtpService());

return $this->shared['swiftmailer.mailer'] = $instance; }

PHPDoc for auto-completion

As fast as it could be

The DIC can manage ANY PHP object (POPO)

Plugins…

or Bundles

Plugins are first-class citizens They are called Bundles

Everything is a bundle Core features

Third-party code Application code

app/ src/ web/

app/ AppKernel.php cache/ config/ console logs/

src/ autoload.php Application/ Bundle/ vendor/ doctrine/ swiftmailer/ symfony/ zend/

web/ index.php index_dev.php

.../ SomeBundle/ Bundle.php Controller/ Model/ Resources/ config/ views/

public function registerBundleDirs() { return array( 'Application' => __DIR__.'/../src/Application', 'Bundle' => __DIR__.'/../src/Bundle', 'Symfony\\Framework' => __DIR__.'/../src/vendor/symfony/src/Symfony/Framework', ); }

$this->render('SomeBundle:Hello:index', $params)

hello: pattern: /hello/:name defaults: { _bundle: SomeBundle, ... }

SomeBundle can be any of

Application\SomeBundle Bundle\SomeBundle Symfony\Framework\SomeBundle

Less concepts… but more powerful ones

symfony 1 View Layer templates

layouts slots

components partials

component slots

Symfony 2 View Layer

templates slots

A layout is just another template with _content as a special slot

A partial is just a template you embed in another one

A component is just another action embedded in a template

<?php $view->output('BlogBundle:Post:list', array('posts' => $posts)) ?>

<?php $view->actions->output('BlogBundle:Post:list', array('limit' => 2)) ?>

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <?php $view->slots->output('_content') ?> </body> </html>

Big and Small Improvements

multiple level of layouts

partials can be decorated!

Better Logs

INFO: Matched route "blog_home" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'index', '_route' => 'blog_home',))

INFO: Using controller "Bundle\BlogBundle\Controller\PostController::indexAction"

INFO: SELECT s0_.id AS id0, s0_.title AS title1, s0_.html_body AS html_body2, s0_.excerpt AS excerpt3, s0_.published_at AS published_at4 FROM sf_weblog_post s0_ ORDER BY s0_.published_at DESC LIMIT 10 (array ())

INFO: Matched route "blog_post" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'show', '_format' => 'html', 'id' => '3456', '_route' => 'blog_post',))

INFO: Using controller "Bundle\BlogBundle\Controller\PostController::showAction »

INFO: SELECT s0_.id AS id0, s0_.title AS title1, s0_.html_body AS html_body2, s0_.excerpt AS excerpt3, s0_.published_at AS published_at4 FROM sf_weblog_post s0_ WHERE s0_.id = ? (array ( 0 => '3456',)) ERR: Post "3456" not found! (No result was found for query although at least one row was expected.) (uncaught Symfony\Components\RequestHandler\Exception\NotFoundHttpException exception)

INFO: Using controller "Symfony\Framework\WebBundle\Controller\ExceptionController::exceptionAction"

<zend:logger priority="debug" />

DEBUG: Notifying (until) event "core.request" to listener "(Symfony\Framework\WebBundle\Listener\RequestParser, resolve)" INFO: Matched route "blog_post" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'show', '_format' => 'html', 'id' => '3456', '_route' => 'blog_post',)) DEBUG: Notifying (until) event "core.load_controller" to listener "(Symfony\Framework\WebBundle\Listener\ControllerLoader, resolve)" INFO: Using controller "Bundle\BlogBundle\Controller\PostController::showAction" DEBUG: Listener "(Symfony\Framework\WebBundle\Listener\ControllerLoader, resolve)" processed the event "core.load_controller" INFO: Trying to get post "3456" from database INFO: SELECT s0_.id AS id0, s0_.title AS title1, s0_.html_body AS html_body2, s0_.excerpt AS excerpt3, s0_.published_at AS published_at4 FROM sf_weblog_post s0_ WHERE s0_.id = ? (array ( 0 => '3456',)) DEBUG: Notifying (until) event "core.exception" to listener "(Symfony\Framework\WebBundle\Listener\ExceptionHandler, handle)" ERR: Post "3456" not found! (No result was found for query although at least one row was expected.) (uncaught Symfony\Components\RequestHandler\Exception\NotFoundHttpException exception) DEBUG: Notifying (until) event "core.request" to listener "(Symfony\Framework\WebBundle\Listener\RequestParser, resolve)" DEBUG: Notifying (until) event "core.load_controller" to listener "(Symfony\Framework\WebBundle\Listener\ControllerLoader, resolve)" INFO: Using controller "Symfony\Framework\WebBundle\Controller\ExceptionController::exceptionAction" DEBUG: Listener "(Symfony\Framework\WebBundle\Listener\ControllerLoader, resolve)" processed the event "core.load_controller" DEBUG: Notifying (filter) event "core.response" to listener "(Symfony\Framework\WebBundle\Listener\ResponseFilter, filter)" DEBUG: Notifying (filter) event "core.response" to listener "(Symfony\Framework\WebBundle\Debug\DataCollector\DataCollectorManager, handle)" DEBUG: Notifying (filter) event "core.response" to listener "(Symfony\Framework\WebBundle\Debug\WebDebugToolbar, handle)" DEBUG: Listener "(Symfony\Framework\WebBundle\Listener\ExceptionHandler, handle)" processed the event "core.exception" DEBUG: Notifying (filter) event "core.response" to listener "(Symfony\Framework\WebBundle\Listener\ResponseFilter, filter)" DEBUG: Notifying (filter) event "core.response" to listener "(Symfony\Framework\WebBundle\Debug\DataCollector\DataCollectorManager, handle)" DEBUG: Notifying (filter) event "core.response" to listener "(Symfony\Framework\WebBundle\Debug\WebDebugToolbar, handle)"

Even Better Exception Error Pages

An Event Better Web Debug Toolbar

Everything you need is at the bottom of the screen

Web Designer “friendly”

app/ views/ BlogBundle/ Post/ index.php AdminGeneratorBundle/ DefaultTheme/ list.php edit.php ...

“Mount” Routing Configuration

blog: resource: BlogBundle/Resources/config/routing.yml

forum: resource: ForumBundle/Resources/config/routing.yml prefix: /forum

Symfony 2 is a lazy framework

Smart Autoloading

require_once __DIR__.'/vendor/symfony/src/Symfony/Foundation/UniversalClassLoader.php';

use Symfony\Foundation\UniversalClassLoader;

$loader = new UniversalClassLoader(); $loader->registerNamespaces(array( 'Symfony' => __DIR__.'/vendor/symfony/src', 'Application' => __DIR__, 'Bundle' => __DIR__, 'Doctrine' => __DIR__.'/vendor/doctrine/lib', )); $loader->registerPrefixes(array( 'Swift_' => __DIR__.'/vendor/swiftmailer/lib/classes', 'Zend_' => __DIR__.'/vendor/zend/library', )); $loader->register();

// for Zend Framework & SwiftMailer set_include_path(__DIR__.'/vendor/zend/library'.PATH_SEPARATOR.__DIR__.'/vendor/swiftmailer/lib'.PATH_SEPARATOR.get_include_path());

lazy-loading of services

lazy-loading of listeners

lazy-loading of helpers

<?php echo $view->router->generate('blog_post', array('id' => $post->getId())) ?>

Symfony 2 is a “cachy” framework

blog/ cache/ prod/ blogProjectContainer.php blogUrlGenerator.php blogUrlMatcher.php classes.php

class blogUrlMatcher extends Symfony\Components\Routing\Matcher\UrlMatcher { public function __construct(array $context = array(), array $defaults = array()) { $this->context = $context; $this->defaults = $defaults; }

public function match($url) { $url = $this->normalizeUrl($url);

if (0 === strpos($url, '/webblog') && preg_match('#^/webblog/(?P<id>[^/\.]+?)$#x', $url, $matches)) return array_merge($this->mergeDefaults($matches, array ( '_bundle' => 'WebBundle', '_controller' => 'Redirect', '_action' => 'redirect', 'route' => 'blog_post',)), array('_route' => 'old_blog_post_redirect'));

You can use Apache for Routing matching

A Very Fast Dev. Env.

blog/ cache/ dev/ blogProjectContainer.meta blogProjectContainer.php blogUrlGenerator.meta blogUrlGenerator.php blogUrlMatcher.meta blogUrlMatcher.php classes.meta classes.php prod/ blogProjectContainer.php blogUrlGenerator.php blogUrlMatcher.php classes.php

Symfony 2

Easy to learn Easy to use

Extensible at will

Easy to learn Easy to use

Extensible at will

But Symfony 2 should be slow, right?

Fast as hell

Benchmark on a simple application

2x faster than

Solar 1.0.0

2.5x faster than

symfony 1.4.2

3x faster than

Zend Framework 1.10

4x faster than

Lithium

6x faster than

CakePHP 1.2.6

60x faster than

Flow3

…and Symfony 2.0 uses half the memory

needed by both symfony 1 and ZF

We have barely scratched the surface of all the goodness of

Symfony 2.0

Controller except for the nice default pages Autoloading Cache via ZF - DI extension coming soon CLI commands still missing Configuration Database via Doctrine DBAL Debug except Timer and extended WDT Escaper Event Dispatcher Form / Validation / Widget can use the 1.4 version as is Admin Generator Helpers I18n / L10n can use the 1.4 version as is Logger via ZF Mailer except commands Bundles except installing Doctrine Plugin just the DBAL part Propel Plugin Request / Response Routing no REST support, no Object support Storage / User Test View

Final Release Target Date Late 2010

If you want the bleeding edge of news, follow me

on Twitter @fabpot on Github github.com/fabpot

http://symfony-reloaded.org/

Questions?

My slides will be available on http://slideshare.com/fabpot

Sensio S.A. 92-98, boulevard Victor Hugo

92 115 Clichy Cedex FRANCE

Tél. : +33 1 40 99 80 80

Contact Fabien Potencier

fabien.potencier at sensio.com

http://www.sensiolabs.com/

http://www.symfony-project.org/

http://fabien.potencier.org/

top related