symfony
                                    Simplify your professional
                                    web development with PHP

                                               Fabien Potencier
                                      http://www.symfony-project.com/
                                         http://www.sensiolabs.com/



International PHP 2007 Conference      www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Sensio
  • French Web Agency, founded in 1998
          – 150 people
          – 30 people dedicated to Web technologies

                                                               SENSIO
                                                              Web Agency




                                                                                 Web
                                              Webmarketing
                                                                             Technologies




                                                                              Open Source
                                                                             Technologies
                                                                           (Framework PHP)




International PHP 2007 Conference   www.symfony-project.com     fabien.potencier@sensio.com   www.sensiolabs.com
Sensio Labs
  • Open-Source technologies
          – Linux
          – Apache
          – MySQL / PostgreSQL
          – PHP / Perl / Python
  • Open-Source dedicated team
  • Big company customers
          – Web Consulting                                                                      symfony
                                                                                             PHP Framework
          – Audit / Training
          – Web Development

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
The symfony Framework
  •     PHP 5 Web Framework
  •     Based on 9 years of Sensio experience
  •     Based on well-known projets (Mojavi, Propel, Prado)
  •     Open-Source
  •     Built for :
          – Professional Websites
          – Complex needs
          – Demanding environments                                                                        Licence
                                                                                                            MIT


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
The symfony Goals
  • Bring together the « Entreprise World »
    and the Open-Source World

  • Develop Faster

  • Don’t Reinvent the Wheel

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Develop Faster
  • Each line of code has a cost
          – To write the line                                                                   less code
                                                                                                    
          – To test it
                                                                                             less complexity
          – To maintain it                                                                          
                                                                                                less bugs
  • Write less code                                                                                 
          –    Architecture : controller, ORM, …                                            more productivity
                                                                                                    
          –    Configuration
                                                                                                more time
          –    Autoloading
          –    Generators
          –    Helpers
  • More time for business rules, edge cases, …
International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com    www.sensiolabs.com
Don’t Reinvent the Wheel
  • Follow best practices
  • MVC Pattern : Model / View / Controller

  • Unit and functional test framework
  •     Environment and deployment support
  •     Configurability
  •     Security (XSS and CSRF protection by default)
  •     Extensible (plugin system)
                                                 Simplify
  •     Admin Generator                        your Dev. Life

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Main Selling Points
  • symfony is about good code but also …

  • Documentation
          – GFDL book (450p)
          – The askeet tutorial (250p)

  • 1.0 maintained for a long time
          – 1 release a month (only bug fixes)                                                                1.0
          – Commercial support


International PHP 2007 Conference    www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Let’s see some features




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Controller
  # apps/frontend/modules/blog/actions/actions.class.php
  class blogActions extends sfActions
  {                                               Controller blog/show
    function executeShow()
    {
      $id = $this->getRequestParameter('id');
                                                  Model call (Propel)
      $this->post = PostPeer::retrieveByPk($id);

              For the View


           $this->forward404Unless($this->post);
       }
                                    Shortcut
  }




International PHP 2007 Conference      www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Controller
  class blogActions extends sfActions
  {                                                                                                              1.0
    function executeShow()
    {
      $this->forward404Unless(
              $this->post = PostPeer::retrieveByPk($this->getRequestParameter('id'))
          );
      }
  }

  class blogActions extends sfActions
  {                                                                                                              1.1
    function showAction($request)
    {
      $this->forward404Unless(
               $this->post = PostPeer::retrieveByPk($request->getParameter('id'))
          );
      }
  }

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Controller (Doctrine)
  # apps/frontend/modules/blog/actions/actions.class.php
  class blogActions extends sfActions
  {
    function executeShow()
    {
      $id = $this->getRequestParameter('id');
      $this->post = Doctrine::getTable('Post')->find($id);

           $this->forward404Unless($this->post);
       }
  }




International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Model (Propel)
  // lib/model/Author.php
  class Author extends BaseAuthor
  {
    function getFullName()
    {
      return $this->getFirstName().' '.$this->getLastName();
    }
  }

  $author = new Author();
  $author->setFirstName('Fabien');
  $author->setLastName('Potencier');
  $author->save();

  $post = new Post();
  $post->setAuthor($author);
  $post->setPublishedOn('tomorrow 12:00');
  $post->isPublished(true);
  $post->save();

  $posts = PostPeer::doSelect(new Criteria());



International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Model (Doctrine)
  // lib/model/doctrine/lib/Author.php
  class Author extends BaseAuthor
  {
    function getFullName()
    {
      return $this->getFirstName().' '.$this->getLastName();
    }
  }

  $author = new Author();
  $author->setFirstName('Fabien');
  $author->setLastName('Potencier');
                                                                       Same as in Propel
  $author->save();

  $post = new Post();
  $post->setAuthor($author);
  $post->setPublishedOn('tomorrow 12:00');
  $post->isPublished(true);
  $post->save();




  $posts = Doctrine::getTable('Post')->findAll();

  $post = Doctrine::getTable('Post')->find($request->getParameter('id'));




International PHP 2007 Conference            www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
View / Template
  # apps/frontend/modules/post/templates/showSuccess.php
  <h1 class="title">
    <?php echo $post->getTitle() ?>
  </h1>

  <h2>
    par <?php echo $post->getAuthor()->getFullName() ?>
  </h2>
                           Escaped       Escaped
  <p>
    <?php echo $post->getHtmlContent(ESC_RAW) ?>
  </p>
                                     Raw Value




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Routing
                                    homepage:
              /                       param: { module: blog, action: recent }                          url_for('@homepage')
                                      url:   /


                                    homepage:
               /                      param: { module: blog, action: list }
                                      url:   /
                                    recent:
         /recent
                                      param: { module: blog, action: recent }
                                      url:   /recent


                                    post:
                                                                                                  link_to(
                                      param: { module: blog, action: show }
        /blog/1                                                                                     $post->getTitle(),
                                      requirements:
                                                                                                    '@post?id='.$post->getId()
                                        id: d+
                                                                                                  )
                                      url:   /blog/:id



International PHP 2007 Conference         www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Routing in a Template
  <?php echo link_to($post->getTitle(), '@post?id='.$post->getId()) ?>




  <a href="<?php echo url_for('@post?id='.$post->getId()) ?>">Next Post</a>




  <?php echo link_to('Google', 'http://www.google.com/') ?>



  <a href="http://www.google.com/">Google</a>


                                                                                              Be pragmatic

International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Tests: Database Fixtures
  • Test data
            # data/fixtures/data.yml
            Author:
              fabien:
                first_name: Fabien
                last_name: Potencier
            Post:
              first_post:
                author_id: fabien
                title:     IPC 2007 Conference

  $ ./symfony propel-load-data frontend



