KEMBAR78
Symfony 2 (PHP day 2009) | PDF
Symfony 2

 Fabien Potencier
Who am I?
•  Founder of Sensio
   –  Web Agency (France and USA)
   –  Since 1998
   –  70 people
   –  Open-Source Specialists
   –  Big corporate customers
   –  Consulting, training, development, web design, …
   –  Main sponsor of symfony and Doctrine


•  Creator and lead developer of symfony
How many of you have used symfony?
         1.0? 1.1? 1.2?
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


•  Decoupled but cohesive components: the symfony platform

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

•  Controller still based on Mojavi

   –  View, Filter Chain, …
Roadmap
•  1.0 – January 2007
•  1.1 – June 2008
•  1.2 – November 2008
•  1.3 – November 2009
•  Version 2.0 …
•  1.4 – Last 1.X version
  – same as 1.3 but with all deprecated features removed
symfony platform                                 >= 1.1
                                                                                  2.0


    sfRequest    sfRouting     sfLogger   sfI18N    sfUser      sfResponse




sfYAML   sfDatabase   sfForm    sfEventDispatcher   sfStorage     sfCache    sfOutputEscaper



             sfValidator     sfWidget                            sfCoreAutoload
                                                                                   platform
Symfony Components
•  Standalone components
•  Packaged individually
•  Upcoming dedicated website
    –  http://components.symfony-project.org/
•  Dedicated section for each component
•  Dedicated documentation
•  Dedicated Subversion repository
    –  http://svn.symfony-project.com/components/
•  Git mirror
    –  http://github.com/fabpot
•  Already published components:
    –  YAML, Dependency Injection, Event Dispatcher
Symfony 2 is an evolution of symfony 1


•  Same Symfony platform / components

•  Different controller implementation

•  Oh! Symfony now takes a capital S!!!
Symfony 2 main goals


   Flexibility
       Fast
     Smart
Symfony 2: New components


Dependency Injection Container
   Templating Framework
     Controller Handling
Symfony 2
•  Not yet available as a full-stack MVC framework
•  Some components have already been merged into Symfony 1
   –  Event Dispatcher
   –  Form Framework
•  Other new components will soon be released as standalone
   components:
   –  Controller Handling
   –  Templating Framework
   –  Dependency Injection Container (done)
symfony 1: Not fast enough?
symfony 1 is
  one of the slowest framework
     when you test it against
a simple Hello World application
1644&

1544&          !"#$%&!'!&

1344&

1144&

1444&

 ;44&

 :44&
                                                                      x 19.5
 944&

 844&


        Hello World
 744&

 644&

 544&    Benchmark
 344&
                                     ()"#*&
 144&                                                                  x 2.3
                                                           +,&           -./0)%.&123&
   4&
               based on numbers from http://paul-m-jones.com/?p=315
Conclusion?
Don’t use symfony
for your next « Hello World » website

            Use PHP ;)
By the way,

      the fastest implemention
of a Hello World application with PHP:

  die('Hello World');
But symfony 1 is probably fast enough
        for your next website
… anyway, it is fast enough for Yahoo!

Yahoo! Bookmarks      sf-to.org/bookmarks

Yahoo! Answers        sf-to.org/answers

delicious.com         sf-to.org/delicious
… and recently
dailymotion.com announced
  its migration to Symfony

 sf-to.org/dailymotion
Second most popular video sharing website
  One of the top 50 websites in the world
     42 million unique users in December
…and of course
many other smaller websites…
Symfony 2: Faster?
Symfony 2 core is so light and flexible
   that you can easily customize it
  to have outstanding performance
     for a Hello World application
Symfony 2 core is so light and flexible
      that its raw performance
            is outstanding
require_once dirname(__FILE__).'/sf20/autoload2/sfCore2Autoload.class.php';
sfCore2Autoload::register();

$app = new HelloApplication();
$app->run()->send();

class HelloApplication
{
  public function __construct()
  {
    $this->dispatcher = new sfEventDispatcher();
    $this->dispatcher->connect('application.load_controller', array($this, 'loadController'));
  }

    public function run()


                                                                                               Hello World
    {
      $request = new sfWebRequest($this->dispatcher);


                                                                                                  Symfony 2.0
      $handler = new sfRequestHandler($this->dispatcher);

                                                                                             with
      $response = $handler->handle($request);

        return $response;
    }

    public function loadController(sfEvent $event)
    {
      $event->setReturnValue(array(array($this, 'hello'), array($this->dispatcher, $event['request'])));

        return true;
    }

    public function hello($dispatcher, $request)
    {
      $response = new sfWebResponse($dispatcher);
      $response->setContent('Hello World');

        return $response;
    }
}
7922&

7822&   !"#$%&!'!&
                                                                        Hello World
                      ()$*+&
7022&
                                                                         Benchmark
7722&

7222&

 >22&

 =22&

 <22&

 ;22&

 :22&
                                        ,-./+%-&012&
 922&
                                                         x 3                  x 7
 822&

 022&
                                                         3+"#4&
 722&                                                                   56&         ,-./+%-&710&
   2&
                 based on numbers from http://paul-m-jones.com/?p=315
7 times faster ?!



You won’t have such a difference for real applications
       as most of the time, the limiting factor
             is not the framework itself
Twitto ?!
Twitto: The PHP framework that fits in a tweet
•  The fastest framework around?
•  Uses some PHP 5.3 new features
•  It also fits in a slide…

require __DIR__.'/c.php';
if (!is_callable($c = @$_GET['c'] ?:
function() { echo 'Woah!'; }))
  throw new Exception('Error');
$c();                            twitt o.org
Don’t use Twitto for your next website
            It is a joke ;)
7 times faster ?!
•  But raw speed matters because
   – It demonstrates that the core « kernel » is very light
   – It allows you to use several Symfony frameworks within a single
     application with the same behavior but different optimizations:

      •  One full-stack framework optimized for ease of use (think symfony 1)

      •  One light framework optimized for speed (think Rails Metal ;))
symfony platform                                            2.0



sfRequestHandler       sfRequest    sfRouting   sfLogger   sfI18N   sfUser    sfTemplate   sfResponse




sfValidator   sfForm     sfWidget    sfCache    sfDatabase    sfStorage      sfYAML   sfOutputEscaper



                           sfServiceContainer      sfEventDispatcher      sfCoreAutoload
                                                                                            platform
Symfony 2 kernel:

The Request Handler
Symfony 2 secret weapon:

  The Request Handler
The Request Handler
•  The backbone of Symfony 2 controller implementation
•  Class to build web frameworks, not only MVC ones
•  Based on a simple assumption:
   –  The input is a request object
   –  The output is a response object
•  The request object can be anything you want
•  The response object must implement a send() method

        Web Request                                  Web Response
                                  Request Handler
The Request Handler


$handler = new sfRequestHandler($dispatcher);

$request = new sfWebRequest($dispatcher);
$response = $handler->handle($request);

