SlideShare una empresa de Scribd logo
1 de 77
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

Php tips-and-tricks4128
Php tips-and-tricks4128Php tips-and-tricks4128
Php tips-and-tricks4128
PrinceGuru MS
 
Dependency injection - phpday 2010
Dependency injection - phpday 2010Dependency injection - phpday 2010
Dependency injection - phpday 2010
Fabien Potencier
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201
Fabien Potencier
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
ConFoo
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
Fabien Potencier
 
Security 202 - Are you sure your site is secure?
Security 202 - Are you sure your site is secure?Security 202 - Are you sure your site is secure?
Security 202 - Are you sure your site is secure?
ConFoo
 
Corephpcomponentpresentation 1211425966721657-8
Corephpcomponentpresentation 1211425966721657-8Corephpcomponentpresentation 1211425966721657-8
Corephpcomponentpresentation 1211425966721657-8
PrinceGuru MS
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier
 
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Fabien Potencier
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
guoqing75
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
Bill Chang
 

La actualidad más candente (20)

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

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

Symfony As A Platform (Symfony Camp 2007)
Symfony As A Platform (Symfony Camp 2007)Symfony As A Platform (Symfony Camp 2007)
Symfony As A Platform (Symfony Camp 2007)
Fabien Potencier
 
Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)
Fabien Potencier
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
Michael Peacock
 
Symfony 2 (PHP Quebec 2009)
Symfony 2 (PHP Quebec 2009)Symfony 2 (PHP Quebec 2009)
Symfony 2 (PHP Quebec 2009)
Fabien Potencier
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
yiditushe
 

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

Symfony As A Platform (Symfony Camp 2007)
Symfony As A Platform (Symfony Camp 2007)Symfony As A Platform (Symfony Camp 2007)
Symfony As A Platform (Symfony Camp 2007)
 
Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
 
What's new with PHP7
What's new with PHP7What's new with PHP7
What's new with PHP7
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers Cracow
 
Symfony 2 (PHP Quebec 2009)
Symfony 2 (PHP Quebec 2009)Symfony 2 (PHP Quebec 2009)
Symfony 2 (PHP Quebec 2009)
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12
 
Pecl Picks
Pecl PicksPecl Picks
Pecl Picks
 
PHP pod mikroskopom
PHP pod mikroskopomPHP pod mikroskopom
PHP pod mikroskopom
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
When symfony met promises
When symfony met promises When symfony met promises
When symfony met promises
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
 
PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life better
 
How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server How to build a High Performance PSGI/Plack Server
How to build a High Performance PSGI/Plack Server
 
Symfony 2 (PHP day 2009)
Symfony 2 (PHP day 2009)Symfony 2 (PHP day 2009)
Symfony 2 (PHP day 2009)
 
Symfony internals [english]
Symfony internals [english]Symfony internals [english]
Symfony internals [english]
 
The new features of PHP 7 - Enrico Zimuel - Codemotion Milan 2016
The new features of PHP 7 - Enrico Zimuel - Codemotion Milan 2016The new features of PHP 7 - Enrico Zimuel - Codemotion Milan 2016
The new features of PHP 7 - Enrico Zimuel - Codemotion Milan 2016
 
The new features of PHP 7
The new features of PHP 7The new features of PHP 7
The new features of PHP 7
 
Php on the desktop and php gtk2
Php on the desktop and php gtk2Php on the desktop and php gtk2
Php on the desktop and php gtk2
 

Más de Fabien Potencier

Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3
Fabien Potencier
 
Caching on the Edge with Symfony2
Caching on the Edge with Symfony2Caching on the Edge with Symfony2
Caching on the Edge with Symfony2
Fabien Potencier
 
Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010
Fabien Potencier
 
Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3
Fabien Potencier
 
Symfony Components 2.0 on PHP 5.3
Symfony Components 2.0 on PHP 5.3Symfony Components 2.0 on PHP 5.3
Symfony Components 2.0 on PHP 5.3
Fabien Potencier
 
Symfony2 San Francisco Meetup 2009
Symfony2 San Francisco Meetup 2009Symfony2 San Francisco Meetup 2009
Symfony2 San Francisco Meetup 2009
Fabien Potencier
 
Symfony And Zend Framework Together 2009
Symfony And Zend Framework Together 2009Symfony And Zend Framework Together 2009
Symfony And Zend Framework Together 2009
Fabien Potencier
 
Twig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPTwig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHP
Fabien Potencier
 
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)
Fabien Potencier
 

Más de Fabien Potencier (18)

Varnish
VarnishVarnish
Varnish
 
Look beyond PHP
Look beyond PHPLook beyond PHP
Look beyond PHP
 
Caching on the Edge
Caching on the EdgeCaching on the Edge
Caching on the Edge
 
Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3
 
Caching on the Edge with Symfony2
Caching on the Edge with Symfony2Caching on the Edge with Symfony2
Caching on the Edge with Symfony2
 
Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
Symfony Components
Symfony ComponentsSymfony Components
Symfony Components
 
PHP 5.3 in practice
PHP 5.3 in practicePHP 5.3 in practice
PHP 5.3 in practice
 
Symfony2 revealed
Symfony2 revealedSymfony2 revealed
Symfony2 revealed
 
Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3
 
Symfony Components 2.0 on PHP 5.3
Symfony Components 2.0 on PHP 5.3Symfony Components 2.0 on PHP 5.3
Symfony Components 2.0 on PHP 5.3
 
Playing With PHP 5.3
Playing With PHP 5.3Playing With PHP 5.3
Playing With PHP 5.3
 
Symfony 2.0 on PHP 5.3
Symfony 2.0 on PHP 5.3Symfony 2.0 on PHP 5.3
Symfony 2.0 on PHP 5.3
 
Symfony2 San Francisco Meetup 2009
Symfony2 San Francisco Meetup 2009Symfony2 San Francisco Meetup 2009
Symfony2 San Francisco Meetup 2009
 
Symfony And Zend Framework Together 2009
Symfony And Zend Framework Together 2009Symfony And Zend Framework Together 2009
Symfony And Zend Framework Together 2009
 
Twig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHPTwig, the flexible, fast, and secure template language for PHP
Twig, the flexible, fast, and secure template language for PHP
 
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)
symfony: Un Framework Open-Source pour les Entreprises (Solutions Linux 2008)
 

Último

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
Earley Information Science
 

Último (20)

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdf
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 

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 fabien.potencier@sensio.com http://www.sensiolabs.com/ http://www.symfony-project.com/ PHP Quebec 2008 www.symfony-project.com 77 www.sensiolabs.com