International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Functional Tests
  • Navigation simulation
          // test/functional/frontend/blogActionsTest.php
          $browser = new sfTestBrowser();
          $browser->initialize();
          $browser->
            get('/blog/1')->                                                                                        Fluent
            isStatusCode(200)->                                                                                     Interface
            checkResponseElement('h1.title', '/IPC 2007 Conference/');




           The power of CSS selectors

                                                                                                      TDD
                                                                                            Test Driven Development



International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com      www.sensiolabs.com
Be Happy… or not
  $ ./symfony test-functional frontend
  # get /
  ok 1 - status code is 200
  not ok 2 - response selector h1 does not match regex /IPC 2007 Conference/
  1..2
   Looks like you failed 1 tests of 2.




  $ ./symfony test-functional frontend
  # get /
  ok 1 - status code is 200
  ok 2 - response selector h1 matches regex /IPC 2007 Conference/
  1..2
   Looks like everything went fine.




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Functional Tests
  # simple CSS selectors
  checkResponseElement('h1.title', '/IPC 2007 Conference/')
  checkResponseElement('#title', '/IPC 2007 Conference/')

  # attribute selectors
  checkResponseElement('ul li a[class~="title"]', '/IPC 2007 Conference/')

  # combinators: > and +
  checkResponseElement('ul > li', '/IPC 2007 Conference/')

  # some CSS3 selectors
  checkResponseElement('#list li:first-child', '/IPC 2007 Conference/')
  checkResponseElement('#list li:nth-child(3)', '/IPC 2007 Conference/')
  checkResponseElement('#list li:contains("IPC 2007 Conference")')




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Functional Tests
  class ApplicationBrowser extends sfTestBrowser
  {
    public function signin($user, $password)
    {
      return $this->
        post('/signin', array('username' => $user, 'password' => $password))->
        isRedirect()->
        followRedirect()->
        checkResponseElement('div.username', 'Welcome back Fabien');
    }

      public function signout()
      {
        return $this->get('/signout');
      }                                                                                               DSL
  }                                                                                         Your own specific browser




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com      www.sensiolabs.com
Admin Generator: Backend Creation
  • Automatic generation of an Administration
    Backend, ready for production usage
          – Lists                          – Filters
          – Pagination                     – Validation
          – Sorting                        – CRUD

  $ ./symfony propel-init-admin frontend post Post



                                                                                            1) Creates a post module
                                                                                            2) Generates configuration


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com     www.sensiolabs.com
Admin Generator
  • Generated code is MVC and customizable
          – Configuration file (generator.yml)
          – Extend the Controller
          – Override some Templates / Partials




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Admin Generator: Configurability
  # apps/frontend/modules/post/config/generator.yml
      generator:
         class:         sfPropelAdminGenerator
         param:
           model_class: Post
           list:
             display: [=title, author, created_at]
             filters: [title, author_id, published_on]
             max_per_page: 5




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Admin Generator
  • List




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Admin Generator
  • Edition




   __toString()


                                                              widgets                                      m2m relationship


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Admin Generator: Extensibility
  • Module extension
          class postActions extends autoPostActions
          {
            protected function addFiltersCriteria($c)                                                        Generated
            {                                                                                                 module
              parent::addFiltersCriteria($c);
              $c->add(PostPeer::IS_PUBLISHED, true);
            }
          }

  • Template customization
               _edit_* : actions, footer, form, header, messages
               _list_* : footer, header, messages, td_actions, t(d|h)_stacked, t(d|h)_tabular
               _filters, editSuccess, listSuccess

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Debugging Tools
  • Web Debug Toolbar




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Debugging Tools
  • Error messages




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
What’s New in symfony 1.1?
  • A new task framework
  • Decoupling
          – Remove singletons
          – Remove dependencies between core classes
          – New Event Dispatcher system
  • Form / Validation framework




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
New Task Framework
  •     Easier to extend the symfony tasks
  •     Task namespaces
  •     Built-in help system
  •     Tasks are decoupled from the CLI
          – Can be called from the CLI
          – … or from your own code… easily




International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Task Namespaces
  generate
    :app                              Generates               a   new    application (init-app)
    :batch                            Generates               a   new    batch (init-batch)
    :controller                       Generates               a   new    controller (init-controller)
    :module                           Generates               a   new    module (init-module)
    :project                          Generates               a   new    project (init-project)

  test
    :all                              Launches all tests
    :functional                       Launches functional tests
    :unit                             Launches unit tests

  i18n
    :extract                          Extracts i18n strings from php files
    :find                             Finds non "i18n ready" strings in an application



International PHP 2007 Conference   www.symfony-project.com       fabien.potencier@sensio.com   www.sensiolabs.com
Task Help
  $ ./symfony help plugin:install
  Usage:
   symfony plugin:install [--stability|-s="..."] [--release|-r="..."] [--channel|-
     c="..."] [--install_deps|-d] name

  Aliases: plugin-install

  Arguments:
   name                         The plugin name

  Options:
   --stability                  (-s)   The preferred stability (stable, beta, alpha)
   --release                    (-r)   The preferred version
   --channel                    (-c)   The PEAR channel name
   --install_deps               (-d)   Whether to force installation of required dependencies

  Description:
    …


International PHP 2007 Conference        www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Task Help
  Description:
   The plugin:install task installs a plugin:

      ./symfony plugin:install sfGuargPlugin

   By default, it installs the latest stable release.

    If you want to install a plugin that is not stable yet,
    use the stability option:

      ./symfony plugin:install --stability=beta sfGuargPlugin
      ./symfony plugin:install -s beta sfGuargPlugin

    You can also force the installation of a specific version:

      ./symfony plugin:install --release=1.0.0 sfGuargPlugin
      ./symfony plugin:install -r 1.0.0 sfGuargPlugin

   To force installation of all required dependencies, use the install_deps flag:

      ./symfony plugin:install --install-deps sfGuargPlugin
      ./symfony plugin:install -d sfGuargPlugin

   By default, the PEAR channel used is symfony-plugins
   (plugins.symfony-project.org).

   You can specify another channel with the channel option:

      ./symfony plugin:install --channel=mypearchannel sfGuargPlugin
      ./symfony plugin:install -c mypearchannel sfGuargPlugin
  …

International PHP 2007 Conference   www.symfony-project.com      fabien.potencier@sensio.com   www.sensiolabs.com
Task Calls in your Code
  # Somewhere in your code
  $task = new sfCacheClearTask($dispatcher);
  $task->run();




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
symfony 1.0 Dependencies
                          sfView

                                                     sfResponse
                                                                                              sfContext

                                    sfI18N                      sfRequest
                                                                                              sfLogger

           sfStorage                  sfUser

                                                                 sfRouting                      Cleanup
                         dependency
                                                                                              Dependencies
  singleton

