SlideShare a Scribd company logo
The symfony platform

                  Create your very own framework

                                    Fabien Potencier / Sensio Labs




PHP Quebec 2008   www.symfony-project.com      1             www.sensiolabs.com
Sensio / Me
  • Founder of Sensio
         – Web Agency
         – Founded in 1998
         – 45 people
         – Open-Source Specialists
  • Creator of symfony
         – PHP Web framework
         – Based on
                  • 10 years of Sensio experience
                  • Existing Open-Source projects

PHP Quebec 2008          www.symfony-project.com   2   www.sensiolabs.com
Web Framework
       « Whatever the application, a framework is build to
       ease development by providing tools for recurrent and
       boring tasks. »
  • Generic components
         – Built-in
         – Well integrated
         – To solve web problems
  • Professionalize web development


PHP Quebec 2008   www.symfony-project.com   3   www.sensiolabs.com
The symfony Platform
  • symfony is made of decoupled classes based on a
    small number of core classes
         – Event Dispatcher
         – Parameter Holder


  • Classes with no dependency

       cache, command, database, form, i18n, log,
       request, response, routing, storage, user,
       validator, widget
PHP Quebec 2008   www.symfony-project.com   4   www.sensiolabs.com
The symfony Platform


          You can use all of those classes by themselves

                  … to create your own framework




PHP Quebec 2008   www.symfony-project.com   5   www.sensiolabs.com
Let’s do it


PHP Quebec 2008   www.symfony-project.com   6   www.sensiolabs.com
The Goals
  • We won’t create a full stack framework

  • We will create a framework customized for YOUR
    needs

  • The code we will write today can be used as a
    bootstrap for your own framework



PHP Quebec 2008   www.symfony-project.com   7   www.sensiolabs.com
The Web Workflow

                  The User asks a Resource in a Browser

           The Browser sends a Request to the Server

                    The Server sends back a Response

     The Browser displays the Resource to the User

PHP Quebec 2008        www.symfony-project.com   8   www.sensiolabs.com
session_start();

  if (isset($_GET['name']))
  {
    $name = $_GET['name'];
  }
  else if (isset($_SESSION['name']))
  {
    $name = $_SESSION['name'];                         PHP Global arrays
  }                                                   Built-in PHP functions
  else
  {
    $name = 'World';
  }

  $_SESSION['name'] = $name;

  echo 'Hello '.$name;




      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008      www.symfony-project.com   9   www.sensiolabs.com
Move to OOP
  • Use objects instead of global arrays and functions
         – $_GET, $_POST, getcookie()                        Request
         – echo, header(), setcookie()                       Response
         – $_SESSION                                         User
  • Why ?
         – To add behaviors to those objects
         – To have several requests, users, responses in one PHP
           process (functional testing)
         – To be able to mock those objects to ease testing
PHP Quebec 2008   www.symfony-project.com   10   www.sensiolabs.com
sfWebRequest
                           PHP                                                    Object
  $_SERVER[‘REQUEST_METHOD’]                                          >getMethod()
  $_GET[‘name’]                                                       >getParameter(‘name’)

  get_magic_quotes_gpc() ?
                                                                      >getCookie(‘name’)
      stripslashes($_COOKIE[$name]) : $_COOKIE[$name];


  (
      isset($_SERVER['HTTPS'])
      && (
        strtolower($_SERVER ['HTTPS']) == 'on’                        >isSecure()
          ||
        strtolower($_SERVER ['HTTPS']) == 1)
        )
      || (
        isset($_SERVER ['HTTP_X_FORWARDED_PROTO'])
          &&
        strtolower($_SERVER ['HTTP_X_FORWARDED_PROTO']) == 'https')
        )
  )                                                                                             Abstract
                                                                                              Parameters
                                                                                              and Headers

PHP Quebec 2008                www.symfony-project.com         11        www.sensiolabs.com
sfWebResponse
                  PHP                                          Object
  echo ‘Hello World!’                              >setContent(‘Hello World’)

  header(‘HTTP/1.0 404 Not Found’)                 >setStatusCode(404)

  setcookie(‘name’, ‘value’)                       >setCookie(‘name’, ‘value’)




                                                                             Abstract
                                                                             Headers
                                                                           and Cookies

PHP Quebec 2008     www.symfony-project.com   12      www.sensiolabs.com
sfUser / sfStorage
                  PHP                                         Object
  $_SESSION[‘name’] = ‘value’                     >setAttribute(‘name’, ‘value’)

                                                  >setCulture(‘fr’)

                                                  >setAuthenticated(true)

                                                  Native session storage +
                                                  MySQL, PostgreSQL, PDO, …




                                                                            Abstract
                                                                          $_SESSION
                                                                          Add features

PHP Quebec 2008    www.symfony-project.com   13      www.sensiolabs.com
sfEventDispatcher
  • Allow decoupled objects to communicate
  // sfUser
  $event = new sfEvent($this, 'user.change_culture', array('culture' => $culture));
  $dispatcher->notify($event);

  // sfI18N
                                                                          Based on
  $callback = array($this, 'listenToChangeCultureEvent');
                                                                   Cocoa Notification Center
  $dispatcher->connect('user.change_culture', $callback);



  • sfI18N and sfUser are decoupled
  • « Anybody » can listen to any event
  • You can notify existing events or create new ones

PHP Quebec 2008     www.symfony-project.com   14        www.sensiolabs.com
session_start();

  if (isset($_GET['name']))
  {

  }
    $name = $_GET['name'];
                                                    sfWebRequest
  else if (isset($_SESSION['name']))
  {
    $name = $_SESSION['name'];
  }
  else
  {                                                 sfUser
    $name = 'World';
  }

  $_SESSION['name'] = $name;

  echo 'Hello '.$name;                              sfWebResponse

      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008      www.symfony-project.com   15   www.sensiolabs.com
Install symfony
  • Install symfony 1.1 (via PEAR or Subversion)
  $ svn co http://svn.symfony-project.com/branches/1.1 symfony




  • Core classes are autoloaded
  require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();




PHP Quebec 2008    www.symfony-project.com   16     www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();

  $dispatcher = new sfEventDispatcher();
  $request = new sfWebRequest($dispatcher);

  session_start();

  if ($request->hasParameter('name'))                       if (isset($_GET['name']))
  {                                                         {
    $name = $request->getParameter('name');                   $name = $_GET['name'];
  }                                                         }
  else if (isset($_SESSION['name']))
  {
    $name = $_SESSION['name'];
  }
  else
  {
    $name = 'World';
  }

  $_SESSION['name'] = $name;

  echo 'Hello '.$name;




      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008          www.symfony-project.com   17            www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();

  $dispatcher = new sfEventDispatcher();
  $request = new sfWebRequest($dispatcher);

  session_start();

  if (!$name = $request->getParameter('name'))
  {
    if (isset($_SESSION['name']))
    {
      $name = $_SESSION['name'];
    }
    else
    {
      $name = 'World';
    }
  }                                                      >getParameter() returns null
  $_SESSION['name'] = $name;
                                                         if the parameter is not defined
  echo 'Hello '.$name;




      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008          www.symfony-project.com   18            www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();

  $dispatcher = new sfEventDispatcher();
  $request = new sfWebRequest($dispatcher);
  session_start();

  if (!$name = $request->getParameter('name'))
  {
    if (isset($_SESSION['name']))
    {
      $name = $_SESSION['name'];
    }
    else
    {
      $name = 'World';
    }
  }

  $_SESSION['name'] = $name;

  $response = new sfWebResponse($dispatcher);
  $response->setContent('Hello '.$name);                    echo 'Hello '.$name;
  $response->send();




      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008        www.symfony-project.com    19             www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();

  $dispatcher = new sfEventDispatcher();
  $storage = new sfSessionStorage();                      session_start();
  $user = new sfUser($dispatcher, $storage);
  $request = new sfWebRequest($dispatcher);

  if (!$name = $request->getParameter('name'))            else if (isset($_SESSION['name']))
  {                                                       {
    if (!$name = $user->getAttribute('name'))               $name = $_SESSION['name'];
    {                                                     }
      $name = 'World';
    }
  }

  $user->setAttribute('name', $name);                     $_SESSION['name'] = $name;


  $response = new sfWebResponse($dispatcher);
  $response->setContent('Hello '.$name);
  $response->send();




      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008     www.symfony-project.com   20        www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();


  $dispatcher = new sfEventDispatcher();
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);
  $request = new sfWebRequest($dispatcher);

  $name = $request->getParameter('name', $user->getAttribute('name', 'World'));


  $user->setAttribute('name', $name);

  $response = new sfWebResponse($dispatcher);
  $response->setContent('Hello '.$name);
  $response->send();



      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008     www.symfony-project.com   21        www.sensiolabs.com