$response->send();
The Request Handler
•  The sfRequestHandler does several things:
   –  Notify events

   –  Execute a callable (the controller)

   –  Ensure that the Request is converted to a Response object

•  The framework is responsible for choosing the controller
•  The controller is responsible for the conversion of the Request to a
   Response
class sfRequestHandler
{
  protected $dispatcher = null;

    public function __construct(sfEventDispatcher $dispatcher)
    {
      $this->dispatcher = $dispatcher;
    }




                                                                                                                                                                                           sfRequestHandler
    public function handle($request)
    {
      try
      {
        return $this->handleRaw($request);




                                                                                                                                                                                            s less than 100
      }
      catch (Exception $e)



                                                                                                                                                                                           i
      {
        $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.exception', array('request' => $request, 'exception' => $e)));
        if ($event->isProcessed())
        {




                                                                                                                                                                                             lines of PHP
          return $this->filterResponse($event->getReturnValue(), 'An "application.exception" listener returned a non response object.');
        }

            throw $e;
        }
    }




                                                                                                                                                                                                 code!
    public function handleRaw($request)
    {
      $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.request', array('request' => $request)));
      if ($event->isProcessed())
      {
        return $this->filterResponse($event->getReturnValue(), 'An "application.request" listener returned a non response object.');
      }

        $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.load_controller', array('request' => $request)));
        if (!$event->isProcessed())
        {
          throw new Exception('Unable to load the controller.');
        }

        list($controller, $arguments) = $event->getReturnValue();

        if (!is_callable($controller))
        {
          throw new Exception(sprintf('The controller must be a callable (%s).', var_export($controller, true)));
        }

        $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.controller', array('request' => $request, 'controller' => &$controller, 'arguments' => &$arguments)));
        if ($event->isProcessed())
        {
          try
          {
             return $this->filterResponse($event->getReturnValue(), 'An "application.controller" listener returned a non response object.');
          }
          catch (Exception $e)
          {
            $retval = $event->getReturnValue();
          }
        }
        else
        {
          $retval = call_user_func_array($controller, $arguments);
        }

        $event = $this->dispatcher->filter(new sfEvent($this, 'application.view'), $retval);

        return $this->filterResponse($event->getReturnValue(), sprintf('The controller must return a response (instead of %s).', is_object($event->getReturnValue()) ? 'an object of class '.get_class($event->getReturnValue()) : (string) $event->getReturnValue()));
    }

    protected function filterResponse($response, $message)
    {
      if (!is_object($response) || !method_exists($response, 'send'))
      {
        throw new RuntimeException($message);
      }

        $event = $this->dispatcher->filter(new sfEvent($this, 'application.response'), $response);
        $response = $event->getReturnValue();

        if (!is_object($response) || !method_exists($response, 'send'))
        {
          throw new RuntimeException('An "application.response" listener returned a non response object.');
        }

        return $response;
    }
}
Request Handler Events
application.request

application.load_controller

application.controller

application.view

application.response

application.exception
application.response




As the very last event notified, a listener can modify the
   Response object just before it is returned to the user
application.request



•  The very first event notified

•  It can act as a short-circuit event

•  If one listener returns a Response object, it stops the processing
application.load_controller



•  Only event for which at least one listener must be connected to

•  A listener must return
   –  A PHP callable (the controller)
   –  The arguments to pass to the callable
application.view




The controller must return a Response object
        except if a listener can convert
  the controller return value to a Response
application.exception




The request handler catches all exceptions
       and give a chance to listeners
        to return a Response object
Request Handler
•  Several listeners can be attached to a single event
•  Listeners are called in turn

                                                    sfEventDispatcher

                                  load_controller



                                                    controller




                                                                                    response
                       request




                                                                 view
                                                                        exception


         request                                    sfRequestHandler                           response
require_once dirname(__FILE__).'/sf20/autoload2/sfCore2Autoload.class.php';
sfCore2Autoload::register();

$app = new HelloApplication();
$app->run()->send();

class HelloApplication
{
  public function __construct()
  {
    $this->dispatcher = new sfEventDispatcher();
    $this->dispatcher->connect('application.load_controller', array($this, 'loadController'));
  }



                                                                            Hello World
    public function run()
    {

                                                                                  y Components
      $request = new sfWebRequest($this->dispatcher);

                                                                      with Symfon
      $handler = new sfRequestHandler($this->dispatcher);
      $response = $handler->handle($request);
      return $response;
    }

    public function loadController(sfEvent $event)
    {
      $event->setReturnValue(array(array($this, 'hello'), array($this->dispatcher, $event['request'])));
      return true;
    }

    public function hello($dispatcher, $request)
    {
      $response = new sfWebResponse($dispatcher);
      $response->setContent('Hello World');
      return $response;
    }
}
require_once '/path/to/sfCore2Autoload.class.php';
sfCore2Autoload::register();
$app = new HelloApplication();
$app->run()->send();
public function __construct()
 {
   $this->dispatcher = new sfEventDispatcher();
   $this->dispatcher->connect(
      'application.load_controller',
      array($this, 'loadController')
   );
 }

                                                  sfEventDispatcher




                                load_controller



                                                  controller




                                                                                  response
                      request




                                                               view
                                                                      exception


            request                               sfRequestHandler                           response
public function loadController(sfEvent $event)
 {
   $event->setReturnValue(array(
    array($this, 'hello'),
    array($this->dispatcher, $event['request'])
   ));

     return true;
 }
public function hello($dispatcher, $request)
 {
   $response = new sfWebResponse($dispatcher);
   $response->setContent('Hello World');

    return $response;
}
public function run()
 {
   $request = new sfWebRequest($this->dispatcher);
   $handler = new sfRequestHandler($this->dispatcher);
   $response = $handler->handle($request);

     return $response;
 }
Case study: dailymotion.com
•  The problem: the Dailymotion developers add new features on a nearly
   everyday basis
•  The challenge: Migrate by introducing small doses of Symfony
   goodness
•  The process
   –  Wrap everything with sfRequestHandler by implementing an
      application.load_controller listener that calls the old code, based on the
      request
   –  Migrate the mod_rewrite rules to the symfony routing
   –  Add unit and functional tests
Symfony 2: The Templating Framework
New Templating Framework
•  4 components
   –  Template Engine
   –  Template Renderers
   –  Template Loaders
   –  Template Storages



•  Independant library
require_once '/path/to/sfCore2Autoload.class.php';
sfCore2Autoload::register();

$dispatcher = new sfEventDispatcher();

$loader = new sfTemplateLoaderFilesystem($dispatcher,
  '/path/to/templates/%s.php');

$t = new sfTemplateEngine($dispatcher, $loader);

echo $t->render('index', array('name' => 'Fabien'));
Template Loaders
•  No assumption about where and how templates are to be found
   –  Filesystem
   –  Database
   –  Memory
   –  …
•  Template names are « logical » names:

  $loader = new sfTemplateLoaderFilesystem($dispatcher,
               '/path/to/templates/%s.php');