International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
symfony 1.1 Dependencies
                                            sfRequest                    sfView
                                                                                               sfResponse
                              sfI18N

                                                   sfEventDispatcher


                              sfUser                                                           sfRouting

   sfStorage                               sfLogger                   sfContext

                                                                                                  Cleanup
                         dependency
                                                                                                Dependencies
  singleton

International PHP 2007 Conference      www.symfony-project.com   fabien.potencier@sensio.com    www.sensiolabs.com
sfEventDispatcher
  • Based on Cocoa Notification Center
  // sfUser
  $event = new sfEvent($this, 'user.change_culture', array('culture' => $culture));
  $dispatcher->notify($event);

  // sfI18N
  $callback = array($this, 'listenToChangeCultureEvent');
  $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


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Form & Validation Framework
  •     Not a refactoring
  •     A different approach
  •     Almost no shared code
  •     Symfony 1.1 can still use the old system
          – set compat_10: on in settings.yml (off by default)
          – you can use the new and the old system in the same
            application
          – symfony 1.0 sfValidator class has been renamed to
            sfValidatorBase to avoid class name conflicts

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
The New Approach
  • Form as a first class object
  • Widget classes for form helpers
  • Validators validate arrays (it doesn’t care if it
    comes from a request, an XML file or a model
    object)
  • A decoupled system
          – Can be used without any other symfony class
          – 3 differents sub-systems
                 • Validators: can be used by themselves
                 • Widgets: can be used by themselves
                 • Form: glue between validators and widgets
International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Validators
  • Every validator extends sfValidator
  • A validator cleans and validates input values
  • sfValidator provides some common features
          – required (validation) > true by default
          – trim (cleaning) > false by default
  • Each validator can have options:
          – sfValidatorString: max_length, min_length



International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Validators
  // create a new string validator
  $v = new sfValidatorString(array('min_length' => 4));

  // clean and validate data
  $value = $v->clean('Fabien');

  // returns the input value
  $value == 'Fabien'

  // change some option
  $v->setOption('trim', true);

  $value = $v->clean(' Fabien ');

  // trims the input value
  $value == 'Fabien'                                                                    Validator objects
                                                                                          are stateless

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Custom Validator
  class CustomValidator extends sfValidator
  {
    protected function configure($options = array(), $messages = array())
    {
      $this->setOption('min_length', null);
      $this->setMessage('min_length', 'Too short.');
    }

      protected function doClean($value)
      {
        if (strlen($value) < $this->getOption('min_length'))
        {
          throw sfValidatorError($this, 'min_length', array('value' => $value));
        }

          return $value;
      }
  }




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Validators
  $v = new sfValidatorString(array('min_length' => 4));

  // throws a sfValidatorError
  $v->clean('Jon');

  $e->getCode() == 'min_length'
  $e->getMessage() == '"Jon" is too short (4 characters min).'

  $v->setMessage('min_length', 'Too short (%name%).');
  $e->getMessage() == 'Too short (Jon).'



                                                                                      Use error codes
                                                                                     or error messages

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Errors Internationalization
  $v = new sfValidatorString(array('min_length' => 4));

  try
  {
    $value = $v->clean('Jon');
  }
  catch (sfValidatorError $e)
  {
    echo $i18n->__($e->getMessageFormat(), $e->getArguments())."n";
    // or
    echo $i18n->__($e->getCode(), $e->getArguments())."n";
  }



                                                                                            Error messages
                                                                                             are i18n ready

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com    www.sensiolabs.com
sfValidator(All|Any)
  • You can aggregate validators to create a new validator:
          – sfValidatorAll: All validators must pass
          – sfValidatorAny: At least one validator must pass

  $v1 = new sfValidatorString(array('min_length' => 4));
  $v2 = new sfValidatorString(array('max_length' => 10));

  $v = new sfValidatorAll(array($v1, $v2));

  // values must validate both validators
  $v->clean('Fabien');




International PHP 2007 Conference    www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidator(All|Any)
  • As they are validators themselves, you can create
    complex logic to validate your values:
       // v1 && (v2 || v3)
       $v = new sfValidatorAll(
              array($v1, sfValidatorAny($v2, $v3))
            );




International PHP 2007 Conference    www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  • A validator schema is composed of fields
  • A field is a named validator

  $v1 = new sfValidatorString(array('min_length' => 4));

  $v = new sfValidatorSchema(array(
         'first_name' => $v1,
         'last_name' => $v1,
       ));




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  • The clean() method takes an array of named
    values and returns an array:
       $v->clean(array(
         'first_name' => 'Fabien',
         'last_name' => 'Potencier',
       ));




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  • It collects all errors for all fields
  • It throws an exception with all errors

     sfValidatorErrorSchema:
        first_name: "Jon" is too short (4 characters min).
        last_name: "Jon" is too short (4 characters min).
      in ….php on line …




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  • A named validator does not have access to the
    whole array of inputs, just its named value
  • Two special validators:
          – _pre_validator and _post_validator
          – They takes the array of values as input
          – They throw « global » errors



                                                                                                      Isolation


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  $v2 = new sfValidatorSchemaCompare(
     'password', '==', 'password_bis'
  );

  $v = new sfValidatorSchema(array(
    'password'     => $v1,
    'password_bis' => $v1,

    '_post_validator' => $v2,
  ));

  sfValidatorErrorSchema:
      "pass" does not match "word".
    in ….php on line …


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  • sfValidatorSchema is a sfValidator, so you can nest them

  $authorValidator = new sfValidatorSchema(array(
    'first_name' => $v1,
    'last_name' => $v1,
  ));

  $bookValidator =                  new sfValidatorSchema(array(
    'title'     =>                  $v1,
    'sub_title' =>                  $v1,
    'author'    =>                  $authorValidator,
  ));




International PHP 2007 Conference       www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  $bookValidator->clean(array(
    'title'     => 'The symfony book',
    'sub_title' => 'The definitive guide',
    'author'    => array(
       'first_name' => 'Fabien',
       'last_name' => 'Potencier',
    ),
  ));




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchema
  • Secure by default
          – allow_extra_fields (false by default)
          – filter_extra_fields (true by default)
  • If you pass a value with no matching validator, sfValidatorSchema
    will throw an error
  • If you switch allow_extra_fields to true, then extra fields won’t
    trigger an error but will be removed from the cleaned values
  • If you also switch filter_extra_fields to false, then extra fields won’t
    be removed from the cleaned values




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchemaForEach
  $choiceValidator = new sfValidatorSchema(array(
    'choice' => $v1,
  ));

  $choicesValidator = new sfValidatorSchemaForEach($choiceValidator, 3);


  $pollValidator = new sfValidatorSchema(array(
    'question' => $v1,
    'choices' => $choicesValidator,
  ));




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfValidatorSchemaForEach
  $pollValidator->clean(array(
    'question' => 'Do you like symfony?',
    'choices' => array(
                    array('choice' => 'Yes'),
                    array('choice' => 'This is the best'),
                    array('choice' => 'A lot'),
                  ),
  ));




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Validators as Strings
  $postValidator = new sfValidatorOr(array(
    new sfValidatorSchemaFilter('age', new sfValidatorInteger(array('min' => 18))),
    new sfValidatorAll(array(
      new sfValidatorSchemaFilter('age', new sfValidatorInteger(array('max' => 18))),
      new sfValidatorSchemaFilter('is_young', new sfValidatorBoolean(array('required' => true))),
    )),
  ));



   $string = $postValidator->asString();
   '
     age:Integer({min: 18})
     or
     age:Integer({max: 18}) and is_young:Boolean({required: true})
   '

   $postValidator = new sfValidatorFromDescription($string);

   $postValidator->asPhp();