sfRouting
  • Clean URLs <> Resources
  • Parses PATH_INFO to inject parameters in the
    sfRequest object
  • Several strategies: PathInfo, Pattern
  • Decouples Request and Controller




PHP Quebec 2008   www.symfony-project.com      22   www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();


  $dispatcher = new sfEventDispatcher();
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);
                                                                  /step.php?name=Fabien
  $routing = new sfPatternRouting($dispatcher);
  $routing->connect('hello', '/hello/:name');
  $request = new sfWebRequest($dispatcher);                       /step.php/hello/Fabien


  $name = $request->getParameter('name', $user->getAttribute('name', 'World’));


  $user->setAttribute('name', $name);

  $response = new sfWebResponse($dispatcher);
  $response->setContent('Hello '.$name);
  $response->send();


      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008     www.symfony-project.com   23        www.sensiolabs.com
The Web Workflow

                  The User asks a Resource in a Browser

           The Browser sends a Request to the Server

                    The Server sends back a Response

     The Browser displays the Resource to the User

PHP Quebec 2008        www.symfony-project.com   24   www.sensiolabs.com
Let’s create
                  a new framework

                      Code name: fp
PHP Quebec 2008   www.symfony-project.com   25   www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();


  $dispatcher = new sfEventDispatcher();                                     Application
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);                                 Resource
  $routing = new sfPatternRouting($dispatcher);
  $routing->connect('hello', '/hello/:name');                                Generic
  $request = new sfWebRequest($dispatcher);

  $name = $request->getParameter('name', $user->getAttribute('name', 'World'));


  $user->setAttribute('name', $name);

  $response = new sfWebResponse($dispatcher);
  $response->setContent('Hello '.$name);
  $response->send();


      User > Resource > Request > Response
                        Request   Response
PHP Quebec 2008     www.symfony-project.com   26        www.sensiolabs.com
Resource specific code
                                              generic

                                             application

                                              resource




              Request
                                               ?                  Response


  fp gives                           You handle the        fp wants
                                     resource
  you a Request                      specific code
                                                           A Response
PHP Quebec 2008    www.symfony-project.com         27      www.sensiolabs.com
• The dispatching logic is the same for every
    resource

  • The business logic depends on the resource and is
    managed by the controller

  • The controller responsability is to « convert » the
    request to a response



PHP Quebec 2008   www.symfony-project.com   28   www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();


  $dispatcher = new sfEventDispatcher();
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);
  $routing = new sfPatternRouting($dispatcher);
  $routing->connect('hello', '/hello/:name');
  $request = new sfWebRequest($dispatcher);

  $controller = new helloController();

  $response = $controller->indexAction($dispatcher, $request, $user);

  $response->send();




PHP Quebec 2008     www.symfony-project.com   29        www.sensiolabs.com
class helloController
  {
    function indexAction($dispatcher, $request, $user)
    {
          $name = $request->getParameter('name', $user->getAttribute('name', 'World’));


          $user->setAttribute('name', $name);

          $response = new sfWebResponse($dispatcher);
          $response->setContent('Hello '.$name);

          return $response;
      }
  }




PHP Quebec 2008        www.symfony-project.com   30        www.sensiolabs.com
The framework creation process

  • We write code that just works

  • We abstract the code to make it generic




PHP Quebec 2008   www.symfony-project.com   31   www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();


  $dispatcher = new sfEventDispatcher();
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);
  $routing = new sfPatternRouting($dispatcher);
  $routing->connect('hello', '/hello/:name',
    array('controller' => 'hello', 'action' => 'index'));
  $request = new sfWebRequest($dispatcher);

  $class = $request->getParameter('controller').'Controller';
  $method = $request->getParameter('action').'Action';
  $controller = new $class();

  $response = $controller->$method($dispatcher, $request, $user);

  $response->send();
                                                   sfPatternRouting accepts default
                                                           parameter values
PHP Quebec 2008     www.symfony-project.com   32        www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();
                                                                             Application
  $dispatcher = new sfEventDispatcher();
                                                                             Resource
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);
                                                                             Generic
  $routing = new sfPatternRouting($dispatcher);
  $routing->connect('hello', '/hello/:name',
    array('controller' => 'hello', 'action' => 'index'));
  $request = new sfWebRequest($dispatcher);

  $class = $request->getParameter('controller').'Controller';
  $method = $request->getParameter('action').'Action';
  $controller = new $class();

  $response = $controller->$method($dispatcher, $request, $user);

  $response->send();



PHP Quebec 2008     www.symfony-project.com   33        www.sensiolabs.com
The Request Handler
  • Handles the dispatching of the request

  • Calls the Controller

  • Has the responsability to return a sfResponse




PHP Quebec 2008   www.symfony-project.com   34   www.sensiolabs.com
$dispatcher = new sfEventDispatcher();
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);
  $routing = new sfPatternRouting($dispatcher);
  $routing->connect('hello', '/hello/:name',
    array('controller' => 'hello', 'action' => 'index'));
  $request = new sfWebRequest($dispatcher);

  $response = fpRequestHandler::handle($dispatcher, $request, $user);

  $response->send();



                                  $class = $request->getParameter('controller').'Controller';
                                  $method = $request->getParameter('action').'Action';
                                  $controller = new $class();

                                  $response = $controller->$method($dispatcher, $request, $user);




PHP Quebec 2008   www.symfony-project.com       35              www.sensiolabs.com
class fpRequestHandler
  {
    static function handle($dispatcher, $request, $user)
    {
      $class = $request->getParameter('controller').'Controller';
      $method = $request->getParameter('action').'Action';

          $controller = new $class();
          $response = $controller->$method($dispatcher, $request, $user);

          return $response;
      }
  }




PHP Quebec 2008       www.symfony-project.com   36   www.sensiolabs.com
require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();
                                                                             Application
  $dispatcher = new sfEventDispatcher();
                                                                             Resource
  $storage = new sfSessionStorage();
  $user = new sfUser($dispatcher, $storage);
                                                                             Generic
  $routing = new sfPatternRouting($dispatcher);
  $routing->connect('hello', '/hello/:name',
    array('controller' => 'hello', 'action' => 'index'));
  $request = new sfWebRequest($dispatcher);

  $response = fpRequestHandler::handle($dispatcher, $request, $user);

  $response->send();




PHP Quebec 2008     www.symfony-project.com   37        www.sensiolabs.com
Abstract object management
  •  I need a container for my application objects
         –  The dispatcher
         –  The user
         –  The routing
         –  The i18n
         –  The database
         –  …
  •  These objects are specific to my Application and do not
     depend on the Request



PHP Quebec 2008      www.symfony-project.com   38   www.sensiolabs.com
$dispatcher = new sfEventDispatcher();
  $application = new helloApplication($dispatcher);

  $request = new sfWebRequest($dispatcher);

  $response = $application->handle($request);

  $response->send();




PHP Quebec 2008        www.symfony-project.com   39   www.sensiolabs.com
class helloApplication
  {
    public $dispatcher, $user, $routing;

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

          $storage = new sfSessionStorage();
          $this->user = new sfUser($this->dispatcher, $storage);

          $this->routing = new sfPatternRouting($this->dispatcher);
          $this->routing->connect('hello', '/hello/:name',
             array('controller' => 'hello', 'action' => 'index'));
      }

      function handle($request)
      {
        return fpRequestHandler::handle($this->dispatcher, $request, $this-
       >user);
      }
  }

PHP Quebec 2008       www.symfony-project.com   40   www.sensiolabs.com
Instead of passing a dispatcher around,
                        pass the application object