Template Renderers
•  No assumption about the format of the templates
•  Template names are prefixed with the renderer name:
   –  index == php:index
   –  user:index

$t = new sfTemplateEngine($dispatcher, $loader, array(
  'user' => new ProjectTemplateRenderer($dispatcher),
  'php' => new sfTemplateRendererPhp($dispatcher),
));
Template Embedding



Hello <?php echo $name ?>

<?php $this->render('embedded', array('name' => $name)) ?>

<?php $this->render('smarty:embedded') ?>
Template Inheritance
<?php $this->decorator('layout') ?>

Hello <?php echo $name ?>

<html>
  <head>
  </head>
  <body>
    <?php $this->output('content') ?>
  </body>
</html>
Template Slots
<html>
  <head>
    <title><?php $this->output('title') ?></title>
  </head>
  <body>
    <?php $this->output('content') ?>
  </body>
</html>

<?php $this->set('title', 'Hello World! ') ?>

<?php $this->start('title') ?>
  Hello World!
<?php $this->stop() ?>
Template Multiple Inheritance



A layout can be decorated by another layout

       Each layout can override slots
Templating: An example
CMS Templating
•  Imagine a CMS with the following features:
   –  The CMS comes bundled with default templates
   –  The developer can override default templates for a specific project
   –  The webmaster can override some templates
•  The CMS and developer templates are stored on the filesystem and are
   written with pure PHP code
•  The webmaster templates are stored in a database and are written in a
   simple templating language: Hello {{ name }}
CMS Templating
•  The CMS has several built-in sections and pages
   –  Each page is decorated by a layout, depending on the section
   –  Each section layout is decorated by a base layout


cms/templates/                          project/templates/
  base.php                                base.php
  articles/                               articles/
    layout.php                              layout.php
    article.php                             article.php
                                            content.php
articles/content.php



<h1>{{ title }}</h1>

<p>
  {{ content }}
</p>
articles/article.php


<?php $this->decorator('articles/layout') ?>

<?php $this->set('title', $title) ?>

<?php echo $this->render(
  'user:articles/content',
  array('title' => $title, 'content' => $content)
) ?>
articles/layout.php


<?php $this->decorator('base') ?>

<?php $this->set('title', 'Articles | '.$this->get('title')) ?>

<?php $this->start('head') ?>
  <?php $this->output('head') ?>
  <link rel="stylesheet" type="text/css" media="all" href="/css/
articles.css" />
<?php $this->stop() ?>

<?php $this->output('content') ?>
articles/layout.php



<?php $this->decorator('base') ?>

<?php $this->set('title', 'Articles | '.$this->get('title')) ?>

<?php $this->stylesheets->add('/css/articles.css') ?>

<?php $this->output('content') ?>
base.php
<html>
  <head>
    <title>
       <?php $this->output('title') ?>
    </title>
    <?php $this->output('head') ?>
  </head>
  <body>
    <?php $this->output('content') ?>
  </body>
</html>
Template Renderer



$t = new sfTemplateEngine($dispatcher, $loader, array(
  'user' => new ProjectTemplateRenderer($dispatcher),
  'php' => new sfTemplateRendererPhp($dispatcher),
));
Template Renderer
class ProjectTemplateRenderer extends sfTemplateRenderer
{
  public function evaluate($template, array $parameters = array())
  {
    if ($template instanceof sfTemplateStorageFile)
    {
      $template = file_get_contents($template);
    }

    $this->parameters = $parameters;

    return preg_replace_callback('/{{s*(.+?)s*}}/', array($this,
'replaceParameters'), $template);
  }

  public function replaceParameters($matches)
  {
    return isset($this->parameters[$matches[1]]) ? $this->parameters[$matches[1]] :
null;
  }
}
Template Loaders



$loader = new sfTemplateLoaderFilesystem($dispatcher,
  array(
    '/path/to/project/templates/%s.php',
    '/path/to/cms/templates/%s.php'
  ));
Template Loader Chain


$loader = new sfTemplateLoaderChain($dispatcher, array(
  new ProjectTemplateLoader(
     $dispatcher, array('pdo' => $pdo)),
  new sfTemplateLoaderFilesystem($dispatcher, array(
    '/path/to/project/templates/%s.php',
    '/path/to/cms/templates/%s.php'
  )),
));
Database Template Loader
class ProjectTemplateLoader extends sfTemplateLoader
{
  public function load($template)
  {
        $stmt = $this->options['pdo']->prepare('SELECT tpl FROM tpl WHERE name = :name');
        try
        {
          $stmt->execute(array('name' => $template));
          if (count($rows = $stmt->fetchAll(PDO::FETCH_NUM)))
          {
            return $rows[0][0];
          }
        }
        catch (PDOException $e)
        {
        }

        return false;
    }
}
Database Template Loader