International PHP 2007 Conference    www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Available Validators
  •     Boolean                                                •    All
  •     Choice                                                 •    Any
  •     ChoiceMany                                             •    Callback
  •     Date                                                   •    Decorator
  •     DateTime                                               •    Pass
  •     Email                                                  •    FromDescription
  •     Integer
  •     Number
                                                               •    Schema
  •     Regex
                                                               •    SchemaForEach
  •     String
                                                               •    SchemaCompare
  •     Time
                                                               •    SchemaFilter
  •     Url

International PHP 2007 Conference    www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Widgets
  • Every widget extends sfWidget
  • A widget is an object that can be rendered as an HTML
    string
  • sfWidget provides some common features
          –    renderTag()
          –    renderContentTag()
          –    Charset support
          –    XHTML or HTML closing tags
  • Each widget can have HTML attributes:
          – Takes care of escaping
          – Fixes double escaping problems


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetForm
  • Base class for all form widgets
  • Some more properties like isHidden()
  • Generates an id if none provided and the widget
    has a name
          – Default id is the widget name
          – Can be customized

                 $w = new sfWidgetFormInput();
                 $w->setIdFormat('id_%s');



International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetForm classes
  // create a new input widget
  $w = new sfWidgetFormInput();

  // render widget
  echo $w->render('first_name', 'Fabien');

  // returns the widget as HTML
  <input type="text" name="first_name" value="Fabien" id="first_name" />


  // change some attributes
  $w->setAttribute('class', 'foo');

  <input … class="foo" />                                                                     Widget objects
                                                                                              are stateless

International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetForm classes
  // add default HTML attributes
  $w = new sfWidgetFormInput(array('class' => 'foo'));

  // render widget with some HTML attributes
  echo $w->render('first_name', 'Fabien', array('class' => 'foo'));




International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetFormSchema
  • A widget schema is composed of fields
  • A field is a named widget

  $w1 = new sfWidgetFormInput();

  $w = new sfWidgetFormSchema(array(
         'first_name' => $w1,
         'last_name' => $w1,
       ));




International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetFormSchema
  • The render() method takes an array of named
    values and returns an HTML string:
       $w->render(null, array(
         'first_name' => 'Fabien',
         'last_name' => 'Potencier',
       ));



  • You can also render individual fields


International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetFormSchema
  • sfWidgetFormSchema is a sfWidget, so you can nest
    them

  $authorWidget = new sfWidgetFormSchema(array(
    'first_name' => $w1,
    'last_name' => $w1,
  ));

  $bookWidget =                     new sfWidgetFormSchema(array(
    'title'                         => $w1,
    'sub_title'                     => $w1,
    'author'                        => $authorValidator,
  ));




International PHP 2007 Conference       www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetFormSchema
  $bookWidget->render(null, array(
    'title'     => 'The symfony book',
    'sub_title' => 'The definitive guide',
    'author'    => array(
       'first_name' => 'Fabien',
       'last_name' => 'Potencier',
    ),
  ));




International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfWidgetFormSchema
  • The render() method can also render
          – Errors
          – Nested form schema
  • The render() method uses a formatter class
          – sfWidgetFormSchemaFormatterList
          – sfWidgetFormSchemaFormatterTable
          – Or build your own




International PHP 2007 Conference     www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Forms
  • A sfForm is the glue between
          – A validator schema
          – A widget schema

  $validator = new sfValidatorSchema();
  $widget = new sfWidgetFormSchema();

  $form = new sfForm();
  $form->setValidatorSchema($validator);
  $form->setWidgetSchema($widget);




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfForm
  • You can also create your own form class

  class BookForm extends sfForm
  {
    public function configure()
    {
      $validator = new BookValidatorSchema();
      $widget    = new BookWidgetFormSchema();

            $this->setValidatorSchema($validator);
            $this->setWidgetSchema($widget);
       }
  }

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
A Form in a Form
  $authorForm = new AuthorForm();

  $bookForm->embedForm($authorForm);



  $choiceForm = new ChoiceForm();

  $pollForm->embedFormForEach($choiceForm, 4);




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfForm
  $form = new AuthorForm();

  $input = array('first_name' => 'Fabien', 'last_name' => 'Potencier');
  $form->bind($input);
  if ($form->isValid())
  {
    // do something with the cleaned values
    $form->getValues();
  }
  else
  {
    // do something with the errors
    $form->getErrorSchema();
  }



International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfForm
  • sfForm can take default values
       $form = new AuthorForm(array('first_name' => 'Fabien'));



  • sfFormField objects are widgets bound to the
    input or default values
       echo $form->getFormField('first_name')->render();




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfForm
  $form = new AuthorForm();

  echo $form->getFormField('first_name')->render();
  // ===
  echo $form['first_name']->render();
  // ===
  echo $form['first_name'];

  // name and value are bound to the sfFormField object
  <input type="text" name="first_name" value="Fabien" id="first_name" />

  // add some HTML attributes
  echo $form['first_name']->render(array('class' => 'foo'));




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
For the laziest




                                                       echo $form




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Customize your Forms
                                                       $bookForm['author']

  <table>                           $bookForm['author']['first_name']->renderRow()

      <tr>
       <th>                 $bookForm['author']['first_name']->renderLabel()

            <label for="book_author_first_name">First Name</label>

                                         $bookForm['author']['first_name']->renderLabelName()
        </th>
        <td>
          <ul class="error_list">
            <li>Required.</li>                        $bookForm['author']['first_name']->renderError()
          </ul>
           <input type="text" name="book[author][first_name]" id="book_author_first_name" />


                                             $bookForm['author']['first_name']
     </td>
    </tr>
    ...
  </table>

International PHP 2007 Conference       www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Forms: In a symfony Action
  class bookActions extends sfActions
  {
    public function executeEdit($request)
    {
      $this->form = new AuthorForm();

          if ($request->isMethod('post'))
          {
            $this->form->bind($request->getParameter('book'));
            if ($this->form->isValid())
            {
              $values = $this->form->getValues();

                  $this->redirect('@homepage');
              }
          }
      }
  }




