SlideShare uma empresa Scribd logo
1 de 77
Baixar para ler offline
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

Mais conteúdo relacionado

Mais procurados

Php tips-and-tricks4128
Php tips-and-tricks4128Php tips-and-tricks4128
Php tips-and-tricks4128PrinceGuru MS
 
Dependency injection - phpday 2010
Dependency injection - phpday 2010Dependency injection - phpday 2010
Dependency injection - phpday 2010Fabien Potencier
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201Fabien 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 WorldFabien Potencier
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the frameworkGOG.com dev team
 
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
 
Api Design
Api DesignApi Design
Api Designsartak
 
Corephpcomponentpresentation 1211425966721657-8
Corephpcomponentpresentation 1211425966721657-8Corephpcomponentpresentation 1211425966721657-8
Corephpcomponentpresentation 1211425966721657-8PrinceGuru MS
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010Fabien Potencier
 
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Fabien Potencier
 
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 frameworkG Woo
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebookguoqing75
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
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)Javier Eguiluz
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 

Mais procurados (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
 

Semelhante a Create your own PHP framework with Symfony classes

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
 
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!Kacper Gunia
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsMichael Peacock
 
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 CracowKacper Gunia
 
Symfony 2 (PHP Quebec 2009)
Symfony 2 (PHP Quebec 2009)Symfony 2 (PHP Quebec 2009)
Symfony 2 (PHP Quebec 2009)Fabien Potencier
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Jakub Zalas
 
When symfony met promises
When symfony met promises When symfony met promises
When symfony met promises Marc Morera
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统yiditushe
 
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 betterZendCon
 
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 Masahiro Nagano
 
Symfony internals [english]
Symfony internals [english]Symfony internals [english]
Symfony internals [english]Raul Fraile
 
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 2016Codemotion
 
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 gtk2Elizabeth Smith
 

Semelhante a Create your own PHP framework with Symfony classes (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
 

Mais 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.3Fabien Potencier
 
Caching on the Edge with Symfony2
Caching on the Edge with Symfony2Caching on the Edge with Symfony2
Caching on the Edge with Symfony2Fabien Potencier
 
Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010Fabien 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.3Fabien 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.3Fabien Potencier
 
Symfony2 San Francisco Meetup 2009
Symfony2 San Francisco Meetup 2009Symfony2 San Francisco Meetup 2009
Symfony2 San Francisco Meetup 2009Fabien Potencier
 
Symfony And Zend Framework Together 2009
Symfony And Zend Framework Together 2009Symfony And Zend Framework Together 2009
Symfony And Zend Framework Together 2009Fabien 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 PHPFabien 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
 

Mais 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

Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
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...apidays
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
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 organizationRadu Cotescu
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
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 2024Rafal Los
 

Último (20)

Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
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...
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
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
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
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
 

Create your own PHP framework with Symfony classes

  • 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