PHP Quebec 2008       www.symfony-project.com   41   www.sensiolabs.com
class helloApplication
  {
    // ...

      function handle($request)
      {
        return fpRequestHandler::handle($this, $request);
      }
  }

  class fpRequestHandler
  {
    static function handle($application, $request)
    {
      $class = $request->getParameter('controller').'Controller';
      $method = $request->getParameter('action').'Action';                                                  fpRequestHandler
          $controller = new $class();
          $response = $controller->$method($application, $request);
                                                                                                              is now generic
          return $response;
      }
  }

  class helloController
  {
    function indexAction($application, $request)
    {
          $name = $request->getParameter('name', $application->user->getAttribute('name', 'World’));


          $application->user->setAttribute('name', $name);

          $response = new sfWebResponse($application->dispatcher);
          $response->setContent('Hello '.$name);

          return $response;
      }
  }


PHP Quebec 2008                     www.symfony-project.com                 42                     www.sensiolabs.com
class helloApplication
  {
                                                                          Application
    public $dispatcher, $user, $routing;

      function __construct($dispatcher)                                   Generic
      {
        $this->dispatcher = $dispatcher;

          $storage = new sfSessionStorage();
          $this->user = new sfUser($this->dispatcher, $storage);

          $this->routing = new sfPatternRouting($this->dispatcher);
          $this->routing->connect('hello', '/hello/:name',
             array('controller' => 'hello', 'action' => 'index'));
      }

      function handle($request)
      {
        return fpRequestHandler::handle($this->dispatcher, $request, $this-
       >user);
      }
  }

PHP Quebec 2008       www.symfony-project.com   43   www.sensiolabs.com
Create a fpApplication class




PHP Quebec 2008   www.symfony-project.com   44   www.sensiolabs.com
abstract class fpApplication
  {
    public $dispatcher, $user, $routing;

      function __construct($dispatcher)
      {
        $this->dispatcher = $dispatcher;
        $this->user = new sfUser($this->dispatcher, new sfSessionStorage());
        $this->routing = new sfPatternRouting($this->dispatcher);

          $this->configure();
      }

      abstract function configure();

      function handle($request)
      {
        return fpRequestHandler::handle($this, $request);
      }
  }

  class helloApplication extends fpApplication
  {
    function configure()
    {
      $this->routing->connect('hello', '/hello/:name',
         array('controller' => 'hello', 'action' => 'index'));
    }
  }


PHP Quebec 2008         www.symfony-project.com   45        www.sensiolabs.com
Move the public properties
                         to accessor methods




PHP Quebec 2008   www.symfony-project.com   46   www.sensiolabs.com
abstract class fpApplication
  {
    protected $dispatcher, $storage, $user, $routing;
      function __construct($dispatcher)
      {
        $this->dispatcher = $dispatcher;
        $this->configure();
      }
      function getDispatcher()
      {
        return $this->dispatcher;
      }
      function getStorage()
      {
        if (is_null($this->storage))
        {
          $this->storage = new new sfSessionStorage();
        }
        return $this->storage;
      }
      function getUser()
      {
        if (is_null($this->user))
        {
          $this->user = new sfUser($this->dispatcher, $this->getStorage());
        }
          return $this->user;
      }
      function getRouting()
      {
        if (is_null($this->routing))
        {
          $this->routing = new sfPatternRouting($this->dispatcher);
        }
          return $this->routing;
      }
      // ...
  }



PHP Quebec 2008                 www.symfony-project.com   47             www.sensiolabs.com
Sensible defaults
  • Most of the time
         – The dispatcher is a sfEventDispatcher
         – The request is a sfWebRequest object


  • Let’s change the Application to take defaults




PHP Quebec 2008   www.symfony-project.com   48   www.sensiolabs.com
abstract class fpApplication
  {
    function __construct($dispatcher = null)
    {
          $this->dispatcher = is_null($dispatcher) ? new sfEventDispatcher() : $dispatcher;

          // ...
      }

      function handle($request = null)
      {
          $request = is_null($request) ? new sfWebRequest($this->dispatcher) : $request;

          return fpRequestHandler::handle($this, $request);
      }
  }

  $application = new helloApplication();

  $response = $application->handle();

  $response->send();



PHP Quebec 2008        www.symfony-project.com   49        www.sensiolabs.com
More sensible defaults
  • Most of the time
         – The controller creates a sfWebResponse object
         – … with some content


  • Let’s introduce a new Controller abstract class




PHP Quebec 2008   www.symfony-project.com   50   www.sensiolabs.com
class fpController
  {
    function __construct($application)
    {
      $this->application = $application;
    }

      function render($content)
      {
        $response = new sfWebResponse($this->application->dispatcher);
        $response->setContent($content);

          return $response;
      }
  }

  class helloController extends fpController
  {
    function indexAction($request)
    {
          $name = $request->getParameter('name', $this->application->getUser()->getAttribute('name', 'World'));


          $this->application->getUser()->setAttribute('name', $name);

          return $this->render('Hello '.$name);
      }
  }

PHP Quebec 2008             www.symfony-project.com      51               www.sensiolabs.com
Move the framework
  • Move the framework code to its own directory
    structure
  require dirname(__FILE__).'/../lib/symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();

  require dirname(__FILE__).'/../lib/framework/fpApplication.class.php';
  require dirname(__FILE__).'/../lib/framework/fpController.class.php';
  require dirname(__FILE__).'/../lib/framework/fpRequestHandler.class.php';

  class helloApplication extends fpApplication
  {
    // ...
  }

  class helloController extends fpController
  {
    // ...
  }

  $application = new helloApplication();
  $application->handle()->send();


PHP Quebec 2008        www.symfony-project.com    52             www.sensiolabs.com
Autoload our framework
  require dirname(__FILE__).'/../lib/symfony/lib/autoload/sfCoreAutoload.class.php';
  sfCoreAutoload::register();

  $autoload = sfSimpleAutoload::getInstance();
  $autoload->addDirectory(dirname(__FILE__).'/../lib/framework');
  $autoload->register();

  class helloApplication extends fpApplication
  {
    // ...
  }

  class helloController extends fpController
  {
    // ...
  }

  $application = new helloApplication();
  $application->handle()->send();




PHP Quebec 2008     www.symfony-project.com   53        www.sensiolabs.com
Create a bootstrap file
  require dirname(__FILE__).'/../lib/framework/bootstrap.php';

  class helloApplication extends fpApplication
  {
    // ...
  }

  class helloController extends fpController
  {
    // ...
  }

  $application = new helloApplication();
  $application->handle()->send();




PHP Quebec 2008     www.symfony-project.com   54        www.sensiolabs.com
Move classes
  • Move the application and controller classes to
    their own directory structure

  require dirname(__FILE__).'/../lib/framework/bootstrap.php';

  require dirname(__FILE__).'/../hello/application.class.php';
  require dirname(__FILE__).'/../hello/controller/helloController.class.php';

  $application = new helloApplication();
  $application->handle()->send();




PHP Quebec 2008     www.symfony-project.com   55        www.sensiolabs.com
Summary
  • 3 generic classes
         – fpApplication
         – fpController
         – fpRequestHandler
  • 2 specific classes
         – helloApplication
         – helloController
  • A small boostrap code
                                               $application = new helloApplication();
                                               $application->handle()->send();

PHP Quebec 2008   www.symfony-project.com     56         www.sensiolabs.com
Conventions
  • We already have some conventions
         – Controller class names
         – Action method names


  • Let’s add some directory name conventions
         – Controllers are in the controller directory in the same
           directory as applications.class.php
         – The controller file is the controller class name suffixed
           by .class.php

PHP Quebec 2008    www.symfony-project.com   57   www.sensiolabs.com
abstract class fpApplication
  {
    function __construct($dispatcher = null)
    {
          $this->dispatcher = is_null($dispatcher) ? new sfEventDispatcher() : $dispatcher;

          $r = new ReflectionObject($this);
          $this->root = realpath(dirname($r->getFileName()));

          // ...
      }

      // ...
  }

  class fpRequestHandler
  {
    static function handle($application, $request)
    {
      $class = $request->getParameter('controller').'Controller';
      $method = $request->getParameter('action').'Action';

          require_once $application->root.'/controller/'.$class.'.class.php';

          $controller = new $class();
          $response = $controller->$method($application, $request);

          return $response;
      }
  }

PHP Quebec 2008           www.symfony-project.com    58             www.sensiolabs.com
Let’s add a simple templating
                       system based on PHP




PHP Quebec 2008   www.symfony-project.com   59   www.sensiolabs.com
abstract class fpApplication
  {
    protected $dispatcher, $storage, $user, $routing, $template;

      function getTemplate()
      {
        if (is_null($this->template))
        {
          $this->template = new fpTemplate();
        }

          return $this->template;
      }

      // ...
  }

  class fpTemplate
  {
    function render($template, $parameters = array())
    {
      extract($parameters);

          ob_start();
          require $template;

          return ob_get_clean();
      }
  }


PHP Quebec 2008         www.symfony-project.com   60    www.sensiolabs.com
The directory structure
  • Add a template directory




PHP Quebec 2008   www.symfony-project.com   61   www.sensiolabs.com
class fpController
  {
    // ...

      function render($template, $parameters = array())
      {
        $template = $this->application->root.'/template/'.$template;
        $content = $this->application->getTemplate()->render($template, $parameters);

          $response = new sfWebResponse($this->application->getDispatcher());
          $response->setContent($content);

          return $response;
      }
  }

  class helloController extends fpController
  {
    function indexAction($request)
    {
      $name = $request->getParameter('name', $this->application->getUser()->getAttribute('name',
      'World'));

          $this->application->getUser()->setAttribute('name', $name);

          return $this->render('hello.php', array('name' => $name));
      }
  }



PHP Quebec 2008               www.symfony-project.com   62              www.sensiolabs.com
Write some tests
  • Create a test/ directory to host test classes
  • Use PHPUnit to test our controllers
  • Change the session storage object




PHP Quebec 2008   www.symfony-project.com   63   www.sensiolabs.com
<?php

  require_once 'PHPUnit/Framework.php';
  require dirname(__FILE__).'/../../lib/framework/bootstrap.php';
  require dirname(__FILE__).'/../application.class.php';

  class helloApplicationTest extends helloApplication
  {
    function __construct($dispatcher = null)
    {
      parent::__construct($dispatcher);
      $this->root = dirname(__FILE__).'/..';
    }

      function getStorage()
      {
        return new sfSessionTestStorage(
           array('session_path' => '/tmp/quebec_demo', 'session_id' => '123')
        );
      }
  }




PHP Quebec 2008       www.symfony-project.com   64         www.sensiolabs.com
class helloControllerTest extends PHPUnit_Framework_TestCase
  {
    protected function setUp()
    {
      $this->application = new helloApplicationTest();
    }

      public function testWithRequestParameter()
      {
        $_SERVER['PATH_INFO'] = '/hello/Fabien';
        $request = new sfWebRequest($this->application->getDispatcher());
        $response = $this->application->handle($request);

          $this->assertEquals('Hello Fabien', $response->getContent());
      }

      public function testWithSession()
      {
        $_SERVER['PATH_INFO'] = '/hello';
        $request = new sfWebRequest($this->application->getDispatcher());
        $response = $this->application->handle($request);

          $this->assertEquals('Hello Fabien', $response->getContent());
      }
  }

PHP Quebec 2008         www.symfony-project.com   65        www.sensiolabs.com
Refactor the code to create
                       a fpControllerTest class




PHP Quebec 2008   www.symfony-project.com   66   www.sensiolabs.com
abstract class fpControllerTest extends PHPUnit_Framework_TestCase
  {
    protected function setUp()
    {
      $r = new ReflectionObject($this);
      $root = realpath(dirname($r->getFileName()).'/..');

          require_once $root.'/application.class.php';

          $this->application = $this->getMock(
             basename($root).'Application', array('getStorage')
          );
          $this->application->root = $root;

          $storage = new sfSessionTestStorage(
             array('session_path' => '/tmp/quebec_demo', 'session_id' => '123')
          );

          $this->application->
            expects($this->any())->
            method('getStorage')->
            will($this->returnValue($storage))
          ;
      }

      // ...
  }

PHP Quebec 2008         www.symfony-project.com   67         www.sensiolabs.com
Add XSS protection
  • Add XSS protection by escaping all template
    parameters
  • Use sfOutputEscaper symfony classes to do the
    job
  • Update the tests




PHP Quebec 2008   www.symfony-project.com   68   www.sensiolabs.com
abstract class fpApplication
  {
    // ...

      function getTemplate()
      {
        if (is_null($this->template))
        {
          $this->template = new fpTemplate($this->dispatcher);
        }

          return $this->template;
      }

      // ...
  }

  class fpTemplate
  {
    function __construct($dispatcher)
    {
      $this->dispatcher = $dispatcher;
    }

      function render($template, $parameters = array())
      {
        $event = $this->dispatcher->filter(new sfEvent($this, 'template.filter_parameters'), $parameters);
        $parameters = $event->getReturnValue();

          // ...
      }
  }
PHP Quebec 2008             www.symfony-project.com     69               www.sensiolabs.com
class helloApplication extends fpApplication
  {
    function configure()
    {
      $this->getRouting()->connect('hello', '/hello/:name',
         array('controller' => 'hello', 'action' => 'index'));

          $this->getDispatcher()->connect('template.filter_parameters’,
             array($this, 'escapeTemplateParameters'));
      }

      function escapeTemplateParameters(sfEvent $event, $parameters)
      {
          $parameters['sf_data'] = sfOutputEscaper::escape(array($this, 'htmlspecialchars'), $parameters);
          foreach ($parameters['sf_data'] as $key => $value)
          {
            $parameters[$key] = $value;
          }

          return $parameters;
      }

      function htmlspecialchars($value)
      {
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
      }
  }




PHP Quebec 2008           www.symfony-project.com    70             www.sensiolabs.com
Move the generic code to fpApplication




PHP Quebec 2008       www.symfony-project.com   71   www.sensiolabs.com
class helloApplication extends fpApplication
  {
    function configure()
    {
      $this->getRouting()->connect('hello', '/hello/:name',
         array('controller' => 'hello', 'action' => 'index'));
          $this->enableOutputEscaping();
      }
  }
  class fpApplication
  {
    // ...
      function enableOutputEscaping()
      {
        $this->dispatcher->connect('template.filter_parameters’,
           array($this, 'escapeTemplateParameters'));
      }
      function escapeTemplateParameters(sfEvent $event, $parameters)
      {
          $parameters['sf_data'] = sfOutputEscaper::escape(array($this, 'htmlspecialchars'), $parameters);
          foreach ($parameters['sf_data'] as $key => $value)
          {
            $parameters[$key] = $value;
          }
          return $parameters;
      }
      function htmlspecialchars($value)
      {
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
      }
  }




PHP Quebec 2008             www.symfony-project.com      72               www.sensiolabs.com
Add custom 404, 500
  • Allow custom 404 and 500 pages
         – 404 pages > sfError404Exception
         – 500 pages > Exception


  • Change fpRequestHandler




PHP Quebec 2008   www.symfony-project.com   73   www.sensiolabs.com
Customization
  • Add some events in the RequestHandler
         – application.request
         – application.controller
         – application.response
         – application.exception


  • They can return a sfResponse and stop the
    RequestHandler flow


PHP Quebec 2008    www.symfony-project.com   74   www.sensiolabs.com
What for?
  • Page caching
         – application.request: If I have the page in cache,
           unserialize the response from the cache and returns it
         – application.response : Serialize the response to the
           cache
  • Security
         – application.controller: Check security and change the
           controller if needed



PHP Quebec 2008   www.symfony-project.com      75   www.sensiolabs.com
If we have time…
  • Add CSS selector support to the test class
  • Implement a page cache system
  • Implement a security mecanism for controllers
  • Improve performance with caching (routing,
    framework « compilation », …)
  • Add a CLI
  • Implement a layout system
  • Use symfony Forms
  • Use a database/model
PHP Quebec 2008   www.symfony-project.com   76   www.sensiolabs.com
SENSIO S.A.
                                    26, rue Salomon de Rothschild
                                        92 286 Suresnes Cedex
                                               FRANCE
                                           Tél. : +33 1 40 99 80 80
                                           Fax : +33 1 40 99 83 34

                                                Contact
                                           Fabien Potencier
                                     fabien.potencier@sensio.com




        http://www.sensiolabs.com/                          http://www.symfony-project.com/
PHP Quebec 2008      www.symfony-project.com        77           www.sensiolabs.com

More Related Content

PDF
symfony on action - WebTech 207
PPTX
Speed up your developments with Symfony2
PDF
Dependency Injection with PHP 5.3
PDF
Unit and Functional Testing with Symfony2
PDF
Advanced symfony Techniques
PDF
PhpBB meets Symfony2
PDF
Introducing Assetic (NYPHP)
PDF
Dependency injection in PHP 5.3/5.4
symfony on action - WebTech 207
Speed up your developments with Symfony2
Dependency Injection with PHP 5.3
Unit and Functional Testing with Symfony2
Advanced symfony Techniques
PhpBB meets Symfony2
Introducing Assetic (NYPHP)
Dependency injection in PHP 5.3/5.4

What's hot (20)

PDF
Php tips-and-tricks4128
PDF
Dependency injection - phpday 2010
PDF
Symfony2 - OSIDays 2010
PDF
Dependency Injection IPC 201
PDF
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
PDF
Symfony2 - WebExpo 2010
PDF
News of the Symfony2 World
PDF
Symfony without the framework
PDF
Security 202 - Are you sure your site is secure?
KEY
Api Design
PPT
Corephpcomponentpresentation 1211425966721657-8
PDF
The state of Symfony2 - SymfonyDay 2010
PDF
Dependency injection-zendcon-2010
PDF
PHP 5.3 and Lithium: the most rad php framework
PDF
関西PHP勉強会 php5.4つまみぐい
PDF
4069180 Caching Performance Lessons From Facebook
PDF
The Zen of Lithium
PDF
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
PDF
Doctrine fixtures
PPS
Php security3895
Php tips-and-tricks4128
Dependency injection - phpday 2010
Symfony2 - OSIDays 2010
Dependency Injection IPC 201
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Symfony2 - WebExpo 2010
News of the Symfony2 World
Symfony without the framework
Security 202 - Are you sure your site is secure?
Api Design
Corephpcomponentpresentation 1211425966721657-8
The state of Symfony2 - SymfonyDay 2010
Dependency injection-zendcon-2010
PHP 5.3 and Lithium: the most rad php framework
関西PHP勉強会 php5.4つまみぐい
4069180 Caching Performance Lessons From Facebook
The Zen of Lithium
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
Doctrine fixtures
Php security3895
Ad

Similar to The symfony platform: Create your very own framework (PHP Quebec 2008) (20)

PDF
Symfony As A Platform (Symfony Camp 2007)
PDF
symfony: Simplify your professional web development with PHP (IPC Frankfurt 2...
PDF
Beyond symfony 1.2 (Symfony Camp 2008)
PDF
Symfony 2.0 on PHP 5.3
PDF
Symfony 2 (PHP day 2009)
PDF
Integrating PHP With System-i using Web Services
PDF
Web Services PHP Tutorial
PDF
Symfony2 San Francisco Meetup 2009
PDF
Web services tutorial
PDF
Web Services Tutorial
PDF
Symfony components in the wild, PHPNW12
PDF
symfony_from_scratch
PDF
symfony_from_scratch
PDF
Php and-web-services-24402
PDF
symfony: Simplify your professional web development with PHP (Symfony PHP Que...
PDF
Symfony Components 2.0 on PHP 5.3
PDF
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
PPTX
PDF
symfony: An Open-Source Framework for Professionals (PHP Day 2008)
PDF
Nginx pres
Symfony As A Platform (Symfony Camp 2007)
symfony: Simplify your professional web development with PHP (IPC Frankfurt 2...
Beyond symfony 1.2 (Symfony Camp 2008)
Symfony 2.0 on PHP 5.3
Symfony 2 (PHP day 2009)
Integrating PHP With System-i using Web Services
Web Services PHP Tutorial
Symfony2 San Francisco Meetup 2009
Web services tutorial
Web Services Tutorial
Symfony components in the wild, PHPNW12
symfony_from_scratch
symfony_from_scratch
Php and-web-services-24402
symfony: Simplify your professional web development with PHP (Symfony PHP Que...
Symfony Components 2.0 on PHP 5.3
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
symfony: An Open-Source Framework for Professionals (PHP Day 2008)
Nginx pres
Ad

More from Fabien Potencier (16)

PDF
PDF
Look beyond PHP
PDF
Caching on the Edge
PDF
Design patterns revisited with PHP 5.3
PDF
Symfony2 - WebExpo 2010
PDF
Caching on the Edge with Symfony2
PDF
Dependency Injection - ConFoo 2010
PDF
Dependency Injection
PDF
Symfony Components
PDF
PHP 5.3 in practice
PDF
Symfony2 revealed
PDF
Dependency Injection with PHP and PHP 5.3
PDF
Playing With PHP 5.3
PDF
Symfony And Zend Framework Together 2009
PDF
Twig, the flexible, fast, and secure template language for PHP
PDF
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)
Look beyond PHP
Caching on the Edge
Design patterns revisited with PHP 5.3
Symfony2 - WebExpo 2010
Caching on the Edge with Symfony2
Dependency Injection - ConFoo 2010
Dependency Injection
Symfony Components
PHP 5.3 in practice
Symfony2 revealed
Dependency Injection with PHP and PHP 5.3
Playing With PHP 5.3
Symfony And Zend Framework Together 2009
Twig, the flexible, fast, and secure template language for PHP
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)

Recently uploaded (20)

PDF
GamePlan Trading System Review: Professional Trader's Honest Take
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Advanced IT Governance
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
[발표본] 너의 과제는 클라우드에 있어_KTDS_김동현_20250524.pdf
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Advanced Soft Computing BINUS July 2025.pdf
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
cuic standard and advanced reporting.pdf
PPTX
breach-and-attack-simulation-cybersecurity-india-chennai-defenderrabbit-2025....
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
Cloud computing and distributed systems.
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
GamePlan Trading System Review: Professional Trader's Honest Take
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
The Rise and Fall of 3GPP – Time for a Sabbatical?
Review of recent advances in non-invasive hemoglobin estimation
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Advanced IT Governance
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
[발표본] 너의 과제는 클라우드에 있어_KTDS_김동현_20250524.pdf
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Advanced Soft Computing BINUS July 2025.pdf
Understanding_Digital_Forensics_Presentation.pptx
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
cuic standard and advanced reporting.pdf
breach-and-attack-simulation-cybersecurity-india-chennai-defenderrabbit-2025....
Per capita expenditure prediction using model stacking based on satellite ima...
Dropbox Q2 2025 Financial Results & Investor Presentation
Cloud computing and distributed systems.
20250228 LYD VKU AI Blended-Learning.pptx

The symfony platform: Create your very own framework (PHP Quebec 2008)

  • 1. The symfony platform Create your very own framework Fabien Potencier / Sensio Labs PHP Quebec 2008 www.symfony-project.com 1 www.sensiolabs.com
  • 2. Sensio / Me • Founder of Sensio – Web Agency – Founded in 1998 – 45 people – Open-Source Specialists • Creator of symfony – PHP Web framework – Based on • 10 years of Sensio experience • Existing Open-Source projects PHP Quebec 2008 www.symfony-project.com 2 www.sensiolabs.com
  • 3. Web Framework « Whatever the application, a framework is build to ease development by providing tools for recurrent and boring tasks. » • Generic components – Built-in – Well integrated – To solve web problems • Professionalize web development PHP Quebec 2008 www.symfony-project.com 3 www.sensiolabs.com
  • 4. The symfony Platform • symfony is made of decoupled classes based on a small number of core classes – Event Dispatcher – Parameter Holder • Classes with no dependency cache, command, database, form, i18n, log, request, response, routing, storage, user, validator, widget PHP Quebec 2008 www.symfony-project.com 4 www.sensiolabs.com
  • 5. The symfony Platform You can use all of those classes by themselves … to create your own framework PHP Quebec 2008 www.symfony-project.com 5 www.sensiolabs.com
  • 6. Let’s do it PHP Quebec 2008 www.symfony-project.com 6 www.sensiolabs.com
  • 7. The Goals • We won’t create a full stack framework • We will create a framework customized for YOUR needs • The code we will write today can be used as a bootstrap for your own framework PHP Quebec 2008 www.symfony-project.com 7 www.sensiolabs.com
  • 8. The Web Workflow The User asks a Resource in a Browser The Browser sends a Request to the Server The Server sends back a Response The Browser displays the Resource to the User PHP Quebec 2008 www.symfony-project.com 8 www.sensiolabs.com
  • 9. session_start(); if (isset($_GET['name'])) { $name = $_GET['name']; } else if (isset($_SESSION['name'])) { $name = $_SESSION['name']; PHP Global arrays } Built-in PHP functions else { $name = 'World'; } $_SESSION['name'] = $name; echo 'Hello '.$name; User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 9 www.sensiolabs.com
  • 10. Move to OOP • Use objects instead of global arrays and functions – $_GET, $_POST, getcookie() Request – echo, header(), setcookie() Response – $_SESSION User • Why ? – To add behaviors to those objects – To have several requests, users, responses in one PHP process (functional testing) – To be able to mock those objects to ease testing PHP Quebec 2008 www.symfony-project.com 10 www.sensiolabs.com
  • 11. sfWebRequest PHP Object $_SERVER[‘REQUEST_METHOD’] >getMethod() $_GET[‘name’] >getParameter(‘name’) get_magic_quotes_gpc() ? >getCookie(‘name’) stripslashes($_COOKIE[$name]) : $_COOKIE[$name]; ( isset($_SERVER['HTTPS']) && ( strtolower($_SERVER ['HTTPS']) == 'on’ >isSecure() || strtolower($_SERVER ['HTTPS']) == 1) ) || ( isset($_SERVER ['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER ['HTTP_X_FORWARDED_PROTO']) == 'https') ) ) Abstract Parameters and Headers PHP Quebec 2008 www.symfony-project.com 11 www.sensiolabs.com
  • 12. sfWebResponse PHP Object echo ‘Hello World!’ >setContent(‘Hello World’) header(‘HTTP/1.0 404 Not Found’) >setStatusCode(404) setcookie(‘name’, ‘value’) >setCookie(‘name’, ‘value’) Abstract Headers and Cookies PHP Quebec 2008 www.symfony-project.com 12 www.sensiolabs.com
  • 13. sfUser / sfStorage PHP Object $_SESSION[‘name’] = ‘value’ >setAttribute(‘name’, ‘value’) >setCulture(‘fr’) >setAuthenticated(true) Native session storage + MySQL, PostgreSQL, PDO, … Abstract $_SESSION Add features PHP Quebec 2008 www.symfony-project.com 13 www.sensiolabs.com
  • 14. sfEventDispatcher • Allow decoupled objects to communicate // sfUser $event = new sfEvent($this, 'user.change_culture', array('culture' => $culture)); $dispatcher->notify($event); // sfI18N Based on $callback = array($this, 'listenToChangeCultureEvent'); Cocoa Notification Center $dispatcher->connect('user.change_culture', $callback); • sfI18N and sfUser are decoupled • « Anybody » can listen to any event • You can notify existing events or create new ones PHP Quebec 2008 www.symfony-project.com 14 www.sensiolabs.com
  • 15. session_start(); if (isset($_GET['name'])) { } $name = $_GET['name']; sfWebRequest else if (isset($_SESSION['name'])) { $name = $_SESSION['name']; } else { sfUser $name = 'World'; } $_SESSION['name'] = $name; echo 'Hello '.$name; sfWebResponse User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 15 www.sensiolabs.com
  • 16. Install symfony • Install symfony 1.1 (via PEAR or Subversion) $ svn co http://svn.symfony-project.com/branches/1.1 symfony • Core classes are autoloaded require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); PHP Quebec 2008 www.symfony-project.com 16 www.sensiolabs.com
  • 17. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $request = new sfWebRequest($dispatcher); session_start(); if ($request->hasParameter('name')) if (isset($_GET['name'])) { { $name = $request->getParameter('name'); $name = $_GET['name']; } } else if (isset($_SESSION['name'])) { $name = $_SESSION['name']; } else { $name = 'World'; } $_SESSION['name'] = $name; echo 'Hello '.$name; User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 17 www.sensiolabs.com
  • 18. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $request = new sfWebRequest($dispatcher); session_start(); if (!$name = $request->getParameter('name')) { if (isset($_SESSION['name'])) { $name = $_SESSION['name']; } else { $name = 'World'; } } >getParameter() returns null $_SESSION['name'] = $name; if the parameter is not defined echo 'Hello '.$name; User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 18 www.sensiolabs.com
  • 19. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $request = new sfWebRequest($dispatcher); session_start(); if (!$name = $request->getParameter('name')) { if (isset($_SESSION['name'])) { $name = $_SESSION['name']; } else { $name = 'World'; } } $_SESSION['name'] = $name; $response = new sfWebResponse($dispatcher); $response->setContent('Hello '.$name); echo 'Hello '.$name; $response->send(); User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 19 www.sensiolabs.com
  • 20. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $storage = new sfSessionStorage(); session_start(); $user = new sfUser($dispatcher, $storage); $request = new sfWebRequest($dispatcher); if (!$name = $request->getParameter('name')) else if (isset($_SESSION['name'])) { { if (!$name = $user->getAttribute('name')) $name = $_SESSION['name']; { } $name = 'World'; } } $user->setAttribute('name', $name); $_SESSION['name'] = $name; $response = new sfWebResponse($dispatcher); $response->setContent('Hello '.$name); $response->send(); User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 20 www.sensiolabs.com
  • 21. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); $request = new sfWebRequest($dispatcher); $name = $request->getParameter('name', $user->getAttribute('name', 'World')); $user->setAttribute('name', $name); $response = new sfWebResponse($dispatcher); $response->setContent('Hello '.$name); $response->send(); User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 21 www.sensiolabs.com
  • 22. sfRouting • Clean URLs <> Resources • Parses PATH_INFO to inject parameters in the sfRequest object • Several strategies: PathInfo, Pattern • Decouples Request and Controller PHP Quebec 2008 www.symfony-project.com 22 www.sensiolabs.com
  • 23. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); /step.php?name=Fabien $routing = new sfPatternRouting($dispatcher); $routing->connect('hello', '/hello/:name'); $request = new sfWebRequest($dispatcher); /step.php/hello/Fabien $name = $request->getParameter('name', $user->getAttribute('name', 'World’)); $user->setAttribute('name', $name); $response = new sfWebResponse($dispatcher); $response->setContent('Hello '.$name); $response->send(); User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 23 www.sensiolabs.com
  • 24. The Web Workflow The User asks a Resource in a Browser The Browser sends a Request to the Server The Server sends back a Response The Browser displays the Resource to the User PHP Quebec 2008 www.symfony-project.com 24 www.sensiolabs.com
  • 25. Let’s create a new framework Code name: fp PHP Quebec 2008 www.symfony-project.com 25 www.sensiolabs.com
  • 26. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); Application $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); Resource $routing = new sfPatternRouting($dispatcher); $routing->connect('hello', '/hello/:name'); Generic $request = new sfWebRequest($dispatcher); $name = $request->getParameter('name', $user->getAttribute('name', 'World')); $user->setAttribute('name', $name); $response = new sfWebResponse($dispatcher); $response->setContent('Hello '.$name); $response->send(); User > Resource > Request > Response Request Response PHP Quebec 2008 www.symfony-project.com 26 www.sensiolabs.com
  • 27. Resource specific code generic application resource Request ? Response fp gives You handle the fp wants resource you a Request specific code A Response PHP Quebec 2008 www.symfony-project.com 27 www.sensiolabs.com
  • 28. • The dispatching logic is the same for every resource • The business logic depends on the resource and is managed by the controller • The controller responsability is to « convert » the request to a response PHP Quebec 2008 www.symfony-project.com 28 www.sensiolabs.com
  • 29. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); $routing = new sfPatternRouting($dispatcher); $routing->connect('hello', '/hello/:name'); $request = new sfWebRequest($dispatcher); $controller = new helloController(); $response = $controller->indexAction($dispatcher, $request, $user); $response->send(); PHP Quebec 2008 www.symfony-project.com 29 www.sensiolabs.com
  • 30. class helloController { function indexAction($dispatcher, $request, $user) { $name = $request->getParameter('name', $user->getAttribute('name', 'World’)); $user->setAttribute('name', $name); $response = new sfWebResponse($dispatcher); $response->setContent('Hello '.$name); return $response; } } PHP Quebec 2008 www.symfony-project.com 30 www.sensiolabs.com
  • 31. The framework creation process • We write code that just works • We abstract the code to make it generic PHP Quebec 2008 www.symfony-project.com 31 www.sensiolabs.com
  • 32. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $dispatcher = new sfEventDispatcher(); $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); $routing = new sfPatternRouting($dispatcher); $routing->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); $request = new sfWebRequest($dispatcher); $class = $request->getParameter('controller').'Controller'; $method = $request->getParameter('action').'Action'; $controller = new $class(); $response = $controller->$method($dispatcher, $request, $user); $response->send(); sfPatternRouting accepts default parameter values PHP Quebec 2008 www.symfony-project.com 32 www.sensiolabs.com
  • 33. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); Application $dispatcher = new sfEventDispatcher(); Resource $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); Generic $routing = new sfPatternRouting($dispatcher); $routing->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); $request = new sfWebRequest($dispatcher); $class = $request->getParameter('controller').'Controller'; $method = $request->getParameter('action').'Action'; $controller = new $class(); $response = $controller->$method($dispatcher, $request, $user); $response->send(); PHP Quebec 2008 www.symfony-project.com 33 www.sensiolabs.com
  • 34. The Request Handler • Handles the dispatching of the request • Calls the Controller • Has the responsability to return a sfResponse PHP Quebec 2008 www.symfony-project.com 34 www.sensiolabs.com
  • 35. $dispatcher = new sfEventDispatcher(); $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); $routing = new sfPatternRouting($dispatcher); $routing->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); $request = new sfWebRequest($dispatcher); $response = fpRequestHandler::handle($dispatcher, $request, $user); $response->send(); $class = $request->getParameter('controller').'Controller'; $method = $request->getParameter('action').'Action'; $controller = new $class(); $response = $controller->$method($dispatcher, $request, $user); PHP Quebec 2008 www.symfony-project.com 35 www.sensiolabs.com
  • 36. class fpRequestHandler { static function handle($dispatcher, $request, $user) { $class = $request->getParameter('controller').'Controller'; $method = $request->getParameter('action').'Action'; $controller = new $class(); $response = $controller->$method($dispatcher, $request, $user); return $response; } } PHP Quebec 2008 www.symfony-project.com 36 www.sensiolabs.com
  • 37. require dirname(__FILE__).'/../symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); Application $dispatcher = new sfEventDispatcher(); Resource $storage = new sfSessionStorage(); $user = new sfUser($dispatcher, $storage); Generic $routing = new sfPatternRouting($dispatcher); $routing->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); $request = new sfWebRequest($dispatcher); $response = fpRequestHandler::handle($dispatcher, $request, $user); $response->send(); PHP Quebec 2008 www.symfony-project.com 37 www.sensiolabs.com
  • 38. Abstract object management •  I need a container for my application objects –  The dispatcher –  The user –  The routing –  The i18n –  The database –  … •  These objects are specific to my Application and do not depend on the Request PHP Quebec 2008 www.symfony-project.com 38 www.sensiolabs.com
  • 39. $dispatcher = new sfEventDispatcher(); $application = new helloApplication($dispatcher); $request = new sfWebRequest($dispatcher); $response = $application->handle($request); $response->send(); PHP Quebec 2008 www.symfony-project.com 39 www.sensiolabs.com
  • 40. class helloApplication { public $dispatcher, $user, $routing; function __construct($dispatcher) { $this->dispatcher = $dispatcher; $storage = new sfSessionStorage(); $this->user = new sfUser($this->dispatcher, $storage); $this->routing = new sfPatternRouting($this->dispatcher); $this->routing->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); } function handle($request) { return fpRequestHandler::handle($this->dispatcher, $request, $this- >user); } } PHP Quebec 2008 www.symfony-project.com 40 www.sensiolabs.com
  • 41. Instead of passing a dispatcher around, pass the application object PHP Quebec 2008 www.symfony-project.com 41 www.sensiolabs.com
  • 42. class helloApplication { // ... function handle($request) { return fpRequestHandler::handle($this, $request); } } class fpRequestHandler { static function handle($application, $request) { $class = $request->getParameter('controller').'Controller'; $method = $request->getParameter('action').'Action'; fpRequestHandler $controller = new $class(); $response = $controller->$method($application, $request); is now generic return $response; } } class helloController { function indexAction($application, $request) { $name = $request->getParameter('name', $application->user->getAttribute('name', 'World’)); $application->user->setAttribute('name', $name); $response = new sfWebResponse($application->dispatcher); $response->setContent('Hello '.$name); return $response; } } PHP Quebec 2008 www.symfony-project.com 42 www.sensiolabs.com
  • 43. class helloApplication { Application public $dispatcher, $user, $routing; function __construct($dispatcher) Generic { $this->dispatcher = $dispatcher; $storage = new sfSessionStorage(); $this->user = new sfUser($this->dispatcher, $storage); $this->routing = new sfPatternRouting($this->dispatcher); $this->routing->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); } function handle($request) { return fpRequestHandler::handle($this->dispatcher, $request, $this- >user); } } PHP Quebec 2008 www.symfony-project.com 43 www.sensiolabs.com
  • 44. Create a fpApplication class PHP Quebec 2008 www.symfony-project.com 44 www.sensiolabs.com
  • 45. abstract class fpApplication { public $dispatcher, $user, $routing; function __construct($dispatcher) { $this->dispatcher = $dispatcher; $this->user = new sfUser($this->dispatcher, new sfSessionStorage()); $this->routing = new sfPatternRouting($this->dispatcher); $this->configure(); } abstract function configure(); function handle($request) { return fpRequestHandler::handle($this, $request); } } class helloApplication extends fpApplication { function configure() { $this->routing->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); } } PHP Quebec 2008 www.symfony-project.com 45 www.sensiolabs.com
  • 46. Move the public properties to accessor methods PHP Quebec 2008 www.symfony-project.com 46 www.sensiolabs.com
  • 47. abstract class fpApplication { protected $dispatcher, $storage, $user, $routing; function __construct($dispatcher) { $this->dispatcher = $dispatcher; $this->configure(); } function getDispatcher() { return $this->dispatcher; } function getStorage() { if (is_null($this->storage)) { $this->storage = new new sfSessionStorage(); } return $this->storage; } function getUser() { if (is_null($this->user)) { $this->user = new sfUser($this->dispatcher, $this->getStorage()); } return $this->user; } function getRouting() { if (is_null($this->routing)) { $this->routing = new sfPatternRouting($this->dispatcher); } return $this->routing; } // ... } PHP Quebec 2008 www.symfony-project.com 47 www.sensiolabs.com
  • 48. Sensible defaults • Most of the time – The dispatcher is a sfEventDispatcher – The request is a sfWebRequest object • Let’s change the Application to take defaults PHP Quebec 2008 www.symfony-project.com 48 www.sensiolabs.com
  • 49. abstract class fpApplication { function __construct($dispatcher = null) { $this->dispatcher = is_null($dispatcher) ? new sfEventDispatcher() : $dispatcher; // ... } function handle($request = null) { $request = is_null($request) ? new sfWebRequest($this->dispatcher) : $request; return fpRequestHandler::handle($this, $request); } } $application = new helloApplication(); $response = $application->handle(); $response->send(); PHP Quebec 2008 www.symfony-project.com 49 www.sensiolabs.com
  • 50. More sensible defaults • Most of the time – The controller creates a sfWebResponse object – … with some content • Let’s introduce a new Controller abstract class PHP Quebec 2008 www.symfony-project.com 50 www.sensiolabs.com
  • 51. class fpController { function __construct($application) { $this->application = $application; } function render($content) { $response = new sfWebResponse($this->application->dispatcher); $response->setContent($content); return $response; } } class helloController extends fpController { function indexAction($request) { $name = $request->getParameter('name', $this->application->getUser()->getAttribute('name', 'World')); $this->application->getUser()->setAttribute('name', $name); return $this->render('Hello '.$name); } } PHP Quebec 2008 www.symfony-project.com 51 www.sensiolabs.com
  • 52. Move the framework • Move the framework code to its own directory structure require dirname(__FILE__).'/../lib/symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); require dirname(__FILE__).'/../lib/framework/fpApplication.class.php'; require dirname(__FILE__).'/../lib/framework/fpController.class.php'; require dirname(__FILE__).'/../lib/framework/fpRequestHandler.class.php'; class helloApplication extends fpApplication { // ... } class helloController extends fpController { // ... } $application = new helloApplication(); $application->handle()->send(); PHP Quebec 2008 www.symfony-project.com 52 www.sensiolabs.com
  • 53. Autoload our framework require dirname(__FILE__).'/../lib/symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); $autoload = sfSimpleAutoload::getInstance(); $autoload->addDirectory(dirname(__FILE__).'/../lib/framework'); $autoload->register(); class helloApplication extends fpApplication { // ... } class helloController extends fpController { // ... } $application = new helloApplication(); $application->handle()->send(); PHP Quebec 2008 www.symfony-project.com 53 www.sensiolabs.com
  • 54. Create a bootstrap file require dirname(__FILE__).'/../lib/framework/bootstrap.php'; class helloApplication extends fpApplication { // ... } class helloController extends fpController { // ... } $application = new helloApplication(); $application->handle()->send(); PHP Quebec 2008 www.symfony-project.com 54 www.sensiolabs.com
  • 55. Move classes • Move the application and controller classes to their own directory structure require dirname(__FILE__).'/../lib/framework/bootstrap.php'; require dirname(__FILE__).'/../hello/application.class.php'; require dirname(__FILE__).'/../hello/controller/helloController.class.php'; $application = new helloApplication(); $application->handle()->send(); PHP Quebec 2008 www.symfony-project.com 55 www.sensiolabs.com
  • 56. Summary • 3 generic classes – fpApplication – fpController – fpRequestHandler • 2 specific classes – helloApplication – helloController • A small boostrap code $application = new helloApplication(); $application->handle()->send(); PHP Quebec 2008 www.symfony-project.com 56 www.sensiolabs.com
  • 57. Conventions • We already have some conventions – Controller class names – Action method names • Let’s add some directory name conventions – Controllers are in the controller directory in the same directory as applications.class.php – The controller file is the controller class name suffixed by .class.php PHP Quebec 2008 www.symfony-project.com 57 www.sensiolabs.com
  • 58. abstract class fpApplication { function __construct($dispatcher = null) { $this->dispatcher = is_null($dispatcher) ? new sfEventDispatcher() : $dispatcher; $r = new ReflectionObject($this); $this->root = realpath(dirname($r->getFileName())); // ... } // ... } class fpRequestHandler { static function handle($application, $request) { $class = $request->getParameter('controller').'Controller'; $method = $request->getParameter('action').'Action'; require_once $application->root.'/controller/'.$class.'.class.php'; $controller = new $class(); $response = $controller->$method($application, $request); return $response; } } PHP Quebec 2008 www.symfony-project.com 58 www.sensiolabs.com
  • 59. Let’s add a simple templating system based on PHP PHP Quebec 2008 www.symfony-project.com 59 www.sensiolabs.com
  • 60. abstract class fpApplication { protected $dispatcher, $storage, $user, $routing, $template; function getTemplate() { if (is_null($this->template)) { $this->template = new fpTemplate(); } return $this->template; } // ... } class fpTemplate { function render($template, $parameters = array()) { extract($parameters); ob_start(); require $template; return ob_get_clean(); } } PHP Quebec 2008 www.symfony-project.com 60 www.sensiolabs.com
  • 61. The directory structure • Add a template directory PHP Quebec 2008 www.symfony-project.com 61 www.sensiolabs.com
  • 62. class fpController { // ... function render($template, $parameters = array()) { $template = $this->application->root.'/template/'.$template; $content = $this->application->getTemplate()->render($template, $parameters); $response = new sfWebResponse($this->application->getDispatcher()); $response->setContent($content); return $response; } } class helloController extends fpController { function indexAction($request) { $name = $request->getParameter('name', $this->application->getUser()->getAttribute('name', 'World')); $this->application->getUser()->setAttribute('name', $name); return $this->render('hello.php', array('name' => $name)); } } PHP Quebec 2008 www.symfony-project.com 62 www.sensiolabs.com
  • 63. Write some tests • Create a test/ directory to host test classes • Use PHPUnit to test our controllers • Change the session storage object PHP Quebec 2008 www.symfony-project.com 63 www.sensiolabs.com
  • 64. <?php require_once 'PHPUnit/Framework.php'; require dirname(__FILE__).'/../../lib/framework/bootstrap.php'; require dirname(__FILE__).'/../application.class.php'; class helloApplicationTest extends helloApplication { function __construct($dispatcher = null) { parent::__construct($dispatcher); $this->root = dirname(__FILE__).'/..'; } function getStorage() { return new sfSessionTestStorage( array('session_path' => '/tmp/quebec_demo', 'session_id' => '123') ); } } PHP Quebec 2008 www.symfony-project.com 64 www.sensiolabs.com
  • 65. class helloControllerTest extends PHPUnit_Framework_TestCase { protected function setUp() { $this->application = new helloApplicationTest(); } public function testWithRequestParameter() { $_SERVER['PATH_INFO'] = '/hello/Fabien'; $request = new sfWebRequest($this->application->getDispatcher()); $response = $this->application->handle($request); $this->assertEquals('Hello Fabien', $response->getContent()); } public function testWithSession() { $_SERVER['PATH_INFO'] = '/hello'; $request = new sfWebRequest($this->application->getDispatcher()); $response = $this->application->handle($request); $this->assertEquals('Hello Fabien', $response->getContent()); } } PHP Quebec 2008 www.symfony-project.com 65 www.sensiolabs.com
  • 66. Refactor the code to create a fpControllerTest class PHP Quebec 2008 www.symfony-project.com 66 www.sensiolabs.com
  • 67. abstract class fpControllerTest extends PHPUnit_Framework_TestCase { protected function setUp() { $r = new ReflectionObject($this); $root = realpath(dirname($r->getFileName()).'/..'); require_once $root.'/application.class.php'; $this->application = $this->getMock( basename($root).'Application', array('getStorage') ); $this->application->root = $root; $storage = new sfSessionTestStorage( array('session_path' => '/tmp/quebec_demo', 'session_id' => '123') ); $this->application-> expects($this->any())-> method('getStorage')-> will($this->returnValue($storage)) ; } // ... } PHP Quebec 2008 www.symfony-project.com 67 www.sensiolabs.com
  • 68. Add XSS protection • Add XSS protection by escaping all template parameters • Use sfOutputEscaper symfony classes to do the job • Update the tests PHP Quebec 2008 www.symfony-project.com 68 www.sensiolabs.com
  • 69. abstract class fpApplication { // ... function getTemplate() { if (is_null($this->template)) { $this->template = new fpTemplate($this->dispatcher); } return $this->template; } // ... } class fpTemplate { function __construct($dispatcher) { $this->dispatcher = $dispatcher; } function render($template, $parameters = array()) { $event = $this->dispatcher->filter(new sfEvent($this, 'template.filter_parameters'), $parameters); $parameters = $event->getReturnValue(); // ... } } PHP Quebec 2008 www.symfony-project.com 69 www.sensiolabs.com
  • 70. class helloApplication extends fpApplication { function configure() { $this->getRouting()->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); $this->getDispatcher()->connect('template.filter_parameters’, array($this, 'escapeTemplateParameters')); } function escapeTemplateParameters(sfEvent $event, $parameters) { $parameters['sf_data'] = sfOutputEscaper::escape(array($this, 'htmlspecialchars'), $parameters); foreach ($parameters['sf_data'] as $key => $value) { $parameters[$key] = $value; } return $parameters; } function htmlspecialchars($value) { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); } } PHP Quebec 2008 www.symfony-project.com 70 www.sensiolabs.com
  • 71. Move the generic code to fpApplication PHP Quebec 2008 www.symfony-project.com 71 www.sensiolabs.com
  • 72. class helloApplication extends fpApplication { function configure() { $this->getRouting()->connect('hello', '/hello/:name', array('controller' => 'hello', 'action' => 'index')); $this->enableOutputEscaping(); } } class fpApplication { // ... function enableOutputEscaping() { $this->dispatcher->connect('template.filter_parameters’, array($this, 'escapeTemplateParameters')); } function escapeTemplateParameters(sfEvent $event, $parameters) { $parameters['sf_data'] = sfOutputEscaper::escape(array($this, 'htmlspecialchars'), $parameters); foreach ($parameters['sf_data'] as $key => $value) { $parameters[$key] = $value; } return $parameters; } function htmlspecialchars($value) { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); } } PHP Quebec 2008 www.symfony-project.com 72 www.sensiolabs.com
  • 73. Add custom 404, 500 • Allow custom 404 and 500 pages – 404 pages > sfError404Exception – 500 pages > Exception • Change fpRequestHandler PHP Quebec 2008 www.symfony-project.com 73 www.sensiolabs.com
  • 74. Customization • Add some events in the RequestHandler – application.request – application.controller – application.response – application.exception • They can return a sfResponse and stop the RequestHandler flow PHP Quebec 2008 www.symfony-project.com 74 www.sensiolabs.com
  • 75. What for? • Page caching – application.request: If I have the page in cache, unserialize the response from the cache and returns it – application.response : Serialize the response to the cache • Security – application.controller: Check security and change the controller if needed PHP Quebec 2008 www.symfony-project.com 75 www.sensiolabs.com
  • 76. If we have time… • Add CSS selector support to the test class • Implement a page cache system • Implement a security mecanism for controllers • Improve performance with caching (routing, framework « compilation », …) • Add a CLI • Implement a layout system • Use symfony Forms • Use a database/model PHP Quebec 2008 www.symfony-project.com 76 www.sensiolabs.com
  • 77. SENSIO S.A. 26, rue Salomon de Rothschild 92 286 Suresnes Cedex FRANCE Tél. : +33 1 40 99 80 80 Fax : +33 1 40 99 83 34 Contact Fabien Potencier [email protected] http://www.sensiolabs.com/ http://www.symfony-project.com/ PHP Quebec 2008 www.symfony-project.com 77 www.sensiolabs.com