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                             Everything is namespaced
namespace ApplicationHelloBundleController;

use SymfonyFrameworkWebBundleController;

class HelloController extends Controller
{
  public function indexAction($name) Variables come from the routing
  {
    return $this->render('HelloBundle:Hello:index', array('name' => $name));
  }
}
                          Template name                      Variables to pass
                                                             to the template
Layout

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

Hello <?php echo $name ?>!
<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 ApplicationHelloBundleController;

class HelloController extends Controller
{
  public function indexAction($name)
  {
    // ...
  }
}
hello:
  pattern: /hello/:name
  defaults:
    _bundle:     HelloBundle
    _controller: Hello
    _action:     index

namespace ApplicationHelloBundleController;

class HelloController extends Controller
{
  public function indexAction($name)
  {
    // ...
  }
}
hello:
  pattern: /hello/:name
  defaults:
    _bundle:     HelloBundle
    _controller: Hello
    _action:     index

namespace ApplicationHelloBundleController;

class HelloController extends Controller
{
  public function indexAction($name)
  {
    // ...
  }
}
hello:
  pattern: /hello/:name
  defaults:
    _bundle:     HelloBundle
    _controller: Hello
    _action:     index

namespace ApplicationHelloBundleController;

class HelloController extends Controller
{
  public function indexAction($name)
  {
    // ...
  }
}
hello:
  pattern: /hello/:name
  defaults:
    _bundle:     HelloBundle
    _controller: Hello
    _action:     index

namespace ApplicationHelloBundleController;

class HelloController extends Controller
{
  public function indexAction($name)
  {
    // ...
  }
}
hello:
  pattern: /hello/:year/:month/:slug
  defaults:
    _bundle:     HelloBundle
    _controller: Hello
    _action:     index

namespace ApplicationHelloBundleController;

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" />
                                     Mix and match formats
  <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"
/>
You choose the format you want

                       Pros                    Cons
 XML                   validation              verbose (not that much)
                       IDE completion & help

 YAML                  concise                 needs the YAML component
                       simple to read          no validation
                       easy to change          no IDE auto-completion
 PHP                   flexible                 no validation
                       more expressive
Store sensitive settings
 outside of your project
<doctrine:dbal
   dbname="sfweb"
   username="root"
   password="SuperSecretPasswordThatAnyoneCanSee"
/>
in a .htaccess or httpd.conf file

SetEnv SYMFONY__DOCTRINE__DBAL__PASSWORD "foobar"
                             %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>


                                                                                                                         XML	
  
  </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>
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.
                                                    PHPDoc for auto-completion
  */
protected function getSwiftmailer_MailerService()
{
    if (isset($this->shared['swiftmailer.mailer']))
      return $this->shared['swiftmailer.mailer'];    As fast as it could be
    $instance = new Swift_Mailer($this->getSwiftmailer_Transport_SmtpService());

    return $this->shared['swiftmailer.mailer'] = $instance;
}
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',
     'SymfonyFramework' => __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


ApplicationSomeBundle
BundleSomeBundle
SymfonyFrameworkSomeBundle
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 "BundleBlogBundleController
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 "BundleBlogBundleController
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 SymfonyComponentsRequestHandlerException
NotFoundHttpException exception)

INFO: Using controller "SymfonyFrameworkWebBundleController
ExceptionController::exceptionAction"
<zend:logger priority="debug" />
DEBUG: Notifying (until) event "core.request" to listener "(SymfonyFrameworkWebBundleListenerRequestParser, 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 "(SymfonyFrameworkWebBundleListenerControllerLoader,
resolve)"
INFO: Using controller "BundleBlogBundleControllerPostController::showAction"
DEBUG: Listener "(SymfonyFrameworkWebBundleListenerControllerLoader, 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 "(SymfonyFrameworkWebBundleListenerExceptionHandler, handle)"
ERR: Post "3456" not found! (No result was found for query although at least one row was expected.) (uncaught SymfonyComponents
RequestHandlerExceptionNotFoundHttpException exception)
DEBUG: Notifying (until) event "core.request" to listener "(SymfonyFrameworkWebBundleListenerRequestParser, resolve)"
DEBUG: Notifying (until) event "core.load_controller" to listener "(SymfonyFrameworkWebBundleListenerControllerLoader,
resolve)"
INFO: Using controller "SymfonyFrameworkWebBundleControllerExceptionController::exceptionAction"
DEBUG: Listener "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" processed the event "core.load_controller"
DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleListenerResponseFilter, filter)"
DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugDataCollector
DataCollectorManager, handle)"
DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugWebDebugToolbar, handle)"
DEBUG: Listener "(SymfonyFrameworkWebBundleListenerExceptionHandler, handle)" processed the event "core.exception"
DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleListenerResponseFilter, filter)"
DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugDataCollector
DataCollectorManager, handle)"
DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugWebDebugToolbar, 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 SymfonyFoundationUniversalClassLoader;