$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE tpl (name, tpl)');
$pdo->exec('INSERT INTO tpl (name, tpl) VALUES
("articles/content", "{{ title }} {{ name }}")');
Template Loader Cache



$loader = new sfTemplateLoaderCache(
   $dispatcher,
   $loader,
   new sfFileCache(array('dir' => 'path/to/cache'))
);
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE tpl (name, tpl)');
$pdo->exec('INSERT INTO tpl (name, tpl) VALUES ("articles/content", "{{ title }}
{{ name }}")');

$loader = new sfTemplateLoaderCache(
   $dispatcher,
   new sfTemplateLoaderChain($dispatcher, array(
     new ProjectTemplateLoader($dispatcher, array('pdo' => $pdo)),
     new sfTemplateLoaderFilesystem($dispatcher, array(
       '/path/to/project/templates/%s.php',
       '/path/to/cms/templates/%s.php'
     )),
   )),
   new sfFileCache(array('dir' => 'path/to/cache'))
);

$t = new sfTemplateEngine($dispatcher, $loader, array(
  'user' => new ProjectTemplateRenderer($dispatcher)
));

$t->render('articles/article', array('title' => 'Title', 'content' => 'Lorem...'));
Symfony 2: Dependency Injection Container
« Dependency Injection is where
  components are given their dependencies
   through their constructors, methods, or
            directly into fields. »
http://www.picocontainer.org/injection.html
The Symfony 2 dependency injection container
replaces several symfony 1 concepts
into one integrated system:

   –  sfContext
   –  sfConfiguration
   –  sfConfig
   –  factories.yml
   –  settings.yml / logging.yml / i18n.yml
DI Hello World example
class Message
{
  public function __construct(OutputInterface $output, array $options)
  {
    $this->output = $output;
    $this->options = array_merge(array('with_newline' => false), $options);
  }

    public function say($msg)
    {
       $this->output->render($msg.($this->options['with_newline'] ? "n" : ''));
    }
}
DI Hello World example
interface OutputInterface
{
  public function render($msg);
}

class Output implements OutputInterface
{
  public function render($msg)
  {
    echo $msg;
  }
}

class FancyOutput implements OutputInterface
{
  public function render($msg)
  {
    echo sprintf("033[33m%s033[0m", $msg);
  }
}
DI Hello World example




$output = new FancyOutput();
$message = new Message($output, array('with_newline' => true));
$message->say('Hello World');
A DI container facilitates
objects description and object relationships,
    configures and instantiates objects
DI Container Hello World example


$container = new sfServiceContainerBuilder();

$container->register('output', 'FancyOutput');

$container->
  register('message', 'Message')->
  setArguments(array(new sfServiceReference('output'), array('with_newline' => true)))
;

$container->message->say('Hello World!');
$message = $container->message;


        Get the configuration for the message service

  The Message constructor must be given an output service

          Get the output object from the container

Create a Message object by passing the constructor arguments
$message = $container->message;


                     is roughly equivalent to

                  $output = new FancyOutput();
$message = new Message($output, array('with_newline' => true));!
$container = new sfServiceContainerBuilder();

$container->register('output', 'FancyOutput');
$container->
  register('message', 'Message')->
  setArguments(array(new sfServiceReference('output'), array('with_newline' => true)))
;

$container->message->say('Hello World!');
                                                                                         PHP
<container>
  <services>                                                                             XML
    <service id="output" class="FancyOutput" />

    <service id="message" class="Message">
      <argument type="service" id="output" />
      <argument type="collection">
        <argument key="with_newline">true</argument>
      </argument>
    </service>
  </services>
</container>

$container = new sfServiceContainerBuilder();
$loader = new sfServiceLoaderFileXml($container);
$loader->load('services.xml');
$container = new sfServiceContainerBuilder();

$container->register('output', 'FancyOutput');
$container->
  register('message', 'Message')->
  setArguments(array(new sfServiceReference('output'), array('with_newline' => true)))
;

$container->message->say('Hello World!');
                                                                                         PHP
services:
  output: { class: FancyOutput }                                                         YAML
  message:
    class: Message
    arguments:
      - @output
      - { with_newline: true }

$container = new sfServiceContainerBuilder();
$loader = new sfServiceLoaderFileYaml($container);
$loader->load('services.yml');
<container>
  <parameters>
    <parameter key="output.class">FancyOutput</parameter>
    <parameter key="message.options" type="collection">
      <parameter key="with_newline">true</parameter>
    </parameter>
  </parameters>

  <services>
    <service id="output" class="%output.class%" />

    <service id="message" class="Message">
      <argument type="service" id="output" />
      <argument>%message.options%</argument>
    </service>
  </services>
</container>

$container = new sfServiceContainerBuilder();
$loader = new sfServiceLoaderFileXml($container);
$loader->load('services.xml');
<container>
  <imports>
    <import resource="config.xml" />
  </imports>

  <services>
    <service id="output" class="%output.class%" />
    <service id="message" class="Message">
      <argument type="service" id="output" />
      <argument>%message.options%</argument>
    </service>
  </services>
</container>

<container>
  <parameters>
    <parameter key="output.class">FancyOutput</parameter>
    <parameter key="message.options" type="collection">
      <parameter key="with_newline">true</parameter>
    </parameter>
  </parameters>
</container>

$container = new sfServiceContainerBuilder();
$loader = new sfServiceLoaderFileXml($container);
$loader->load('services.xml');
<services>
 <import resource="config.yml" class="sfServiceLoaderFileYaml" />

  <service id="output" class="%output.class%" />
  <service id="message" class="Message">
    <argument type="service" id="output" />
    <argument>%message.options%</argument>
  </service>
</services>

parameters:
  output.class: FancyOutput

  message.options: { with_newline: true }

$container = new sfServiceContainerBuilder();
$loader = new sfServiceLoaderFileXml($container);
$loader->load('services.xml');
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE tpl (name, tpl)');
$pdo->exec('INSERT INTO tpl (name, tpl) VALUES ("articles/content", "{{ title }}
{{ name }}")');

$loader = new sfTemplateLoaderCache(
   $dispatcher,
   new sfTemplateLoaderChain($dispatcher, array(
     new ProjectTemplateLoader($dispatcher, array('pdo' => $pdo)),
     new sfTemplateLoaderFilesystem($dispatcher, array(
       '/path/to/project/templates/%s.php',
       '/path/to/cms/templates/%s.php'
     )),
   )),
   new sfFileCache(array('dir' => 'path/to/cache'))
);

$t = new sfTemplateEngine($dispatcher, $loader, array(
  'user' => new ProjectTemplateRenderer($dispatcher)
));

$t->render('articles/article', array('title' => 'Title', 'content' => 'Lorem...'));
$pdo = new PDO('sqlite::memory:');



<service id="pdo" class="PDO">
  <argument>sqlite::memory:</argument>
</service>
$container = new sfServiceContainerBuilder();
$loader = new sfServiceLoaderFileXml($container);
$loader->load('cms.xml');

$pdo->exec('CREATE TABLE tpl (name, tpl)');
$pdo->exec('INSERT INTO tpl (name, tpl) VALUES ("articles/content",
"{{ title }} {{ name }}")');

echo $container->template->render('articles/article', array('title' => 'Title',
'content' => 'Lorem...'));
<container>
  <imports>
    <import resource="config.yml" class="sfServiceLoaderFileYaml" />
    <import resource="template_loader.xml" />
  </imports>

  <services>
    <service id="event_dispatcher" class="sfEventDispatcher" />

    <service id="pdo" class="PDO">
      <argument>sqlite::memory:</argument>
    </service>

    <service id="template_renderer" class="ProjectTemplateRenderer" lazy="true">
      <argument type="service" id="event_dispatcher" />
    </service>

    <service id="template" class="sfTemplateEngine" lazy="true">
      <argument type="service" id="event_dispatcher" />
      <argument type="service" id="template_loader" />
      <argument type="collection">
        <argument type="service" key="user" id="template_renderer" />
      </argument>
    </service>
  </services>
</container>
<services>
  <service id="template_loader_project" class="ProjectTemplateLoader">
    <argument type="service" id="event_dispatcher" />
    <argument type="collection"><argument type="service" key="pdo" id="pdo" /></argument>
  </service>

  <service id="template_loader_filesystem" class="sfTemplateLoaderFilesystem">
    <argument type="service" id="event_dispatcher" />
    <argument>%template.filesystem_pattern%</argument>
  </service>

  <service id="template_loader_chain" class="sfTemplateLoaderChain">
    <argument type="service" id="event_dispatcher" />
    <argument type="collection">
      <argument type="service" id="template_loader_project" />
      <argument type="service" id="template_loader_filesystem" />
    </argument>
  </service>

  <service id="template_loader_cache" class="sfFileCache">
    <argument type="collection"><argument key="cache_dir">%application.dir%/cache</argument></argument>
  </service>

  <service id="template_loader" class="sfTemplateLoaderCache" lazy="true">
    <argument type="service" id="event_dispatcher" />
    <argument type="service" id="template_loader_chain" />
    <argument type="service" id="template_cache" />
  </service>
</services>
<services>
  <service id="template_loader" class="sfTemplateLoaderCache" lazy="true">
    <argument type="service" id="event_dispatcher" />
    <argument type="service">
      <service class="sfTemplateLoaderChain">
        <argument type="service" id="event_dispatcher" />
        <argument type="collection">
           <argument type="service">
             <service class="ProjectTemplateLoader">
               <argument type="service" id="event_dispatcher" />
               <argument type="collection"><argument type="service" key="pdo" id="pdo" /></argument>
             </service>
           </argument>
           <argument type="service">
             <service class="sfTemplateLoaderFilesystem">
               <argument type="service" id="event_dispatcher" />
               <argument>%template.filesystem_patterns%</argument>
             </service>
           </argument>
        </argument>
      </service>
    </argument>
    <argument type="service">
      <service class="sfFileCache">
        <argument type="collection"><argument key="cache_dir">%application.dir%/cache</argument></
argument>
      </service>
    </argument>
  </service>
</services>
Questions?
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/

Symfony 2 (PHP day 2009)

  • 1.
  • 2.
    Who am I? • Founder of Sensio –  Web Agency (France and USA) –  Since 1998 –  70 people –  Open-Source Specialists –  Big corporate customers –  Consulting, training, development, web design, … –  Main sponsor of symfony and Doctrine •  Creator and lead developer of symfony
  • 3.
    How many ofyou have used symfony? 1.0? 1.1? 1.2?
  • 4.
    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, …
  • 5.
    symfony 1.2 –November 2008 •  Decoupled but cohesive components: the symfony platform –  Forms, Routing, Cache, YAML, ORMs, … •  Controller still based on Mojavi –  View, Filter Chain, …
  • 6.
    Roadmap •  1.0 –January 2007 •  1.1 – June 2008 •  1.2 – November 2008 •  1.3 – November 2009 •  Version 2.0 … •  1.4 – Last 1.X version – same as 1.3 but with all deprecated features removed
  • 7.
    symfony platform >= 1.1 2.0 sfRequest sfRouting sfLogger sfI18N sfUser sfResponse sfYAML sfDatabase sfForm sfEventDispatcher sfStorage sfCache sfOutputEscaper sfValidator sfWidget sfCoreAutoload platform
  • 8.
    Symfony Components •  Standalonecomponents •  Packaged individually •  Upcoming dedicated website –  http://components.symfony-project.org/ •  Dedicated section for each component •  Dedicated documentation •  Dedicated Subversion repository –  http://svn.symfony-project.com/components/ •  Git mirror –  http://github.com/fabpot •  Already published components: –  YAML, Dependency Injection, Event Dispatcher
  • 9.
    Symfony 2 isan evolution of symfony 1 •  Same Symfony platform / components •  Different controller implementation •  Oh! Symfony now takes a capital S!!!
  • 10.
    Symfony 2 maingoals Flexibility Fast Smart
  • 11.
    Symfony 2: Newcomponents Dependency Injection Container Templating Framework Controller Handling
  • 12.
    Symfony 2 •  Notyet available as a full-stack MVC framework •  Some components have already been merged into Symfony 1 –  Event Dispatcher –  Form Framework •  Other new components will soon be released as standalone components: –  Controller Handling –  Templating Framework –  Dependency Injection Container (done)
  • 13.
    symfony 1: Notfast enough?
  • 14.
    symfony 1 is one of the slowest framework when you test it against a simple Hello World application
  • 15.
    1644& 1544& !"#$%&!'!& 1344& 1144& 1444& ;44& :44& x 19.5 944& 844& Hello World 744& 644& 544& Benchmark 344& ()"#*& 144& x 2.3 +,& -./0)%.&123& 4& based on numbers from http://paul-m-jones.com/?p=315
  • 16.
  • 17.
    Don’t use symfony foryour next « Hello World » website Use PHP ;)
  • 18.
    By the way, the fastest implemention of a Hello World application with PHP: die('Hello World');
  • 19.
    But symfony 1is probably fast enough for your next website
  • 20.
    … anyway, itis fast enough for Yahoo! Yahoo! Bookmarks sf-to.org/bookmarks Yahoo! Answers sf-to.org/answers delicious.com sf-to.org/delicious
  • 21.
    … and recently dailymotion.comannounced its migration to Symfony sf-to.org/dailymotion
  • 22.
    Second most popularvideo sharing website One of the top 50 websites in the world 42 million unique users in December
  • 23.
    …and of course manyother smaller websites…
  • 24.
  • 25.
    Symfony 2 coreis so light and flexible that you can easily customize it to have outstanding performance for a Hello World application
  • 26.
    Symfony 2 coreis so light and flexible that its raw performance is outstanding
  • 27.
    require_once dirname(__FILE__).'/sf20/autoload2/sfCore2Autoload.class.php'; sfCore2Autoload::register(); $app =new HelloApplication(); $app->run()->send(); class HelloApplication { public function __construct() { $this->dispatcher = new sfEventDispatcher(); $this->dispatcher->connect('application.load_controller', array($this, 'loadController')); } public function run() Hello World { $request = new sfWebRequest($this->dispatcher); Symfony 2.0 $handler = new sfRequestHandler($this->dispatcher); with $response = $handler->handle($request); return $response; } public function loadController(sfEvent $event) { $event->setReturnValue(array(array($this, 'hello'), array($this->dispatcher, $event['request']))); return true; } public function hello($dispatcher, $request) { $response = new sfWebResponse($dispatcher); $response->setContent('Hello World'); return $response; } }
  • 28.
    7922& 7822& !"#$%&!'!& Hello World ()$*+& 7022& Benchmark 7722& 7222& >22& =22& <22& ;22& :22& ,-./+%-&012& 922& x 3 x 7 822& 022& 3+"#4& 722& 56& ,-./+%-&710& 2& based on numbers from http://paul-m-jones.com/?p=315
  • 29.
    7 times faster?! You won’t have such a difference for real applications as most of the time, the limiting factor is not the framework itself
  • 30.
  • 31.
    Twitto: The PHPframework that fits in a tweet •  The fastest framework around? •  Uses some PHP 5.3 new features •  It also fits in a slide… require __DIR__.'/c.php'; if (!is_callable($c = @$_GET['c'] ?: function() { echo 'Woah!'; })) throw new Exception('Error'); $c(); twitt o.org
  • 32.
    Don’t use Twittofor your next website It is a joke ;)
  • 33.
    7 times faster?! •  But raw speed matters because – It demonstrates that the core « kernel » is very light – It allows you to use several Symfony frameworks within a single application with the same behavior but different optimizations: •  One full-stack framework optimized for ease of use (think symfony 1) •  One light framework optimized for speed (think Rails Metal ;))
  • 34.
    symfony platform 2.0 sfRequestHandler sfRequest sfRouting sfLogger sfI18N sfUser sfTemplate sfResponse sfValidator sfForm sfWidget sfCache sfDatabase sfStorage sfYAML sfOutputEscaper sfServiceContainer sfEventDispatcher sfCoreAutoload platform
  • 35.
    Symfony 2 kernel: TheRequest Handler
  • 36.
    Symfony 2 secretweapon: The Request Handler
  • 37.
    The Request Handler • The backbone of Symfony 2 controller implementation •  Class to build web frameworks, not only MVC ones •  Based on a simple assumption: –  The input is a request object –  The output is a response object •  The request object can be anything you want •  The response object must implement a send() method Web Request Web Response Request Handler
  • 38.
    The Request Handler $handler= new sfRequestHandler($dispatcher); $request = new sfWebRequest($dispatcher); $response = $handler->handle($request); $response->send();
  • 39.
    The Request Handler • The sfRequestHandler does several things: –  Notify events –  Execute a callable (the controller) –  Ensure that the Request is converted to a Response object •  The framework is responsible for choosing the controller •  The controller is responsible for the conversion of the Request to a Response
  • 40.
    class sfRequestHandler { protected $dispatcher = null; public function __construct(sfEventDispatcher $dispatcher) { $this->dispatcher = $dispatcher; } sfRequestHandler public function handle($request) { try { return $this->handleRaw($request); s less than 100 } catch (Exception $e) i { $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.exception', array('request' => $request, 'exception' => $e))); if ($event->isProcessed()) { lines of PHP return $this->filterResponse($event->getReturnValue(), 'An "application.exception" listener returned a non response object.'); } throw $e; } } code! public function handleRaw($request) { $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.request', array('request' => $request))); if ($event->isProcessed()) { return $this->filterResponse($event->getReturnValue(), 'An "application.request" listener returned a non response object.'); } $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.load_controller', array('request' => $request))); if (!$event->isProcessed()) { throw new Exception('Unable to load the controller.'); } list($controller, $arguments) = $event->getReturnValue(); if (!is_callable($controller)) { throw new Exception(sprintf('The controller must be a callable (%s).', var_export($controller, true))); } $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'application.controller', array('request' => $request, 'controller' => &$controller, 'arguments' => &$arguments))); if ($event->isProcessed()) { try { return $this->filterResponse($event->getReturnValue(), 'An "application.controller" listener returned a non response object.'); } catch (Exception $e) { $retval = $event->getReturnValue(); } } else { $retval = call_user_func_array($controller, $arguments); } $event = $this->dispatcher->filter(new sfEvent($this, 'application.view'), $retval); return $this->filterResponse($event->getReturnValue(), sprintf('The controller must return a response (instead of %s).', is_object($event->getReturnValue()) ? 'an object of class '.get_class($event->getReturnValue()) : (string) $event->getReturnValue())); } protected function filterResponse($response, $message) { if (!is_object($response) || !method_exists($response, 'send')) { throw new RuntimeException($message); } $event = $this->dispatcher->filter(new sfEvent($this, 'application.response'), $response); $response = $event->getReturnValue(); if (!is_object($response) || !method_exists($response, 'send')) { throw new RuntimeException('An "application.response" listener returned a non response object.'); } return $response; } }
  • 41.
  • 42.
    application.response As the verylast event notified, a listener can modify the Response object just before it is returned to the user
  • 43.
    application.request •  The veryfirst event notified •  It can act as a short-circuit event •  If one listener returns a Response object, it stops the processing
  • 44.
    application.load_controller •  Only eventfor which at least one listener must be connected to •  A listener must return –  A PHP callable (the controller) –  The arguments to pass to the callable
  • 45.
    application.view The controller mustreturn a Response object except if a listener can convert the controller return value to a Response
  • 46.
    application.exception The request handlercatches all exceptions and give a chance to listeners to return a Response object
  • 47.
    Request Handler •  Severallisteners can be attached to a single event •  Listeners are called in turn sfEventDispatcher load_controller controller response request view exception request sfRequestHandler response
  • 48.
    require_once dirname(__FILE__).'/sf20/autoload2/sfCore2Autoload.class.php'; sfCore2Autoload::register(); $app =new HelloApplication(); $app->run()->send(); class HelloApplication { public function __construct() { $this->dispatcher = new sfEventDispatcher(); $this->dispatcher->connect('application.load_controller', array($this, 'loadController')); } Hello World public function run() { y Components $request = new sfWebRequest($this->dispatcher); with Symfon $handler = new sfRequestHandler($this->dispatcher); $response = $handler->handle($request); return $response; } public function loadController(sfEvent $event) { $event->setReturnValue(array(array($this, 'hello'), array($this->dispatcher, $event['request']))); return true; } public function hello($dispatcher, $request) { $response = new sfWebResponse($dispatcher); $response->setContent('Hello World'); return $response; } }
  • 49.
  • 50.
    $app = newHelloApplication(); $app->run()->send();
  • 51.
    public function __construct() { $this->dispatcher = new sfEventDispatcher(); $this->dispatcher->connect( 'application.load_controller', array($this, 'loadController') ); } sfEventDispatcher load_controller controller response request view exception request sfRequestHandler response
  • 52.
    public function loadController(sfEvent$event) { $event->setReturnValue(array( array($this, 'hello'), array($this->dispatcher, $event['request']) )); return true; }
  • 53.
    public function hello($dispatcher,$request) { $response = new sfWebResponse($dispatcher); $response->setContent('Hello World'); return $response; }
  • 54.
    public function run() { $request = new sfWebRequest($this->dispatcher); $handler = new sfRequestHandler($this->dispatcher); $response = $handler->handle($request); return $response; }
  • 55.
    Case study: dailymotion.com • The problem: the Dailymotion developers add new features on a nearly everyday basis •  The challenge: Migrate by introducing small doses of Symfony goodness •  The process –  Wrap everything with sfRequestHandler by implementing an application.load_controller listener that calls the old code, based on the request –  Migrate the mod_rewrite rules to the symfony routing –  Add unit and functional tests
  • 56.
    Symfony 2: TheTemplating Framework
  • 57.
    New Templating Framework • 4 components –  Template Engine –  Template Renderers –  Template Loaders –  Template Storages •  Independant library
  • 58.
    require_once '/path/to/sfCore2Autoload.class.php'; sfCore2Autoload::register(); $dispatcher =new sfEventDispatcher(); $loader = new sfTemplateLoaderFilesystem($dispatcher, '/path/to/templates/%s.php'); $t = new sfTemplateEngine($dispatcher, $loader); echo $t->render('index', array('name' => 'Fabien'));
  • 59.
    Template Loaders •  Noassumption about where and how templates are to be found –  Filesystem –  Database –  Memory –  … •  Template names are « logical » names: $loader = new sfTemplateLoaderFilesystem($dispatcher, '/path/to/templates/%s.php');
  • 60.
    Template Renderers •  Noassumption about the format of the templates •  Template names are prefixed with the renderer name: –  index == php:index –  user:index $t = new sfTemplateEngine($dispatcher, $loader, array( 'user' => new ProjectTemplateRenderer($dispatcher), 'php' => new sfTemplateRendererPhp($dispatcher), ));
  • 61.
    Template Embedding Hello <?phpecho $name ?> <?php $this->render('embedded', array('name' => $name)) ?> <?php $this->render('smarty:embedded') ?>
  • 62.
    Template Inheritance <?php $this->decorator('layout')?> Hello <?php echo $name ?> <html> <head> </head> <body> <?php $this->output('content') ?> </body> </html>
  • 63.
    Template Slots <html> <head> <title><?php $this->output('title') ?></title> </head> <body> <?php $this->output('content') ?> </body> </html> <?php $this->set('title', 'Hello World! ') ?> <?php $this->start('title') ?> Hello World! <?php $this->stop() ?>
  • 64.
    Template Multiple Inheritance Alayout can be decorated by another layout Each layout can override slots
  • 65.
  • 66.
    CMS Templating •  Imaginea CMS with the following features: –  The CMS comes bundled with default templates –  The developer can override default templates for a specific project –  The webmaster can override some templates •  The CMS and developer templates are stored on the filesystem and are written with pure PHP code •  The webmaster templates are stored in a database and are written in a simple templating language: Hello {{ name }}
  • 67.
    CMS Templating •  TheCMS has several built-in sections and pages –  Each page is decorated by a layout, depending on the section –  Each section layout is decorated by a base layout cms/templates/ project/templates/ base.php base.php articles/ articles/ layout.php layout.php article.php article.php content.php
  • 68.
  • 69.
    articles/article.php <?php $this->decorator('articles/layout') ?> <?php$this->set('title', $title) ?> <?php echo $this->render( 'user:articles/content', array('title' => $title, 'content' => $content) ) ?>
  • 70.
    articles/layout.php <?php $this->decorator('base') ?> <?php$this->set('title', 'Articles | '.$this->get('title')) ?> <?php $this->start('head') ?> <?php $this->output('head') ?> <link rel="stylesheet" type="text/css" media="all" href="/css/ articles.css" /> <?php $this->stop() ?> <?php $this->output('content') ?>
  • 71.
    articles/layout.php <?php $this->decorator('base') ?> <?php$this->set('title', 'Articles | '.$this->get('title')) ?> <?php $this->stylesheets->add('/css/articles.css') ?> <?php $this->output('content') ?>
  • 72.
    base.php <html> <head> <title> <?php $this->output('title') ?> </title> <?php $this->output('head') ?> </head> <body> <?php $this->output('content') ?> </body> </html>
  • 73.
    Template Renderer $t =new sfTemplateEngine($dispatcher, $loader, array( 'user' => new ProjectTemplateRenderer($dispatcher), 'php' => new sfTemplateRendererPhp($dispatcher), ));
  • 74.
    Template Renderer class ProjectTemplateRendererextends sfTemplateRenderer { public function evaluate($template, array $parameters = array()) { if ($template instanceof sfTemplateStorageFile) { $template = file_get_contents($template); } $this->parameters = $parameters; return preg_replace_callback('/{{s*(.+?)s*}}/', array($this, 'replaceParameters'), $template); } public function replaceParameters($matches) { return isset($this->parameters[$matches[1]]) ? $this->parameters[$matches[1]] : null; } }
  • 75.
    Template Loaders $loader =new sfTemplateLoaderFilesystem($dispatcher, array( '/path/to/project/templates/%s.php', '/path/to/cms/templates/%s.php' ));
  • 76.
    Template Loader Chain $loader= new sfTemplateLoaderChain($dispatcher, array( new ProjectTemplateLoader( $dispatcher, array('pdo' => $pdo)), new sfTemplateLoaderFilesystem($dispatcher, array( '/path/to/project/templates/%s.php', '/path/to/cms/templates/%s.php' )), ));
  • 77.
    Database Template Loader classProjectTemplateLoader extends sfTemplateLoader { public function load($template) { $stmt = $this->options['pdo']->prepare('SELECT tpl FROM tpl WHERE name = :name'); try { $stmt->execute(array('name' => $template)); if (count($rows = $stmt->fetchAll(PDO::FETCH_NUM))) { return $rows[0][0]; } } catch (PDOException $e) { } return false; } }
  • 78.
    Database Template Loader $pdo= new PDO('sqlite::memory:'); $pdo->exec('CREATE TABLE tpl (name, tpl)'); $pdo->exec('INSERT INTO tpl (name, tpl) VALUES ("articles/content", "{{ title }} {{ name }}")');
  • 79.
    Template Loader Cache $loader= new sfTemplateLoaderCache( $dispatcher, $loader, new sfFileCache(array('dir' => 'path/to/cache')) );
  • 80.
    $pdo = newPDO('sqlite::memory:'); $pdo->exec('CREATE TABLE tpl (name, tpl)'); $pdo->exec('INSERT INTO tpl (name, tpl) VALUES ("articles/content", "{{ title }} {{ name }}")'); $loader = new sfTemplateLoaderCache( $dispatcher, new sfTemplateLoaderChain($dispatcher, array( new ProjectTemplateLoader($dispatcher, array('pdo' => $pdo)), new sfTemplateLoaderFilesystem($dispatcher, array( '/path/to/project/templates/%s.php', '/path/to/cms/templates/%s.php' )), )), new sfFileCache(array('dir' => 'path/to/cache')) ); $t = new sfTemplateEngine($dispatcher, $loader, array( 'user' => new ProjectTemplateRenderer($dispatcher) )); $t->render('articles/article', array('title' => 'Title', 'content' => 'Lorem...'));
  • 81.
    Symfony 2: DependencyInjection Container
  • 82.
    « Dependency Injection iswhere components are given their dependencies through their constructors, methods, or directly into fields. » http://www.picocontainer.org/injection.html
  • 83.
    The Symfony 2dependency injection container replaces several symfony 1 concepts into one integrated system: –  sfContext –  sfConfiguration –  sfConfig –  factories.yml –  settings.yml / logging.yml / i18n.yml
  • 84.
    DI Hello Worldexample class Message { public function __construct(OutputInterface $output, array $options) { $this->output = $output; $this->options = array_merge(array('with_newline' => false), $options); } public function say($msg) { $this->output->render($msg.($this->options['with_newline'] ? "n" : '')); } }
  • 85.
    DI Hello Worldexample interface OutputInterface { public function render($msg); } class Output implements OutputInterface { public function render($msg) { echo $msg; } } class FancyOutput implements OutputInterface { public function render($msg) { echo sprintf("033[33m%s033[0m", $msg); } }
  • 86.
    DI Hello Worldexample $output = new FancyOutput(); $message = new Message($output, array('with_newline' => true)); $message->say('Hello World');
  • 87.
    A DI containerfacilitates objects description and object relationships, configures and instantiates objects
  • 88.
    DI Container HelloWorld example $container = new sfServiceContainerBuilder(); $container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array(new sfServiceReference('output'), array('with_newline' => true))) ; $container->message->say('Hello World!');
  • 89.
    $message = $container->message; Get the configuration for the message service The Message constructor must be given an output service Get the output object from the container Create a Message object by passing the constructor arguments
  • 90.
    $message = $container->message; is roughly equivalent to $output = new FancyOutput(); $message = new Message($output, array('with_newline' => true));!
  • 91.
    $container = newsfServiceContainerBuilder(); $container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array(new sfServiceReference('output'), array('with_newline' => true))) ; $container->message->say('Hello World!'); PHP <container> <services> XML <service id="output" class="FancyOutput" /> <service id="message" class="Message"> <argument type="service" id="output" /> <argument type="collection"> <argument key="with_newline">true</argument> </argument> </service> </services> </container> $container = new sfServiceContainerBuilder(); $loader = new sfServiceLoaderFileXml($container); $loader->load('services.xml');
  • 92.
    $container = newsfServiceContainerBuilder(); $container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array(new sfServiceReference('output'), array('with_newline' => true))) ; $container->message->say('Hello World!'); PHP services: output: { class: FancyOutput } YAML message: class: Message arguments: - @output - { with_newline: true } $container = new sfServiceContainerBuilder(); $loader = new sfServiceLoaderFileYaml($container); $loader->load('services.yml');
  • 93.
    <container> <parameters> <parameter key="output.class">FancyOutput</parameter> <parameter key="message.options" type="collection"> <parameter key="with_newline">true</parameter> </parameter> </parameters> <services> <service id="output" class="%output.class%" /> <service id="message" class="Message"> <argument type="service" id="output" /> <argument>%message.options%</argument> </service> </services> </container> $container = new sfServiceContainerBuilder(); $loader = new sfServiceLoaderFileXml($container); $loader->load('services.xml');
  • 94.
    <container> <imports> <import resource="config.xml" /> </imports> <services> <service id="output" class="%output.class%" /> <service id="message" class="Message"> <argument type="service" id="output" /> <argument>%message.options%</argument> </service> </services> </container> <container> <parameters> <parameter key="output.class">FancyOutput</parameter> <parameter key="message.options" type="collection"> <parameter key="with_newline">true</parameter> </parameter> </parameters> </container> $container = new sfServiceContainerBuilder(); $loader = new sfServiceLoaderFileXml($container); $loader->load('services.xml');
  • 95.
    <services> <import resource="config.yml"class="sfServiceLoaderFileYaml" /> <service id="output" class="%output.class%" /> <service id="message" class="Message"> <argument type="service" id="output" /> <argument>%message.options%</argument> </service> </services> parameters: output.class: FancyOutput message.options: { with_newline: true } $container = new sfServiceContainerBuilder(); $loader = new sfServiceLoaderFileXml($container); $loader->load('services.xml');
  • 96.
    $pdo = newPDO('sqlite::memory:'); $pdo->exec('CREATE TABLE tpl (name, tpl)'); $pdo->exec('INSERT INTO tpl (name, tpl) VALUES ("articles/content", "{{ title }} {{ name }}")'); $loader = new sfTemplateLoaderCache( $dispatcher, new sfTemplateLoaderChain($dispatcher, array( new ProjectTemplateLoader($dispatcher, array('pdo' => $pdo)), new sfTemplateLoaderFilesystem($dispatcher, array( '/path/to/project/templates/%s.php', '/path/to/cms/templates/%s.php' )), )), new sfFileCache(array('dir' => 'path/to/cache')) ); $t = new sfTemplateEngine($dispatcher, $loader, array( 'user' => new ProjectTemplateRenderer($dispatcher) )); $t->render('articles/article', array('title' => 'Title', 'content' => 'Lorem...'));
  • 97.
    $pdo = newPDO('sqlite::memory:'); <service id="pdo" class="PDO"> <argument>sqlite::memory:</argument> </service>
  • 98.
    $container = newsfServiceContainerBuilder(); $loader = new sfServiceLoaderFileXml($container); $loader->load('cms.xml'); $pdo->exec('CREATE TABLE tpl (name, tpl)'); $pdo->exec('INSERT INTO tpl (name, tpl) VALUES ("articles/content", "{{ title }} {{ name }}")'); echo $container->template->render('articles/article', array('title' => 'Title', 'content' => 'Lorem...'));
  • 99.
    <container> <imports> <import resource="config.yml" class="sfServiceLoaderFileYaml" /> <import resource="template_loader.xml" /> </imports> <services> <service id="event_dispatcher" class="sfEventDispatcher" /> <service id="pdo" class="PDO"> <argument>sqlite::memory:</argument> </service> <service id="template_renderer" class="ProjectTemplateRenderer" lazy="true"> <argument type="service" id="event_dispatcher" /> </service> <service id="template" class="sfTemplateEngine" lazy="true"> <argument type="service" id="event_dispatcher" /> <argument type="service" id="template_loader" /> <argument type="collection"> <argument type="service" key="user" id="template_renderer" /> </argument> </service> </services> </container>
  • 100.
    <services> <serviceid="template_loader_project" class="ProjectTemplateLoader"> <argument type="service" id="event_dispatcher" /> <argument type="collection"><argument type="service" key="pdo" id="pdo" /></argument> </service> <service id="template_loader_filesystem" class="sfTemplateLoaderFilesystem"> <argument type="service" id="event_dispatcher" /> <argument>%template.filesystem_pattern%</argument> </service> <service id="template_loader_chain" class="sfTemplateLoaderChain"> <argument type="service" id="event_dispatcher" /> <argument type="collection"> <argument type="service" id="template_loader_project" /> <argument type="service" id="template_loader_filesystem" /> </argument> </service> <service id="template_loader_cache" class="sfFileCache"> <argument type="collection"><argument key="cache_dir">%application.dir%/cache</argument></argument> </service> <service id="template_loader" class="sfTemplateLoaderCache" lazy="true"> <argument type="service" id="event_dispatcher" /> <argument type="service" id="template_loader_chain" /> <argument type="service" id="template_cache" /> </service> </services>
  • 101.
    <services> <serviceid="template_loader" class="sfTemplateLoaderCache" lazy="true"> <argument type="service" id="event_dispatcher" /> <argument type="service"> <service class="sfTemplateLoaderChain"> <argument type="service" id="event_dispatcher" /> <argument type="collection"> <argument type="service"> <service class="ProjectTemplateLoader"> <argument type="service" id="event_dispatcher" /> <argument type="collection"><argument type="service" key="pdo" id="pdo" /></argument> </service> </argument> <argument type="service"> <service class="sfTemplateLoaderFilesystem"> <argument type="service" id="event_dispatcher" /> <argument>%template.filesystem_patterns%</argument> </service> </argument> </argument> </service> </argument> <argument type="service"> <service class="sfFileCache"> <argument type="collection"><argument key="cache_dir">%application.dir%/cache</argument></ argument> </service> </argument> </service> </services>
  • 103.
  • 104.
    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/