International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfFormPropel
  • Generated forms for Propel objects
  • Fully customizable
  • Introspects the Propel schema
          – Maps Propel/Creole types to symfony validators and
            widgets
          – Foreign keys
          – Many to many relationships
          – Internationalized tables


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
sfFormPropel
  class bookActions                 extends sfActions
  {
    public function                 executeEdit($request)
    {
      $this->book =                 BookPeer::retrieveByPk($request->getParameter('id'));
      $this->form =                 new AuthorForm($this->book);

          if ($request->isMethod('post'))
          {
            $this->form->bind($request->getParameter('book');
            if ($this->form->isValid())
            {
              $book = $this->form->save();

                  $this->redirect('@book?id='.$book->getId());
              }
          }
      }
  }


International PHP 2007 Conference       www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Customize Propel Forms
  class BookForm extends BaseBookForm
  {
    public function configure()
    {
      $this->embedI18n(array('en', 'fr'));

           $this->widgetSchema['en']->setLabel('en', 'English');

           unset($this['created_at']);

           $this->validatorSchema['foo'] = new sfValidatorPass();
           $this->widgetSchema['foo'] = new sfWidgetIdentity();

           $this->setDefault('published_on', time());
       }
  }



International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
A Professional Web Framework
  • Built from experience
  • 1.0 stable, maintained with commercial support
  • Growing community
          – Developers in more than 80 countries
          – 200 000 visitors per month on symfony-project.com
          – 200 plugins in just 8 months
  • Open-Source Documentation
          – The book (450 pages - GFDL)
          – Askeet Tutorial (250 pages)


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Yahoo! uses symfony
  • Yahoo! Bookmarks
          – 20 millions users
          – Web 2.0 / AJAX


  • del.icio.us
          – New beta on symfony
          – preview.delicious.com




International PHP 2007 Conference    www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Next symfony Workshops


   En français : Paris, France - Dec 05, 2007

      In English : Paris, France - Feb 13, 2008

                More info on www.sensiolabs.com

International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com
Join Us
  • Sensio Labs is recruiting in France
          – project managers
          – web developers
  • You have a passion for the web?
          – Web Developer : You have a minimum of 3 years experience in
            web development with Open-Source projects and you wish to
            participate to development of Web 2.0 sites using the best
            frameworks available.
          – Project Manager : You have more than 5 years experience as a
            developer and/or a project manager and you want to manage
            complex Web projects for prestigious clients.


International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   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/
International PHP 2007 Conference   www.symfony-project.com   fabien.potencier@sensio.com   www.sensiolabs.com

symfony: Simplify your professional web development with PHP (IPC Frankfurt 2007)

  • 1.
    symfony Simplify your professional web development with PHP Fabien Potencier http://www.symfony-project.com/ http://www.sensiolabs.com/ International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 2.
    Sensio •French Web Agency, founded in 1998 – 150 people – 30 people dedicated to Web technologies SENSIO Web Agency Web Webmarketing Technologies Open Source Technologies (Framework PHP) International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 3.
    Sensio Labs • Open-Source technologies – Linux – Apache – MySQL / PostgreSQL – PHP / Perl / Python • Open-Source dedicated team • Big company customers – Web Consulting symfony PHP Framework – Audit / Training – Web Development International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 4.
    The symfony Framework • PHP 5 Web Framework • Based on 9 years of Sensio experience • Based on well-known projets (Mojavi, Propel, Prado) • Open-Source • Built for : – Professional Websites – Complex needs – Demanding environments Licence MIT International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 5.
    The symfony Goals • Bring together the « Entreprise World » and the Open-Source World • Develop Faster • Don’t Reinvent the Wheel International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 6.
    Develop Faster • Each line of code has a cost – To write the line less code  – To test it less complexity – To maintain it  less bugs • Write less code  – Architecture : controller, ORM, … more productivity  – Configuration more time – Autoloading – Generators – Helpers • More time for business rules, edge cases, … International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 7.
    Don’t Reinvent theWheel • Follow best practices • MVC Pattern : Model / View / Controller • Unit and functional test framework • Environment and deployment support • Configurability • Security (XSS and CSRF protection by default) • Extensible (plugin system) Simplify • Admin Generator your Dev. Life International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 8.
    Main Selling Points • symfony is about good code but also … • Documentation – GFDL book (450p) – The askeet tutorial (250p) • 1.0 maintained for a long time – 1 release a month (only bug fixes) 1.0 – Commercial support International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 9.
    Let’s see somefeatures International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 10.
    Controller #apps/frontend/modules/blog/actions/actions.class.php class blogActions extends sfActions { Controller blog/show function executeShow() { $id = $this->getRequestParameter('id'); Model call (Propel) $this->post = PostPeer::retrieveByPk($id); For the View $this->forward404Unless($this->post); } Shortcut } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 11.
    Controller classblogActions extends sfActions { 1.0 function executeShow() { $this->forward404Unless( $this->post = PostPeer::retrieveByPk($this->getRequestParameter('id')) ); } } class blogActions extends sfActions { 1.1 function showAction($request) { $this->forward404Unless( $this->post = PostPeer::retrieveByPk($request->getParameter('id')) ); } } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 12.
    Controller (Doctrine) # apps/frontend/modules/blog/actions/actions.class.php class blogActions extends sfActions { function executeShow() { $id = $this->getRequestParameter('id'); $this->post = Doctrine::getTable('Post')->find($id); $this->forward404Unless($this->post); } } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 13.
    Model (Propel) // lib/model/Author.php class Author extends BaseAuthor { function getFullName() { return $this->getFirstName().' '.$this->getLastName(); } } $author = new Author(); $author->setFirstName('Fabien'); $author->setLastName('Potencier'); $author->save(); $post = new Post(); $post->setAuthor($author); $post->setPublishedOn('tomorrow 12:00'); $post->isPublished(true); $post->save(); $posts = PostPeer::doSelect(new Criteria()); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 14.
    Model (Doctrine) // lib/model/doctrine/lib/Author.php class Author extends BaseAuthor { function getFullName() { return $this->getFirstName().' '.$this->getLastName(); } } $author = new Author(); $author->setFirstName('Fabien'); $author->setLastName('Potencier'); Same as in Propel $author->save(); $post = new Post(); $post->setAuthor($author); $post->setPublishedOn('tomorrow 12:00'); $post->isPublished(true); $post->save(); $posts = Doctrine::getTable('Post')->findAll(); $post = Doctrine::getTable('Post')->find($request->getParameter('id')); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 15.
    View / Template # apps/frontend/modules/post/templates/showSuccess.php <h1 class="title"> <?php echo $post->getTitle() ?> </h1> <h2> par <?php echo $post->getAuthor()->getFullName() ?> </h2> Escaped Escaped <p> <?php echo $post->getHtmlContent(ESC_RAW) ?> </p> Raw Value International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 16.
    Routing homepage: / param: { module: blog, action: recent } url_for('@homepage') url: / homepage: / param: { module: blog, action: list } url: / recent: /recent param: { module: blog, action: recent } url: /recent post: link_to( param: { module: blog, action: show } /blog/1 $post->getTitle(), requirements: '@post?id='.$post->getId() id: d+ ) url: /blog/:id International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 17.
    Routing in aTemplate <?php echo link_to($post->getTitle(), '@post?id='.$post->getId()) ?> <a href="<?php echo url_for('@post?id='.$post->getId()) ?>">Next Post</a> <?php echo link_to('Google', 'http://www.google.com/') ?> <a href="http://www.google.com/">Google</a> Be pragmatic International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 18.
    Tests: Database Fixtures • Test data # data/fixtures/data.yml Author: fabien: first_name: Fabien last_name: Potencier Post: first_post: author_id: fabien title: IPC 2007 Conference $ ./symfony propel-load-data frontend International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 19.
    Functional Tests • Navigation simulation // test/functional/frontend/blogActionsTest.php $browser = new sfTestBrowser(); $browser->initialize(); $browser-> get('/blog/1')-> Fluent isStatusCode(200)-> Interface checkResponseElement('h1.title', '/IPC 2007 Conference/'); The power of CSS selectors TDD Test Driven Development International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 20.
    Be Happy… ornot $ ./symfony test-functional frontend # get / ok 1 - status code is 200 not ok 2 - response selector h1 does not match regex /IPC 2007 Conference/ 1..2 Looks like you failed 1 tests of 2. $ ./symfony test-functional frontend # get / ok 1 - status code is 200 ok 2 - response selector h1 matches regex /IPC 2007 Conference/ 1..2 Looks like everything went fine. International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 21.
    Functional Tests # simple CSS selectors checkResponseElement('h1.title', '/IPC 2007 Conference/') checkResponseElement('#title', '/IPC 2007 Conference/') # attribute selectors checkResponseElement('ul li a[class~="title"]', '/IPC 2007 Conference/') # combinators: > and + checkResponseElement('ul > li', '/IPC 2007 Conference/') # some CSS3 selectors checkResponseElement('#list li:first-child', '/IPC 2007 Conference/') checkResponseElement('#list li:nth-child(3)', '/IPC 2007 Conference/') checkResponseElement('#list li:contains("IPC 2007 Conference")') International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 22.
    Functional Tests class ApplicationBrowser extends sfTestBrowser { public function signin($user, $password) { return $this-> post('/signin', array('username' => $user, 'password' => $password))-> isRedirect()-> followRedirect()-> checkResponseElement('div.username', 'Welcome back Fabien'); } public function signout() { return $this->get('/signout'); } DSL } Your own specific browser International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 23.
    Admin Generator: BackendCreation • Automatic generation of an Administration Backend, ready for production usage – Lists – Filters – Pagination – Validation – Sorting – CRUD $ ./symfony propel-init-admin frontend post Post 1) Creates a post module 2) Generates configuration International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 24.
    Admin Generator • Generated code is MVC and customizable – Configuration file (generator.yml) – Extend the Controller – Override some Templates / Partials International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 25.
    Admin Generator: Configurability # apps/frontend/modules/post/config/generator.yml generator: class: sfPropelAdminGenerator param: model_class: Post list: display: [=title, author, created_at] filters: [title, author_id, published_on] max_per_page: 5 International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 26.
    Admin Generator • List International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 27.
    Admin Generator • Edition __toString() widgets m2m relationship International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 28.
    Admin Generator: Extensibility • Module extension class postActions extends autoPostActions { protected function addFiltersCriteria($c) Generated { module parent::addFiltersCriteria($c); $c->add(PostPeer::IS_PUBLISHED, true); } } • Template customization _edit_* : actions, footer, form, header, messages _list_* : footer, header, messages, td_actions, t(d|h)_stacked, t(d|h)_tabular _filters, editSuccess, listSuccess International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 29.
    Debugging Tools • Web Debug Toolbar International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 30.
    Debugging Tools • Error messages International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 31.
    What’s New insymfony 1.1? • A new task framework • Decoupling – Remove singletons – Remove dependencies between core classes – New Event Dispatcher system • Form / Validation framework International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 32.
    New Task Framework • Easier to extend the symfony tasks • Task namespaces • Built-in help system • Tasks are decoupled from the CLI – Can be called from the CLI – … or from your own code… easily International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 33.
    Task Namespaces generate :app Generates a new application (init-app) :batch Generates a new batch (init-batch) :controller Generates a new controller (init-controller) :module Generates a new module (init-module) :project Generates a new project (init-project) test :all Launches all tests :functional Launches functional tests :unit Launches unit tests i18n :extract Extracts i18n strings from php files :find Finds non "i18n ready" strings in an application International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 34.
    Task Help $ ./symfony help plugin:install Usage: symfony plugin:install [--stability|-s="..."] [--release|-r="..."] [--channel|- c="..."] [--install_deps|-d] name Aliases: plugin-install Arguments: name The plugin name Options: --stability (-s) The preferred stability (stable, beta, alpha) --release (-r) The preferred version --channel (-c) The PEAR channel name --install_deps (-d) Whether to force installation of required dependencies Description: … International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 35.
    Task Help Description: The plugin:install task installs a plugin: ./symfony plugin:install sfGuargPlugin By default, it installs the latest stable release. If you want to install a plugin that is not stable yet, use the stability option: ./symfony plugin:install --stability=beta sfGuargPlugin ./symfony plugin:install -s beta sfGuargPlugin You can also force the installation of a specific version: ./symfony plugin:install --release=1.0.0 sfGuargPlugin ./symfony plugin:install -r 1.0.0 sfGuargPlugin To force installation of all required dependencies, use the install_deps flag: ./symfony plugin:install --install-deps sfGuargPlugin ./symfony plugin:install -d sfGuargPlugin By default, the PEAR channel used is symfony-plugins (plugins.symfony-project.org). You can specify another channel with the channel option: ./symfony plugin:install --channel=mypearchannel sfGuargPlugin ./symfony plugin:install -c mypearchannel sfGuargPlugin … International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 36.
    Task Calls inyour Code # Somewhere in your code $task = new sfCacheClearTask($dispatcher); $task->run(); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 37.
    symfony 1.0 Dependencies sfView sfResponse sfContext sfI18N sfRequest sfLogger sfStorage sfUser sfRouting Cleanup dependency Dependencies singleton International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 38.
    symfony 1.1 Dependencies sfRequest sfView sfResponse sfI18N sfEventDispatcher sfUser sfRouting sfStorage sfLogger sfContext Cleanup dependency Dependencies singleton International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 39.
    sfEventDispatcher •Based on Cocoa Notification Center // sfUser $event = new sfEvent($this, 'user.change_culture', array('culture' => $culture)); $dispatcher->notify($event); // sfI18N $callback = array($this, 'listenToChangeCultureEvent'); $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 International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 40.
    Form & ValidationFramework • Not a refactoring • A different approach • Almost no shared code • Symfony 1.1 can still use the old system – set compat_10: on in settings.yml (off by default) – you can use the new and the old system in the same application – symfony 1.0 sfValidator class has been renamed to sfValidatorBase to avoid class name conflicts International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 41.
    The New Approach • Form as a first class object • Widget classes for form helpers • Validators validate arrays (it doesn’t care if it comes from a request, an XML file or a model object) • A decoupled system – Can be used without any other symfony class – 3 differents sub-systems • Validators: can be used by themselves • Widgets: can be used by themselves • Form: glue between validators and widgets International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 42.
    Validators •Every validator extends sfValidator • A validator cleans and validates input values • sfValidator provides some common features – required (validation) > true by default – trim (cleaning) > false by default • Each validator can have options: – sfValidatorString: max_length, min_length International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 43.
    Validators //create a new string validator $v = new sfValidatorString(array('min_length' => 4)); // clean and validate data $value = $v->clean('Fabien'); // returns the input value $value == 'Fabien' // change some option $v->setOption('trim', true); $value = $v->clean(' Fabien '); // trims the input value $value == 'Fabien' Validator objects are stateless International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 44.
    Custom Validator class CustomValidator extends sfValidator { protected function configure($options = array(), $messages = array()) { $this->setOption('min_length', null); $this->setMessage('min_length', 'Too short.'); } protected function doClean($value) { if (strlen($value) < $this->getOption('min_length')) { throw sfValidatorError($this, 'min_length', array('value' => $value)); } return $value; } } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 45.
    Validators $v= new sfValidatorString(array('min_length' => 4)); // throws a sfValidatorError $v->clean('Jon'); $e->getCode() == 'min_length' $e->getMessage() == '"Jon" is too short (4 characters min).' $v->setMessage('min_length', 'Too short (%name%).'); $e->getMessage() == 'Too short (Jon).' Use error codes or error messages International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 46.
    Errors Internationalization $v = new sfValidatorString(array('min_length' => 4)); try { $value = $v->clean('Jon'); } catch (sfValidatorError $e) { echo $i18n->__($e->getMessageFormat(), $e->getArguments())."n"; // or echo $i18n->__($e->getCode(), $e->getArguments())."n"; } Error messages are i18n ready International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 47.
    sfValidator(All|Any) •You can aggregate validators to create a new validator: – sfValidatorAll: All validators must pass – sfValidatorAny: At least one validator must pass $v1 = new sfValidatorString(array('min_length' => 4)); $v2 = new sfValidatorString(array('max_length' => 10)); $v = new sfValidatorAll(array($v1, $v2)); // values must validate both validators $v->clean('Fabien'); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 48.
    sfValidator(All|Any) •As they are validators themselves, you can create complex logic to validate your values: // v1 && (v2 || v3) $v = new sfValidatorAll( array($v1, sfValidatorAny($v2, $v3)) ); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 49.
    sfValidatorSchema •A validator schema is composed of fields • A field is a named validator $v1 = new sfValidatorString(array('min_length' => 4)); $v = new sfValidatorSchema(array( 'first_name' => $v1, 'last_name' => $v1, )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 50.
    sfValidatorSchema •The clean() method takes an array of named values and returns an array: $v->clean(array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 51.
    sfValidatorSchema •It collects all errors for all fields • It throws an exception with all errors sfValidatorErrorSchema: first_name: "Jon" is too short (4 characters min). last_name: "Jon" is too short (4 characters min). in ….php on line … International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 52.
    sfValidatorSchema •A named validator does not have access to the whole array of inputs, just its named value • Two special validators: – _pre_validator and _post_validator – They takes the array of values as input – They throw « global » errors Isolation International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 53.
    sfValidatorSchema $v2= new sfValidatorSchemaCompare( 'password', '==', 'password_bis' ); $v = new sfValidatorSchema(array( 'password' => $v1, 'password_bis' => $v1, '_post_validator' => $v2, )); sfValidatorErrorSchema:   "pass" does not match "word". in ….php on line … International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 54.
    sfValidatorSchema •sfValidatorSchema is a sfValidator, so you can nest them $authorValidator = new sfValidatorSchema(array( 'first_name' => $v1, 'last_name' => $v1, )); $bookValidator = new sfValidatorSchema(array( 'title' => $v1, 'sub_title' => $v1, 'author' => $authorValidator, )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 55.
    sfValidatorSchema $bookValidator->clean(array( 'title' => 'The symfony book', 'sub_title' => 'The definitive guide', 'author' => array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', ), )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 56.
    sfValidatorSchema •Secure by default – allow_extra_fields (false by default) – filter_extra_fields (true by default) • If you pass a value with no matching validator, sfValidatorSchema will throw an error • If you switch allow_extra_fields to true, then extra fields won’t trigger an error but will be removed from the cleaned values • If you also switch filter_extra_fields to false, then extra fields won’t be removed from the cleaned values International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 57.
    sfValidatorSchemaForEach $choiceValidator= new sfValidatorSchema(array( 'choice' => $v1, )); $choicesValidator = new sfValidatorSchemaForEach($choiceValidator, 3); $pollValidator = new sfValidatorSchema(array( 'question' => $v1, 'choices' => $choicesValidator, )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 58.
    sfValidatorSchemaForEach $pollValidator->clean(array( 'question' => 'Do you like symfony?', 'choices' => array( array('choice' => 'Yes'), array('choice' => 'This is the best'), array('choice' => 'A lot'), ), )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 59.
    Validators as Strings $postValidator = new sfValidatorOr(array( new sfValidatorSchemaFilter('age', new sfValidatorInteger(array('min' => 18))), new sfValidatorAll(array( new sfValidatorSchemaFilter('age', new sfValidatorInteger(array('max' => 18))), new sfValidatorSchemaFilter('is_young', new sfValidatorBoolean(array('required' => true))), )), )); $string = $postValidator->asString(); ' age:Integer({min: 18}) or age:Integer({max: 18}) and is_young:Boolean({required: true}) ' $postValidator = new sfValidatorFromDescription($string); $postValidator->asPhp(); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 60.
    Available Validators • Boolean • All • Choice • Any • ChoiceMany • Callback • Date • Decorator • DateTime • Pass • Email • FromDescription • Integer • Number • Schema • Regex • SchemaForEach • String • SchemaCompare • Time • SchemaFilter • Url International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 61.
    Widgets •Every widget extends sfWidget • A widget is an object that can be rendered as an HTML string • sfWidget provides some common features – renderTag() – renderContentTag() – Charset support – XHTML or HTML closing tags • Each widget can have HTML attributes: – Takes care of escaping – Fixes double escaping problems International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 62.
    sfWidgetForm •Base class for all form widgets • Some more properties like isHidden() • Generates an id if none provided and the widget has a name – Default id is the widget name – Can be customized $w = new sfWidgetFormInput(); $w->setIdFormat('id_%s'); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 63.
    sfWidgetForm classes // create a new input widget $w = new sfWidgetFormInput(); // render widget echo $w->render('first_name', 'Fabien'); // returns the widget as HTML <input type="text" name="first_name" value="Fabien" id="first_name" /> // change some attributes $w->setAttribute('class', 'foo'); <input … class="foo" /> Widget objects are stateless International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 64.
    sfWidgetForm classes // add default HTML attributes $w = new sfWidgetFormInput(array('class' => 'foo')); // render widget with some HTML attributes echo $w->render('first_name', 'Fabien', array('class' => 'foo')); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 65.
    sfWidgetFormSchema •A widget schema is composed of fields • A field is a named widget $w1 = new sfWidgetFormInput(); $w = new sfWidgetFormSchema(array( 'first_name' => $w1, 'last_name' => $w1, )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 66.
    sfWidgetFormSchema •The render() method takes an array of named values and returns an HTML string: $w->render(null, array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', )); • You can also render individual fields International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 67.
    sfWidgetFormSchema •sfWidgetFormSchema is a sfWidget, so you can nest them $authorWidget = new sfWidgetFormSchema(array( 'first_name' => $w1, 'last_name' => $w1, )); $bookWidget = new sfWidgetFormSchema(array( 'title' => $w1, 'sub_title' => $w1, 'author' => $authorValidator, )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 68.
    sfWidgetFormSchema $bookWidget->render(null,array( 'title' => 'The symfony book', 'sub_title' => 'The definitive guide', 'author' => array( 'first_name' => 'Fabien', 'last_name' => 'Potencier', ), )); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 69.
    sfWidgetFormSchema •The render() method can also render – Errors – Nested form schema • The render() method uses a formatter class – sfWidgetFormSchemaFormatterList – sfWidgetFormSchemaFormatterTable – Or build your own International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 70.
    Forms •A sfForm is the glue between – A validator schema – A widget schema $validator = new sfValidatorSchema(); $widget = new sfWidgetFormSchema(); $form = new sfForm(); $form->setValidatorSchema($validator); $form->setWidgetSchema($widget); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 71.
    sfForm •You can also create your own form class class BookForm extends sfForm { public function configure() { $validator = new BookValidatorSchema(); $widget = new BookWidgetFormSchema(); $this->setValidatorSchema($validator); $this->setWidgetSchema($widget); } } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 72.
    A Form ina Form $authorForm = new AuthorForm(); $bookForm->embedForm($authorForm); $choiceForm = new ChoiceForm(); $pollForm->embedFormForEach($choiceForm, 4); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 73.
    sfForm $form= new AuthorForm(); $input = array('first_name' => 'Fabien', 'last_name' => 'Potencier'); $form->bind($input); if ($form->isValid()) { // do something with the cleaned values $form->getValues(); } else { // do something with the errors $form->getErrorSchema(); } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 74.
    sfForm •sfForm can take default values $form = new AuthorForm(array('first_name' => 'Fabien')); • sfFormField objects are widgets bound to the input or default values echo $form->getFormField('first_name')->render(); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 75.
    sfForm $form= new AuthorForm(); echo $form->getFormField('first_name')->render(); // === echo $form['first_name']->render(); // === echo $form['first_name']; // name and value are bound to the sfFormField object <input type="text" name="first_name" value="Fabien" id="first_name" /> // add some HTML attributes echo $form['first_name']->render(array('class' => 'foo')); International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 76.
    For the laziest echo $form International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 77.
    Customize your Forms $bookForm['author'] <table> $bookForm['author']['first_name']->renderRow() <tr> <th> $bookForm['author']['first_name']->renderLabel() <label for="book_author_first_name">First Name</label> $bookForm['author']['first_name']->renderLabelName() </th> <td> <ul class="error_list"> <li>Required.</li> $bookForm['author']['first_name']->renderError() </ul> <input type="text" name="book[author][first_name]" id="book_author_first_name" /> $bookForm['author']['first_name'] </td> </tr> ... </table> International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 78.
    Forms: In asymfony Action class bookActions extends sfActions { public function executeEdit($request) { $this->form = new AuthorForm(); if ($request->isMethod('post')) { $this->form->bind($request->getParameter('book')); if ($this->form->isValid()) { $values = $this->form->getValues(); $this->redirect('@homepage'); } } } } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 79.
    sfFormPropel •Generated forms for Propel objects • Fully customizable • Introspects the Propel schema – Maps Propel/Creole types to symfony validators and widgets – Foreign keys – Many to many relationships – Internationalized tables International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 80.
    sfFormPropel classbookActions extends sfActions { public function executeEdit($request) { $this->book = BookPeer::retrieveByPk($request->getParameter('id')); $this->form = new AuthorForm($this->book); if ($request->isMethod('post')) { $this->form->bind($request->getParameter('book'); if ($this->form->isValid()) { $book = $this->form->save(); $this->redirect('@book?id='.$book->getId()); } } } } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 81.
    Customize Propel Forms class BookForm extends BaseBookForm { public function configure() { $this->embedI18n(array('en', 'fr')); $this->widgetSchema['en']->setLabel('en', 'English'); unset($this['created_at']); $this->validatorSchema['foo'] = new sfValidatorPass(); $this->widgetSchema['foo'] = new sfWidgetIdentity(); $this->setDefault('published_on', time()); } } International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 82.
    A Professional WebFramework • Built from experience • 1.0 stable, maintained with commercial support • Growing community – Developers in more than 80 countries – 200 000 visitors per month on symfony-project.com – 200 plugins in just 8 months • Open-Source Documentation – The book (450 pages - GFDL) – Askeet Tutorial (250 pages) International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 83.
    Yahoo! uses symfony • Yahoo! Bookmarks – 20 millions users – Web 2.0 / AJAX • del.icio.us – New beta on symfony – preview.delicious.com International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 84.
    Next symfony Workshops En français : Paris, France - Dec 05, 2007 In English : Paris, France - Feb 13, 2008 More info on www.sensiolabs.com International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 85.
    Join Us • Sensio Labs is recruiting in France – project managers – web developers • You have a passion for the web? – Web Developer : You have a minimum of 3 years experience in web development with Open-Source projects and you wish to participate to development of Web 2.0 sites using the best frameworks available. – Project Manager : You have more than 5 years experience as a developer and/or a project manager and you want to manage complex Web projects for prestigious clients. International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com
  • 86.
    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/ International PHP 2007 Conference www.symfony-project.com fabien.potencier@sensio.com www.sensiolabs.com