$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 SymfonyComponentsRoutingMatcherUrlMatcher
{
  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/

Symfony2 revealed

  • 1.
  • 2.
    A bit ofhistory
  • 3.
    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, …
  • 4.
    symfony 1.2 –November 2008 •  Based on decoupled but cohesive components –  Forms, Routing, Cache, YAML, ORMs, … •  Controller still based on Mojavi –  View, Filter Chain, …
  • 5.
    symfony 1.4 –November 2009 •  Added some polish on existing features •  Removed the support for deprecated features •  Current LTS release, maintained until late 2012
  • 6.
    Symfony Components YAML Dependency Injection Container Event Dispatcher Templating Routing Console Output Escaper Request Handler …
  • 7.
  • 8.
    Symfony 2 isthe next version of the symfony framework… except Symfony now takes a S instead of a s
  • 9.
  • 10.
    To make itclear: Symfony 1 does not make any sense
  • 11.
    symfony 2 does notmake more sense
  • 12.
  • 13.
    Same philosophy, just better
  • 14.
  • 15.
    hmmm, now that Ithink about it…
  • 16.
    …it’s now probablymore a Fabien’s style framework than anything else
  • 17.
    Highly configurable Highly extensible Same Symfony Components Same great developer tools Full-featured
  • 18.
    Ok, but whya major version then?
  • 19.
    Symfony 2 has a brand new low-level architecture
  • 20.
  • 21.
  • 22.
    <?php require_once __DIR__.'/../blog/BlogKernel.php'; $kernel =new BlogKernel('prod', false); $kernel->run();
  • 23.
    <?php Everything is namespaced namespace ApplicationHelloBundleController; use SymfonyFrameworkWebBundleController; class HelloController extends Controller { public function indexAction($name) Variables come from the routing { return $this->render('HelloBundle:Hello:index', array('name' => $name)); } } Template name Variables to pass to the template
  • 24.
  • 25.
    <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
  • 26.
    hello: pattern:/hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index
  • 27.
    hello: pattern:/hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 28.
    hello: pattern:/hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 29.
    hello: pattern:/hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 30.
    hello: pattern:/hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 31.
    hello: pattern:/hello/:name defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($name) { // ... } }
  • 32.
    hello: pattern:/hello/:year/:month/:slug defaults: _bundle: HelloBundle _controller: Hello _action: index namespace ApplicationHelloBundleController; class HelloController extends Controller { public function indexAction($slug, $year) { // ... } }
  • 33.
  • 34.
  • 35.
    Replaces a lotof symfony 1 “things” sfConfig All config handlers sfProjectConfiguration / sfApplicationConfiguration sfContext (No Singleton anymore) The configuration cache system … and some more
  • 36.
    in one easy-to-master unified and cohesive package
  • 37.
    Thanks to theDIC, Configuration has never been so easy and so flexible
  • 38.
    Name your configurationfiles the way you want
  • 39.
  • 40.
    Use PHP, XML,YAML, or INI
  • 41.
    $configuration = newBuilderConfiguration(); $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   ;
  • 42.
    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  
  • 43.
    <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  
  • 44.
    $configuration->mergeExtension('swift.mailer', array( 'transport' => 'gmail', 'username' => 'fabien.potencier', 'password' => 'xxxxxx', )); PHP  
  • 45.
    swift.mailer: transport:gmail username: fabien.potencier password: xxxxxxxx YAML  
  • 46.
    <swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" /> XML  
  • 47.
    <?xml version="1.0" ?> <containerxmlns="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  
  • 48.
    <?xml version="1.0" ?> <containerxmlns="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  
  • 50.
    Inherit them asmuch as you want
  • 51.
    Mix and match configurationfiles written in any format useful when using third-party plugins
  • 52.
    <imports> <importresource="parent.xml" /> <import resource="config.yml" /> Mix and match formats <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" />
  • 53.
    You choose theformat you want Pros Cons XML validation verbose (not that much) IDE completion & help YAML concise needs the YAML component simple to read no validation easy to change no IDE auto-completion PHP flexible no validation more expressive
  • 54.
    Store sensitive settings outside of your project
  • 55.
    <doctrine:dbal dbname="sfweb" username="root" password="SuperSecretPasswordThatAnyoneCanSee" />
  • 56.
    in a .htaccessor httpd.conf file SetEnv SYMFONY__DOCTRINE__DBAL__PASSWORD "foobar" %doctrine.dbal.password%
  • 57.
  • 58.
    <swift:mailer transport="gmail" username="fabien.potencier" password="xxxxxxxx" /> XML  
  • 59.
    <swift:mailer transport="smtp" encryption="ssl" auth_mode="login" host="smtp.gmail.com" username="fabien.potencier" password="xxxxxxxx" /> XML  
  • 60.
    <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> XML   </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>
  • 61.
    Creating DIC extensions is insanely simple
  • 62.
    Very Fast thanks to a Smart Caching mechanism it always knows when to flush the cache
  • 63.
    /** *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. PHPDoc for auto-completion */ protected function getSwiftmailer_MailerService() { if (isset($this->shared['swiftmailer.mailer'])) return $this->shared['swiftmailer.mailer']; As fast as it could be $instance = new Swift_Mailer($this->getSwiftmailer_Transport_SmtpService()); return $this->shared['swiftmailer.mailer'] = $instance; }
  • 64.
    The DIC canmanage ANY PHP object (POPO)
  • 65.
  • 66.
  • 67.
    Plugins are first-classcitizens They are called Bundles
  • 68.
    Everything is abundle Core features Third-party code Application code
  • 69.
  • 70.
    app/ AppKernel.php cache/ config/ console logs/
  • 71.
    src/ autoload.php Application/ Bundle/ vendor/ doctrine/ swiftmailer/ symfony/ zend/
  • 72.
    web/ index.php index_dev.php
  • 73.
    .../ SomeBundle/ Bundle.php Controller/ Model/ Resources/ config/ views/
  • 74.
    public function registerBundleDirs() { return array( 'Application' => __DIR__.'/../src/Application', 'Bundle' => __DIR__.'/../src/Bundle', 'SymfonyFramework' => __DIR__.'/../src/vendor/ symfony/src/Symfony/Framework', ); }
  • 75.
  • 76.
    hello: pattern:/hello/:name defaults: { _bundle: SomeBundle, ... }
  • 77.
    SomeBundle can beany of ApplicationSomeBundle BundleSomeBundle SymfonyFrameworkSomeBundle
  • 78.
  • 79.
    symfony 1 ViewLayer templates layouts slots components partials component slots
  • 80.
    Symfony 2 ViewLayer templates slots
  • 81.
    A layout isjust 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
  • 82.
  • 83.
  • 84.
    <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <?php $view->slots->output('_content') ?> </body> </html>
  • 85.
  • 86.
  • 87.
    partials can bedecorated!
  • 88.
  • 89.
    INFO: Matched route"blog_home" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'index', '_route' => 'blog_home',)) INFO: Using controller "BundleBlogBundleController 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 ())
  • 90.
    INFO: Matched route"blog_post" (parameters: array ( '_bundle' => 'BlogBundle', '_controller' => 'Post', '_action' => 'show', '_format' => 'html', 'id' => '3456', '_route' => 'blog_post',)) INFO: Using controller "BundleBlogBundleController 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 SymfonyComponentsRequestHandlerException NotFoundHttpException exception) INFO: Using controller "SymfonyFrameworkWebBundleController ExceptionController::exceptionAction"
  • 91.
  • 92.
    DEBUG: Notifying (until)event "core.request" to listener "(SymfonyFrameworkWebBundleListenerRequestParser, 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 "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" INFO: Using controller "BundleBlogBundleControllerPostController::showAction" DEBUG: Listener "(SymfonyFrameworkWebBundleListenerControllerLoader, 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 "(SymfonyFrameworkWebBundleListenerExceptionHandler, handle)" ERR: Post "3456" not found! (No result was found for query although at least one row was expected.) (uncaught SymfonyComponents RequestHandlerExceptionNotFoundHttpException exception) DEBUG: Notifying (until) event "core.request" to listener "(SymfonyFrameworkWebBundleListenerRequestParser, resolve)" DEBUG: Notifying (until) event "core.load_controller" to listener "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" INFO: Using controller "SymfonyFrameworkWebBundleControllerExceptionController::exceptionAction" DEBUG: Listener "(SymfonyFrameworkWebBundleListenerControllerLoader, resolve)" processed the event "core.load_controller" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleListenerResponseFilter, filter)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugDataCollector DataCollectorManager, handle)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugWebDebugToolbar, handle)" DEBUG: Listener "(SymfonyFrameworkWebBundleListenerExceptionHandler, handle)" processed the event "core.exception" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleListenerResponseFilter, filter)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugDataCollector DataCollectorManager, handle)" DEBUG: Notifying (filter) event "core.response" to listener "(SymfonyFrameworkWebBundleDebugWebDebugToolbar, handle)"
  • 93.
  • 95.
    An Event Better WebDebug Toolbar
  • 96.
    Everything you needis at the bottom of the screen
  • 97.
  • 98.
    app/ views/ BlogBundle/ Post/ index.php AdminGeneratorBundle/ DefaultTheme/ list.php edit.php ...
  • 99.
    “Mount” Routing Configuration
  • 100.
    blog: resource:BlogBundle/Resources/config/routing.yml forum: resource: ForumBundle/Resources/config/routing.yml prefix: /forum
  • 101.
    Symfony 2 is alazy framework
  • 102.
  • 103.
    require_once __DIR__.'/vendor/symfony/src/Symfony/Foundation/UniversalClassLoader.php'; use SymfonyFoundationUniversalClassLoader; $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());
  • 104.
  • 105.
  • 106.
  • 107.
    <?php echo $view->router->generate('blog_post',array('id' => $post->getId())) ?>
  • 108.
    Symfony 2 is a“cachy” framework
  • 109.
    blog/ cache/ prod/ blogProjectContainer.php blogUrlGenerator.php blogUrlMatcher.php classes.php
  • 110.
    class blogUrlMatcher extendsSymfonyComponentsRoutingMatcherUrlMatcher { 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'));
  • 111.
    You can useApache for Routing matching
  • 112.
    A Very Fast Dev. Env.
  • 113.
    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
  • 114.
  • 115.
    Easy to learn Easy to use Extensible at will
  • 116.
    Easy to learn Easy to use
  • 117.
  • 118.
    But Symfony 2should be slow, right?
  • 119.
  • 120.
  • 121.
    2x faster than Solar 1.0.0
  • 122.
    2.5x faster than symfony 1.4.2
  • 123.
    3x faster than Zend Framework 1.10
  • 124.
    4x faster than Lithium
  • 125.
    6x faster than CakePHP 1.2.6
  • 126.
    60x faster than Flow3
  • 127.
    …and Symfony 2.0uses half the memory needed by both symfony 1 and ZF
  • 128.
    We have barelyscratched the surface of all the goodness of Symfony 2.0
  • 129.
    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
  • 130.
    Final Release TargetDate Late 2010
  • 131.
    If you wantthe bleeding edge of news, follow me on Twitter @fabpot on Github github.com/fabpot
  • 132.
  • 133.
  • 134.
    Questions? My slides willbe available on http://slideshare.com/fabpot
  • 135.
    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/