SlideShare a Scribd company logo
1 of 76
Download to read offline
RESTful avec symfony et Symfony2
  Damien Alexandre, Xavier Lacot – 03 mars 2011




Symfony Day – 4. Juni 2009
Clever Age | Xavier Lacot
Clever Age

   Création en 2001 à Paris par des managers expérimentés
   Plusieurs agences en France :
    
        Paris                                      
                                                       Bordeaux
    
        Lyon                                       
                                                       Nantes

   Valeurs fondatrices : Indépendance, Veille technologique,
    Conviction
    
        Quelques chiffres :
        
            CA 2009 : 6 M€
        
            Effectif au 01/03/2011 : 90 personnes
    
        Notre Mission : « Concevoir des systèmes informatiques flexibles en
        limitant la dépendance vis-à-vis des prestataires et des éditeurs »

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                   2
Qui sommes nous ?

   Damien :
       
           Spécialiste Web full stack
       
           Plusieurs contributions, notamment à symfony
       
           Expert technique PHP chez Clever Age
       
           Éleveur de poneys
       
           http://twitter.com/damienalexandre

   Xavier :
       
           Expert Frameworks, développeur symfony depuis fin 2005
       
           Plusieurs contributions (plugins, doc, patches, etc.)
       
           Leader PHP chez Clever Age + en charge du pôle d'expertise
       
           Vice-président de l'AFUP
       
           http://twitter.com/xavierlacot
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                    3
Sommaire


         2 ou 3 rappels sur REST

      
          symfony, un framework RESTFul

         Et dans Symfony2 ?
          
              howto
          
              benchmarks

         Autour de Symfony – Plugins et Bundles


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot               4
2 ou 3 rappels sur REST



RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                  5
2 ou 3 rappels sur REST


         Développement d'APIs : de nombreux cas d'utilisation :
          
              favoriser l'interopérabilité
              
                  contextes répartis
              
                  plusieurs applications différentes
              
                  plusieurs langages
          
              exposer un référentiel de données
          
              exposer des données publiques (OpenData, etc.)




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                  6
2 ou 3 rappels sur REST


         Un service Web
          
              Un fournisseur (le « service »)
          
              Un agent (le « client »)




                                                   requête

                client                                               service
                                                   réponse




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                        7
REST et SOAP sont sur un bateau


         REST
          
              Representational state transfer
          
              exposition de ressources
          
              "colle" à HTTP
          
              simple et compréhensible

         WS-*
          
              SOAP + WSDL
          
              approche plus standardisée
          
              plus verbeux
          
              moins adapté à la description de ressources


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                      8
De l'utilisation d'HTTP avec REST


         REST != HTTP
         mais REST <3 HTTP :
          
              les codes de réponse
          
              les URL
          
              les content-type
          
              les méthodes




                                                   Roy Fielding
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                                                     9
                                                                     http://www.flickr.com/photos/bblfish/637617442/
De l'utilisation d'HTTP avec REST


         Les verbes :
          
              GET : Obtenir une ressource
          
              POST + http content : Créer une ressource
          
              PUT + http content : Modifier une ressource
          
              DELETE : Supprimer une ressource




          
              HEAD : Obtenir les headers d'une ressource
          
              OPTIONS : Obtenir les méthodes acceptés




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                       10
De l'utilisation d'HTTP avec REST


         Les codes de succès :
          
              200 : OK
          
              201 : Créé



         Les codes de redirection :
          
              301 : Ressource déplacée
          
              304 : Ressource non modifiée




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                       11
De l'utilisation d'HTTP avec REST


         Les codes d'erreur client :
          
              400 : Bad Request
          
              404 : Ressource introuvable
          
              405 : Méthode non autorisé
          
              406 : Non acceptable
          
              418 : I'm a teapot



         Les codes d'erreur serveur :
          
              500 : Erreur générale




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                       12
De l'utilisation d'HTTP avec REST


         L'URL
          
              Uniform Resource Locator



         http://domaine.tld/ressource/id : Désigne une ressource
         http://domaine.tld/ressource : Désigne toutes les
          ressources




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                       13
symfony 1, un framework RESTful

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                  14
Introducing Pony !




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                  15
Ressource Pony


  Pony                                             Pony:
  id                             integer             columns:
                                                       name:
  name                           string(255)             type: string(255)
  picture_url                    string(255)           picture_url:
                                                         type: string(255)
  description                    text                  description:
  slug                           string(255)             type: text
                                                     actAs:
                                                       Sluggable:
                                                         unique: true
                                                         fields: [name]
                                                         canUpdate: false




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                  16
Ressource Pony


         New forest => new-forest
         Magic Pony => magic-pony


         GET http://domain.tld/pony/new-forest.xml
          ou
         GET http://domain.tld/pony/8.xml


         Le routing décide.


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                       17
Le routing dans symfony 1


         gérer le plan d'adressage d'une application symfony
          
              jolies urls
          
              obfuscation des technos employées
          
              validation des paramètres passés
          
              découpler les urls exposées de l'organisation interne du projet

         routing.yml
         plusieurs classes de routing
          
              sfRequestRoute / sfPatternRoute / sfRouteCollection
          
              sfDoctrineRoute / sfDoctrineRouteCollection
          
              etc...

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                     18
sfDoctrineRoute


         routing.yml
           pony_show:
             url: /pony/:slug.:sf_format
             param: { module: pony, action: show, sf_format: xml }
             class: sfDoctrineRoute
             options: { model: Pony, column: slug, type: object }
             requirements: { sf_method: GET, sf_format: (xml|json|yml) }



         actions.class.php

               public function executeShow(sfWebRequest $request)
               {
                 $pony = $this->getRoute()->getObject();
                 // etc.
               }

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                19
Routing et format


         sf_format est html par défaut
           pony_show:
             url: /pony/:slug.:sf_format
             param: { module: pony, action: show, sf_format: xml }
             class: sfDoctrineRoute
             options: { model: Pony, column: slug, type: object }
             requirements: { sf_method: GET, sf_format: (xml|json|yml) }



         actions.class.php

               public function executeShow(sfWebRequest $request)
               {
                 $format = $request->getRequestFormat();
                 // etc.
               }

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                20
Les formats



   GET http://domain/pony/new-forest.xml


    <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
    <response>
      <name><![CDATA[New Forest]]></name>
      <picture_url>
        <![CDATA[http://www.feracheval.com/nvxsite/newforest.jpg]]>
      </picture_url>
      <description>
        <![CDATA[Le poney New-Forest...]]>
      </description>
      <slug><![CDATA[new-forest]]></slug>
    </response>




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                           21
Les formats



   GET http://domain/pony/new-forest.json




    {
        "name": "New Forest",
        "picture_url": "http://www.feracheval.com/nvxsite/newforest.jpg",
        "description": "Le poney New-Forest...",
        "slug": "new-forest"
    }




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                 22
Format et template


         Le template utilisé change suivant sf_format :
          
              html => showSuccess.php
          
              xml => showSuccess.xml.php
          
              json => showSuccess.json.php

         Content-Type automatique pour les formats suivants :
          
              txt, js, css, json, xml, rdf, atom.

         Exemple :
          
              GET http://domain/pony/connemara.json
          
              Content-Type: application/json



RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                           23
Format et template


          showSuccess.json.php

   {
       "name": <?php echo json_encode($pony->getName()) ?>,
       "picture_url": <?php echo json_encode($pony->getPictureUrl(ESC_RAW)) ?>,
       "description": <?php echo json_encode($pony->getDescription(ESC_RAW)) ?>,
       "slug": <?php echo json_encode($pony->getSlug())."n" ?>
   }




          Bon courage pour les listes d'objets...




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                        24
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot        25
Format sans template


         Sans template, on spécifie le Content-Type à la main :
              $format = $request->getRequestFormat();
              $this->getResponse()->setContentType(
                 $request->getMimeType($format)
              );
              return sfView::NONE;

         La solution consiste à employer un Serializer...
          
               Pas de serializer dans symfony 1
          
               Pas de serializer XML dans Zend
          
               Doctrine peut le faire pour vous :

              $this->getResponse()->setContent(
                 $query->execute()->exportTo($format)
              );
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                               26
Format sans template


         Le cas particulier de json...
              $this->getResponse()->setContent(
                 json_encode($pony->toArray())
              );




         facile
         plus rapide (natif)
         écriture compacte (moins verbeux que XML)
          
              Cool pour la bande passante

         Javascript compliant

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                          27
Validation des paramètres


         Exposer une interface = nécessité de valider les entrées
          
              en GET : valider les restrictions
          
              http://api.domain.tld/pony.json?country=france
          
              que doit faire l'API sur un appel à l'url suivante ?
              http://api.domain.tld/pony.json?speaks=german



         en écriture (POST / PUT) :
          
              valider la présence du payload (HTTP content)
          
              format attendu
          
              contenu du payload


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                               28
Validation des paramètres


         symfony 1 offre une série complète de validateurs
          
              Email
          
              Url
          
              Date
          
              etc.

      public function executeShow(sfWebRequest $request)
      {
        $name = $request->getParameter('name');
        $validator = new sfValidatorString(array('required' => true));

          // throws an exception if not valid
          $cleaned = $validator->clean($name)

          // etc.
      }

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                               29
REST en haute Sécurité


         Plusieurs approches de la sécurité :
          
              qui a accès (authentification) ?
          
              à quel rythme / fréquence (throttling) ?

         Authentification :
          
              approche naïve par preExecute
          
              approche OAuth

         Throttling
          
              aidez vous d'Apache – mod_cband http://codee.pl/cband.html




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                30
Authentification d'API par api_key


         ajout d'un paramètre à chaque requête
          
              dans l'url
          
              ou en header http



         approche dite "naïve" :
          
              pas très sécurisée (man in the middle = le secret est connu)
          
              mode d'authentification persistant dans le temps (clé
              compromise → changement de clé)




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                        31
Authentification d'API par api_key



    class ponyApiActions extends sfActions
    {
      public function preExecute()
      {
        $request = $this->getRequest();
        $key = $request->getParameter('ApiKey');

            if (!Doctrine::getTable('ApiKey')->findOneByApiKey($key))
            {
               $this->getResponse()->setStatusCode(401);
               throw new sfException('Invalid authentication credentials.');
            }
        }

        // etc
    }




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                        32
Authentification d'API par OAuth


         OAuth :
          
              plus robuste et rassurant
          
              plus long à mettre en place
          
              protocole adapté à la gestion de l'authentification sur APIs




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                      33
Authentification d'API par OAuth


                                               redirection vers page de login
    Application
      cliente                                           réponse (code)



               Passage du code
                                                                                     API
                                                        getToken (code)             REST
                                                        réponse (token)

    Application
      serveur                                         appel de l'API (token)

                                                   réponse (donnée demandée)




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                                34
Gestion du cache


         Du cache sur une API ?
          
               Oui, même de courte durée

         cache.yml

              default:
                enabled:     true
                with_layout: true
                lifetime:    120                   # 2 minutes cache



         settings.yml
              prod:
                .settings:
                  cache:                             true


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                          35
Gestion du cache


         Bootstrap = config, plugins, config BDD, routing,
          autoloading, session...




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                         36
Gestion du cache


         Invalidation du cache


          if ($cache = $this->getContext()->getViewCacheManager())
          {
            $cache->remove('pony/index');
            $cache->remove('pony/show?slug='.$slug);
          }




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                             37
Et avec Symfony2 ?




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                         38
Pony2, des poneys en Doctrine2


      entité Doctrine2 = modèle Doctrine 1
      définition du schéma en PHP, yml ou XML
      aussi possible à l'aide des annotations
      pas d'héritage de Doctrine_Record :
       
           src/CleverAge/SymfponyBundle/Entity/Pony.php

             class Pony
             {
                 protected              $id;
                 protected              $name;
                 protected              $picture_url;
                 protected              $description;
                 protected              $slug;
             }


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                         39
Pony2, des poneys en Doctrine2


         les annotations permettent d'indiquer à Doctrine comment
          persister les données
             /**
               * @orm:Table(name="pony")
               * @orm:Entity
               */
             class Pony
             {
                  /**
                   * @orm:Column(name="id", type="integer")
                   * @orm:Id
                   * @orm:GeneratedValue(strategy="IDENTITY")
                   */
                  protected $id;

                   /**
                    * @orm:Column(name="name", type="string", length=110)
                    */
                   protected $name;

                   // etc.

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                    40
Pony2 - Générer getter et setters


   $ php app/console doctrine:generate:entities CleverAgeSymfponyBundle

   Generating entities for "CleverAgeSymfponyBundle"
     > generating CleverAgeSymfponyBundleEntityPony


         /**
           * Get id
           * @return integer $id
           */
         public function getId()
         {
              return $this->id;
         }

             /**
               * Set name
               * @param string $name
               */
             public function setName($name)
             {
                   $this->name = $name;
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
             }
Clever Age | Damien Alexandre, Xavier Lacot                                 41
Le routing avec Symfony2


       app/config/routing.yml :
       symfpony:
           resource: "@CleverAgeSymfponyBundle/Resources/config/routing.yml"




       src/CleverAge/SymfponyBundle/Resources/config/routing.yml :

       pony_list:
           pattern:      /pony.{_format}
           defaults:
               - _controller: CleverAgeSymfponyBundle:Default:index
               - _format: xml
           requirements: { _format: (xml|json), _method: GET }




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                    42
Le routing avec Symfony2


       Dumpable en RewriteRule Apache :
       $ php app/console router:dump-apache

       RewriteCond %{REQUEST_METHOD} ^(GET) [NC]
       RewriteCond %{PATH_INFO} ^/pony(?:.((xml|json)))?$
       RewriteRule .* app.php
       [QSA,L,E=_ROUTING__route:pony_list,E=_ROUTING__format:
       %1,E=_ROUTING__controller:CleverAgeSymfponyBundleControllerDefaul
       tController::indexAction,E=_ROUTING__format:xml]

       RewriteCond %{REQUEST_METHOD} ^(GET) [NC]
       RewriteCond %{PATH_INFO} ^/pony/([a-z0-9-]+)(?:.((xml|json)))?$
       RewriteRule .* app.php
       [QSA,L,E=_ROUTING__route:pony_show,E=_ROUTING_slug:
       %1,E=_ROUTING__format:
       %2,E=_ROUTING__controller:CleverAgeSymfponyBundleControllerDefaul
       tController::showAction,E=_ROUTING__format:xml]

       etc.
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                   43
Le routing avec Symfony2

      Pas de sfDoctrineRoute :'(
      pony_show:
          pattern:      /pony/{slug}.{_format}
          Defaults:
              - _controller: CleverAgeSymfponyBundle:Default:show
              - _format: xml
          requirements: { _format: (xml|json), _method: GET, slug: "[a-z0-9-]+" }


      Mais FrameworkExtraBundle est là...
       
           http://bundles.symfony-reloaded.org/frameworkextrabundle/
       
           ParamConverter FTW :

            /**
              * the Pony is automaticaly fetched by the ParamConverter.
              * @param Pony $pony
              * @param string $_format
              */
            public function showAction(Pony $pony, $_format)
            {
RESTful avec symfony do Symfony2 – 3 mars 2011
                // 1 et what you want with $pony
Clever Age | Damien Alexandre, Xavier Lacot                                         44
            }
Serializer component




                                                   Finally !
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                      45
Les “normalizers”

      Transformation ressource → array
       
           CustomNormalizer
           
               normalize() et denormalize() sont implémentées sur l'objet
       
           GetSetMethodNormalizer
           
               inspecte les getters et les setters de l'objet




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                    46
Les “encodeurs”

      Transformation array → format
       
           JsonEncoder
           
               simple implémentation de json_encode
       
           XmlEncoder
           
               Utilisation de DOMDocument
       
           [insérer un format ici]Encoder
           
               Forkez moi !




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                        47
Implémentation de CustomNormalizer



use SymfonyComponentSerializerNormalizerNormalizableInterface;
use SymfonyComponentSerializerNormalizerNormalizerInterface;

class Pony implements NormalizableInterface
{
    function normalize(NormalizerInterface $normalizer, $format, $properties = null)
    {
        return array(
            'name' => $this->getName(),
            'picture_url' => $this->getPictureUrl(),
            'description' => $this->getDescription(),
            'slug' => $this->getSlug(),
        );
    }

      // etc.
}




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                            48
Utilisation du Serializer



$serializer = new SerializerSerializer();

$serializer->addNormalizer(
    new SerializerNormalizerCustomNormalizer()
);

$serializer->setEncoder(
    'xml',
    new SerializerEncoderXmlEncoder()
);

$serializer->encode($pony, 'xml');




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                               49
Utilisation du Serializer



     <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
     <response>
       <name><![CDATA[New Forest]]></name>
       <picture_url>
         <![CDATA[http://www.feracheval.com/nvxsite/newforest.jpg]]>
       </picture_url>
       <description>
         <![CDATA[Le poney New-Forest...]]>
       </description>
       <slug><![CDATA[new-forest]]></slug>
     </response>




     $xmlEncoder = new SerializerEncoderXmlEncoder();
     $xmlEncoder->setRootNodeName('Pony');

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                               50
Validation des entrées


         Comme avec symfony 1 :
          
              Nécessaire de valider les entrées
          
              Symfony2 fournit toute une série de validateurs
              
                  classes étendant SymfonyComponentValidatorConstraint
          
              aussi complet que les validators symfony1

         Doctrine2 ne fournit aucun validateur (par choix)




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                  51
Déclaration des validateurs


         Format de déclaration des validateurs au choix
          
              YML
          
              XML
          
              PHP
          
              annotations

          class Pony
          {
              /**
               * @validation:NotBlank()
               * @validation:MinLength(3)
               */
              private $name;

                ...
          }

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                 52
Exécution de la validation


         activer le service de validation
           # app/config/config.yml
           framework:
               validation:
                   enabled: true



         valider dans le contrôleur

           $validator = $container->get('validator');

           if (0 === count($validator->validate($pony)))
           {
               // persist the Pony
           }



RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                53
Le cache dans Symfony2



         Présentation de Fabien demain à 11h30
          
              Sujet majeur !
          
              Conférence pas trop tôt
          
              Conférencier pas trop mauvais :-)




         Quelques détails quand même...


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                            54
Cache HTTP


         Le cache est vérifié avant d'instancier le framework.




                        gateway cache,
                        ou reverse proxy




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                        55
Cache HTTP


         Le cache est vérifié avant d'instancier le framework.




                        gateway cache,
                        ou reverse proxy




                                                   ou   ou   Symfony2
                                                             HttpCache
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                              56
Expiration du cache HTTP


              $response = new Response();
              $response->setPublic();
              $response->setSharedMaxAge(600);




         Comme pour symfony1 mais sans invalidation.
          
              Impossible d'invalider programmatiquement le cache
          
              Possible de définir une durée de vie




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                              57
Validation du cache HTTP


      $response = new Response();
      $response->setETag(md5(serialize($pony)));

      if ($response->isNotModified($request))
      {
           return $response; // 304
      }
      else
      {
          $response->setContent(
             $this->getSerializer($_format)
                         ->encode($pony, $_format)
            );
            return $response;
      }



         Ressource expirée ?
                                                   Le meilleur des deux mondes.
         Etag modifié ?
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                       58
Les très nécessaires benchmarks

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                      59
Méthodologie


         même service Pony au format json :
          
              symfony 1 mode dev
          
              symfony 1 mode prod sans cache
          
              symfony 1 mode prod avec cache
          
              Symfony2 mode dev
          
              Symfony2 mode prod sans cache
          
              Symfony2 mode prod sans cache (format XML)
          
              Symfony2 mode prod avec cache HTTP (PHP)

         mêmes fonctionnalités
         mesures faites à l'aide de siege -
          http://www.joedog.org/index/siege-home
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                          60
Résultats bruts


         Lecture :
          
              rel = rapport au moins performant,
          
              avg = requêtes par seconde, en moyenne sur les 3 runs




framework                                  |      rel |      avg |        1 |        2 |        3
------------------------                   | -------- | -------- | -------- | -------- | --------
Symfony2Cache                              |   9.6251 |   373.87 |   369.49 |   373.75 |   378.36
symfony1Cache                              |   2.2480 |    87.32 |    86.25 |    87.78 |    87.94
Symfony2ProdJson                           |   1.7970 |    69.80 |    69.64 |    70.71 |    69.04
Symfony2ProdXml                            |   1.6106 |    62.56 |    60.92 |    62.73 |    64.03
symfony1Prod                               |   1.2769 |    49.60 |    48.41 |    50.60 |    49.80
Symfony2Dev                                |   1.1467 |    44.54 |    43.43 |    44.09 |    46.09
symfony1Dev                                |   1.0000 |    38.84 |    37.85 |    40.90 |    37.78




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                                     61
Commentaires


         Symfony2 + cache HTTP est
          
              4,5 fois plus performant que symfony 1 avec cache
          
              7 fois plus performant que Symfony2 en mode dev (avec profiler)
          
              10 fois plus que symfony 1 en mode dev

         Symfony2 sans cache : 40% de gains de perfs face à symfony 1
         le profiler Symfony2 est génial mais couteux
         Préférez json à XML pour vos APIs
         Chiffres à prendre avec précaution
          
              Framework pas terminé vs. Framework vieux de 5 ans
          
              Attention à la configuration

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                 62
Traces Xdebug et XHProf


                              Nos premiers tests : Symfony2 était
                                    plus lent que symfony 1




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                 63
Traces Xdebug et XHProf


         Xdebug
          
              Kudos à Derick Rethans
          
              Super outil depuis 2002
          
              Traces exploitables par KcacheGrind ou WinCacheGrind
          
              http://www.xdebug.org/

         XHProf
          
              Facebook, Mars 2009
          
              Très pratique aussi
          
              Interface Web de consultation des traces
          
              Graphes d'exécution avec graphviz
          
              https://github.com/facebook/xhprof
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                             64
Traces Xdebug et XHProf

   Vraiment ? Regardons la trace XHProf




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                             65
Traces Xdebug et XHProf




         Annotations Doctrine parsées à chaque requête !
           # app/config/config.yml
           doctrine:
               orm:
                    metadata_cache_driver: apc

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                             66
Traces Xdebug et XHProf


         Après :




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                             67
Encore plus parlant avec XDebug




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                     68
Encore plus parlant avec XDebug




                                                        Interprétation
                                                        de la Query


                                      Parsing des
                                      annotations Doctrine

                                            # app/config/config.yml
                                            doctrine:
                                                orm:
                                                     metadata_cache_driver: apc
                                                    query_cache_driver: apc

RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                       69
Memory usage


         memory_get_peak_usage()
             
                 symfony 1 mode dev : 4 581 856 b
             
                 symfony 1 mode prod sans cache : 4 282 944 b
             
                 symfony 1 mode prod avec cache : 3 335 688 b
             
                 Symfony2 mode dev : 3 567 632 b
             
                 Symfony2 mode prod sans cache d'annotations : 3 027 240 b
             
                 Symfony2 mode prod sans cache HTTP : 2 800 600 b
             
                 Symfony2 mode prod avec cache HTTP : 1 012 496 b




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                   70
Autour de Symfony
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                        71
Des plugins pour symfony 1


         sfDoctrineRestGeneratorPlugin :
          
              admin generator like (generator.yml)
          
              Serialization                        
                                                       facile à étendre
          
              Validation                           
                                                       complexe mais puissant




         sfRestWebServicePlugin :
          
              Super-module                         
                                                       Template
          
              facile à mettre en place             
                                                       route api/*
          
              support de PUT hacké (POST et sf_method=PUT)


RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                      72
Des bundles pour Symfony2


         EverzetRestfulControllersBundle
          
              création automatique des routes à partir de noms d'actions
          
              pas de lien avec le modèle (actions à développer)
          
              https://github.com/everzet/EverzetRestfulControllersBundle
          # app/config/routing.yml
          ponys:
            type:        restful
            resource:    CleverAgeSymfponyBundleControllerPonysController



                                                   class PonysController extends Controller
      [GET]                                        {
                                                       public function getPonyAction($slug)
      /pony/connemara                                  {
                                                         // etc.
                                                       }
      [POST]                                          public function postPonysAction()
      /ponys                                          {
RESTful avec symfony 1 et Symfony2 – 3 mars 2011        // etc.
Clever Age | Damien Alexandre, Xavier Lacot
                                                      }
                                                                                              73
Pour conclure...


                       REST c'est bien. Et avec symfony c'est facile.
                                 Avec Symfony2, c'est encore mieux.




                                      http://symfpony-project.org/
            https://github.com/xavierlacot/symfpony-project.org
             https://github.com/xavierlacot/symfpony-benchmark
RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                        74
Time to REST

                                                   Des questions ?




RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                          75

                                                       Récompense des projets Web innovants
                                                       
                                                           HTML5
                                                       
                                                           OpenData
                                                       
                                                           Mobilité
                                                       
                                                           etc.



                                                   
                                                       Appel à candidatures :
                                                       candidat_awards@clever-age.com


                                                   
                                                       Détails sur http://awards.clever-age.com/



RESTful avec symfony 1 et Symfony2 – 3 mars 2011
Clever Age | Damien Alexandre, Xavier Lacot                                                   76

More Related Content

Viewers also liked (20)

24
2424
24
 
04 Evaluación políticas públicas en francia - OIeau
04 Evaluación políticas públicas en francia - OIeau04 Evaluación políticas públicas en francia - OIeau
04 Evaluación políticas públicas en francia - OIeau
 
Présentation Agence Urbaine de Tánger 2012
Présentation Agence Urbaine de Tánger 2012 Présentation Agence Urbaine de Tánger 2012
Présentation Agence Urbaine de Tánger 2012
 
18
1818
18
 
Les sources d'énergie
Les sources d'énergieLes sources d'énergie
Les sources d'énergie
 
9
99
9
 
Transition vers le Numérique
Transition vers le Numérique Transition vers le Numérique
Transition vers le Numérique
 
7
77
7
 
8
88
8
 
21
2121
21
 
11
1111
11
 
14
1414
14
 
22
2222
22
 
6
66
6
 
19
1919
19
 
20
2020
20
 
10
1010
10
 
Améliorer la qualité des milieux via les projets d'assainissement agence eau ...
Améliorer la qualité des milieux via les projets d'assainissement agence eau ...Améliorer la qualité des milieux via les projets d'assainissement agence eau ...
Améliorer la qualité des milieux via les projets d'assainissement agence eau ...
 
Présentation agence Klaim
Présentation agence KlaimPrésentation agence Klaim
Présentation agence Klaim
 
Armature urbaine 01
Armature urbaine 01Armature urbaine 01
Armature urbaine 01
 

More from Xavier Lacot

Symfony2 components to the rescue of your PHP projects
Symfony2 components to the rescue of your PHP projectsSymfony2 components to the rescue of your PHP projects
Symfony2 components to the rescue of your PHP projectsXavier Lacot
 
Keynote d'ouverture - Forum PHP 2012
Keynote d'ouverture - Forum PHP 2012Keynote d'ouverture - Forum PHP 2012
Keynote d'ouverture - Forum PHP 2012Xavier Lacot
 
Développement Cross-Platform avec Titanium Mobile
Développement Cross-Platform avec Titanium MobileDéveloppement Cross-Platform avec Titanium Mobile
Développement Cross-Platform avec Titanium MobileXavier Lacot
 
Keynote de clôture - PHP Tour 2011
Keynote de clôture - PHP Tour 2011Keynote de clôture - PHP Tour 2011
Keynote de clôture - PHP Tour 2011Xavier Lacot
 
Abstracting databases access in Titanium Mobile
Abstracting databases access in Titanium MobileAbstracting databases access in Titanium Mobile
Abstracting databases access in Titanium MobileXavier Lacot
 
Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...
Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...
Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...Xavier Lacot
 
Symfony Day 2009 - Symfony vs Integrating products
Symfony Day 2009 - Symfony vs Integrating productsSymfony Day 2009 - Symfony vs Integrating products
Symfony Day 2009 - Symfony vs Integrating productsXavier Lacot
 

More from Xavier Lacot (7)

Symfony2 components to the rescue of your PHP projects
Symfony2 components to the rescue of your PHP projectsSymfony2 components to the rescue of your PHP projects
Symfony2 components to the rescue of your PHP projects
 
Keynote d'ouverture - Forum PHP 2012
Keynote d'ouverture - Forum PHP 2012Keynote d'ouverture - Forum PHP 2012
Keynote d'ouverture - Forum PHP 2012
 
Développement Cross-Platform avec Titanium Mobile
Développement Cross-Platform avec Titanium MobileDéveloppement Cross-Platform avec Titanium Mobile
Développement Cross-Platform avec Titanium Mobile
 
Keynote de clôture - PHP Tour 2011
Keynote de clôture - PHP Tour 2011Keynote de clôture - PHP Tour 2011
Keynote de clôture - PHP Tour 2011
 
Abstracting databases access in Titanium Mobile
Abstracting databases access in Titanium MobileAbstracting databases access in Titanium Mobile
Abstracting databases access in Titanium Mobile
 
Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...
Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...
Forum PHP 2010 - Les frameworks, essentiels dans-l-ecosysteme-php-xavier-laco...
 
Symfony Day 2009 - Symfony vs Integrating products
Symfony Day 2009 - Symfony vs Integrating productsSymfony Day 2009 - Symfony vs Integrating products
Symfony Day 2009 - Symfony vs Integrating products
 

RESTful avec symfony 1 et Symfony2

  • 1. RESTful avec symfony et Symfony2 Damien Alexandre, Xavier Lacot – 03 mars 2011 Symfony Day – 4. Juni 2009 Clever Age | Xavier Lacot
  • 2. Clever Age  Création en 2001 à Paris par des managers expérimentés  Plusieurs agences en France :  Paris  Bordeaux  Lyon  Nantes  Valeurs fondatrices : Indépendance, Veille technologique, Conviction  Quelques chiffres :  CA 2009 : 6 M€  Effectif au 01/03/2011 : 90 personnes  Notre Mission : « Concevoir des systèmes informatiques flexibles en limitant la dépendance vis-à-vis des prestataires et des éditeurs » RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 2
  • 3. Qui sommes nous ?  Damien :  Spécialiste Web full stack  Plusieurs contributions, notamment à symfony  Expert technique PHP chez Clever Age  Éleveur de poneys  http://twitter.com/damienalexandre  Xavier :  Expert Frameworks, développeur symfony depuis fin 2005  Plusieurs contributions (plugins, doc, patches, etc.)  Leader PHP chez Clever Age + en charge du pôle d'expertise  Vice-président de l'AFUP  http://twitter.com/xavierlacot RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 3
  • 4. Sommaire  2 ou 3 rappels sur REST  symfony, un framework RESTFul  Et dans Symfony2 ?  howto  benchmarks  Autour de Symfony – Plugins et Bundles RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 4
  • 5. 2 ou 3 rappels sur REST RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 5
  • 6. 2 ou 3 rappels sur REST  Développement d'APIs : de nombreux cas d'utilisation :  favoriser l'interopérabilité  contextes répartis  plusieurs applications différentes  plusieurs langages  exposer un référentiel de données  exposer des données publiques (OpenData, etc.) RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 6
  • 7. 2 ou 3 rappels sur REST  Un service Web  Un fournisseur (le « service »)  Un agent (le « client ») requête client service réponse RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 7
  • 8. REST et SOAP sont sur un bateau  REST  Representational state transfer  exposition de ressources  "colle" à HTTP  simple et compréhensible  WS-*  SOAP + WSDL  approche plus standardisée  plus verbeux  moins adapté à la description de ressources RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 8
  • 9. De l'utilisation d'HTTP avec REST  REST != HTTP  mais REST <3 HTTP :  les codes de réponse  les URL  les content-type  les méthodes Roy Fielding RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 9 http://www.flickr.com/photos/bblfish/637617442/
  • 10. De l'utilisation d'HTTP avec REST  Les verbes :  GET : Obtenir une ressource  POST + http content : Créer une ressource  PUT + http content : Modifier une ressource  DELETE : Supprimer une ressource  HEAD : Obtenir les headers d'une ressource  OPTIONS : Obtenir les méthodes acceptés RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 10
  • 11. De l'utilisation d'HTTP avec REST  Les codes de succès :  200 : OK  201 : Créé  Les codes de redirection :  301 : Ressource déplacée  304 : Ressource non modifiée RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 11
  • 12. De l'utilisation d'HTTP avec REST  Les codes d'erreur client :  400 : Bad Request  404 : Ressource introuvable  405 : Méthode non autorisé  406 : Non acceptable  418 : I'm a teapot  Les codes d'erreur serveur :  500 : Erreur générale RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 12
  • 13. De l'utilisation d'HTTP avec REST  L'URL  Uniform Resource Locator  http://domaine.tld/ressource/id : Désigne une ressource  http://domaine.tld/ressource : Désigne toutes les ressources RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 13
  • 14. symfony 1, un framework RESTful RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 14
  • 15. Introducing Pony ! RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 15
  • 16. Ressource Pony Pony Pony: id integer columns: name: name string(255) type: string(255) picture_url string(255) picture_url: type: string(255) description text description: slug string(255) type: text actAs: Sluggable: unique: true fields: [name] canUpdate: false RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 16
  • 17. Ressource Pony  New forest => new-forest  Magic Pony => magic-pony  GET http://domain.tld/pony/new-forest.xml ou  GET http://domain.tld/pony/8.xml  Le routing décide. RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 17
  • 18. Le routing dans symfony 1  gérer le plan d'adressage d'une application symfony  jolies urls  obfuscation des technos employées  validation des paramètres passés  découpler les urls exposées de l'organisation interne du projet  routing.yml  plusieurs classes de routing  sfRequestRoute / sfPatternRoute / sfRouteCollection  sfDoctrineRoute / sfDoctrineRouteCollection  etc... RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 18
  • 19. sfDoctrineRoute  routing.yml pony_show: url: /pony/:slug.:sf_format param: { module: pony, action: show, sf_format: xml } class: sfDoctrineRoute options: { model: Pony, column: slug, type: object } requirements: { sf_method: GET, sf_format: (xml|json|yml) }  actions.class.php public function executeShow(sfWebRequest $request) { $pony = $this->getRoute()->getObject(); // etc. } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 19
  • 20. Routing et format  sf_format est html par défaut pony_show: url: /pony/:slug.:sf_format param: { module: pony, action: show, sf_format: xml } class: sfDoctrineRoute options: { model: Pony, column: slug, type: object } requirements: { sf_method: GET, sf_format: (xml|json|yml) }  actions.class.php public function executeShow(sfWebRequest $request) { $format = $request->getRequestFormat(); // etc. } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 20
  • 21. Les formats GET http://domain/pony/new-forest.xml <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <response> <name><![CDATA[New Forest]]></name> <picture_url> <![CDATA[http://www.feracheval.com/nvxsite/newforest.jpg]]> </picture_url> <description> <![CDATA[Le poney New-Forest...]]> </description> <slug><![CDATA[new-forest]]></slug> </response> RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 21
  • 22. Les formats GET http://domain/pony/new-forest.json { "name": "New Forest", "picture_url": "http://www.feracheval.com/nvxsite/newforest.jpg", "description": "Le poney New-Forest...", "slug": "new-forest" } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 22
  • 23. Format et template  Le template utilisé change suivant sf_format :  html => showSuccess.php  xml => showSuccess.xml.php  json => showSuccess.json.php  Content-Type automatique pour les formats suivants :  txt, js, css, json, xml, rdf, atom.  Exemple :  GET http://domain/pony/connemara.json  Content-Type: application/json RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 23
  • 24. Format et template  showSuccess.json.php { "name": <?php echo json_encode($pony->getName()) ?>, "picture_url": <?php echo json_encode($pony->getPictureUrl(ESC_RAW)) ?>, "description": <?php echo json_encode($pony->getDescription(ESC_RAW)) ?>, "slug": <?php echo json_encode($pony->getSlug())."n" ?> }  Bon courage pour les listes d'objets... RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 24
  • 25. RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 25
  • 26. Format sans template  Sans template, on spécifie le Content-Type à la main : $format = $request->getRequestFormat(); $this->getResponse()->setContentType( $request->getMimeType($format) ); return sfView::NONE;  La solution consiste à employer un Serializer...  Pas de serializer dans symfony 1  Pas de serializer XML dans Zend  Doctrine peut le faire pour vous : $this->getResponse()->setContent( $query->execute()->exportTo($format) ); RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 26
  • 27. Format sans template  Le cas particulier de json... $this->getResponse()->setContent( json_encode($pony->toArray()) );  facile  plus rapide (natif)  écriture compacte (moins verbeux que XML)  Cool pour la bande passante  Javascript compliant RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 27
  • 28. Validation des paramètres  Exposer une interface = nécessité de valider les entrées  en GET : valider les restrictions  http://api.domain.tld/pony.json?country=france  que doit faire l'API sur un appel à l'url suivante ? http://api.domain.tld/pony.json?speaks=german  en écriture (POST / PUT) :  valider la présence du payload (HTTP content)  format attendu  contenu du payload RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 28
  • 29. Validation des paramètres  symfony 1 offre une série complète de validateurs  Email  Url  Date  etc. public function executeShow(sfWebRequest $request) { $name = $request->getParameter('name'); $validator = new sfValidatorString(array('required' => true)); // throws an exception if not valid $cleaned = $validator->clean($name) // etc. } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 29
  • 30. REST en haute Sécurité  Plusieurs approches de la sécurité :  qui a accès (authentification) ?  à quel rythme / fréquence (throttling) ?  Authentification :  approche naïve par preExecute  approche OAuth  Throttling  aidez vous d'Apache – mod_cband http://codee.pl/cband.html RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 30
  • 31. Authentification d'API par api_key  ajout d'un paramètre à chaque requête  dans l'url  ou en header http  approche dite "naïve" :  pas très sécurisée (man in the middle = le secret est connu)  mode d'authentification persistant dans le temps (clé compromise → changement de clé) RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 31
  • 32. Authentification d'API par api_key class ponyApiActions extends sfActions { public function preExecute() { $request = $this->getRequest(); $key = $request->getParameter('ApiKey'); if (!Doctrine::getTable('ApiKey')->findOneByApiKey($key)) { $this->getResponse()->setStatusCode(401); throw new sfException('Invalid authentication credentials.'); } } // etc } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 32
  • 33. Authentification d'API par OAuth  OAuth :  plus robuste et rassurant  plus long à mettre en place  protocole adapté à la gestion de l'authentification sur APIs RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 33
  • 34. Authentification d'API par OAuth redirection vers page de login Application cliente réponse (code) Passage du code API getToken (code) REST réponse (token) Application serveur appel de l'API (token) réponse (donnée demandée) RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 34
  • 35. Gestion du cache  Du cache sur une API ?  Oui, même de courte durée  cache.yml default: enabled: true with_layout: true lifetime: 120 # 2 minutes cache  settings.yml prod: .settings: cache: true RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 35
  • 36. Gestion du cache  Bootstrap = config, plugins, config BDD, routing, autoloading, session... RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 36
  • 37. Gestion du cache  Invalidation du cache if ($cache = $this->getContext()->getViewCacheManager()) { $cache->remove('pony/index'); $cache->remove('pony/show?slug='.$slug); } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 37
  • 38. Et avec Symfony2 ? RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 38
  • 39. Pony2, des poneys en Doctrine2  entité Doctrine2 = modèle Doctrine 1  définition du schéma en PHP, yml ou XML  aussi possible à l'aide des annotations  pas d'héritage de Doctrine_Record :  src/CleverAge/SymfponyBundle/Entity/Pony.php class Pony { protected $id; protected $name; protected $picture_url; protected $description; protected $slug; } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 39
  • 40. Pony2, des poneys en Doctrine2  les annotations permettent d'indiquer à Doctrine comment persister les données /** * @orm:Table(name="pony") * @orm:Entity */ class Pony { /** * @orm:Column(name="id", type="integer") * @orm:Id * @orm:GeneratedValue(strategy="IDENTITY") */ protected $id; /** * @orm:Column(name="name", type="string", length=110) */ protected $name; // etc. RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 40
  • 41. Pony2 - Générer getter et setters $ php app/console doctrine:generate:entities CleverAgeSymfponyBundle Generating entities for "CleverAgeSymfponyBundle" > generating CleverAgeSymfponyBundleEntityPony /** * Get id * @return integer $id */ public function getId() { return $this->id; } /** * Set name * @param string $name */ public function setName($name) { $this->name = $name; RESTful avec symfony 1 et Symfony2 – 3 mars 2011 } Clever Age | Damien Alexandre, Xavier Lacot 41
  • 42. Le routing avec Symfony2  app/config/routing.yml : symfpony: resource: "@CleverAgeSymfponyBundle/Resources/config/routing.yml"  src/CleverAge/SymfponyBundle/Resources/config/routing.yml : pony_list: pattern: /pony.{_format} defaults: - _controller: CleverAgeSymfponyBundle:Default:index - _format: xml requirements: { _format: (xml|json), _method: GET } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 42
  • 43. Le routing avec Symfony2  Dumpable en RewriteRule Apache : $ php app/console router:dump-apache RewriteCond %{REQUEST_METHOD} ^(GET) [NC] RewriteCond %{PATH_INFO} ^/pony(?:.((xml|json)))?$ RewriteRule .* app.php [QSA,L,E=_ROUTING__route:pony_list,E=_ROUTING__format: %1,E=_ROUTING__controller:CleverAgeSymfponyBundleControllerDefaul tController::indexAction,E=_ROUTING__format:xml] RewriteCond %{REQUEST_METHOD} ^(GET) [NC] RewriteCond %{PATH_INFO} ^/pony/([a-z0-9-]+)(?:.((xml|json)))?$ RewriteRule .* app.php [QSA,L,E=_ROUTING__route:pony_show,E=_ROUTING_slug: %1,E=_ROUTING__format: %2,E=_ROUTING__controller:CleverAgeSymfponyBundleControllerDefaul tController::showAction,E=_ROUTING__format:xml] etc. RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 43
  • 44. Le routing avec Symfony2  Pas de sfDoctrineRoute :'( pony_show: pattern: /pony/{slug}.{_format} Defaults: - _controller: CleverAgeSymfponyBundle:Default:show - _format: xml requirements: { _format: (xml|json), _method: GET, slug: "[a-z0-9-]+" }  Mais FrameworkExtraBundle est là...  http://bundles.symfony-reloaded.org/frameworkextrabundle/  ParamConverter FTW : /** * the Pony is automaticaly fetched by the ParamConverter. * @param Pony $pony * @param string $_format */ public function showAction(Pony $pony, $_format) { RESTful avec symfony do Symfony2 – 3 mars 2011 // 1 et what you want with $pony Clever Age | Damien Alexandre, Xavier Lacot 44 }
  • 45. Serializer component Finally ! RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 45
  • 46. Les “normalizers”  Transformation ressource → array  CustomNormalizer  normalize() et denormalize() sont implémentées sur l'objet  GetSetMethodNormalizer  inspecte les getters et les setters de l'objet RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 46
  • 47. Les “encodeurs”  Transformation array → format  JsonEncoder  simple implémentation de json_encode  XmlEncoder  Utilisation de DOMDocument  [insérer un format ici]Encoder  Forkez moi ! RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 47
  • 48. Implémentation de CustomNormalizer use SymfonyComponentSerializerNormalizerNormalizableInterface; use SymfonyComponentSerializerNormalizerNormalizerInterface; class Pony implements NormalizableInterface { function normalize(NormalizerInterface $normalizer, $format, $properties = null) { return array( 'name' => $this->getName(), 'picture_url' => $this->getPictureUrl(), 'description' => $this->getDescription(), 'slug' => $this->getSlug(), ); } // etc. } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 48
  • 49. Utilisation du Serializer $serializer = new SerializerSerializer(); $serializer->addNormalizer( new SerializerNormalizerCustomNormalizer() ); $serializer->setEncoder( 'xml', new SerializerEncoderXmlEncoder() ); $serializer->encode($pony, 'xml'); RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 49
  • 50. Utilisation du Serializer <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <response> <name><![CDATA[New Forest]]></name> <picture_url> <![CDATA[http://www.feracheval.com/nvxsite/newforest.jpg]]> </picture_url> <description> <![CDATA[Le poney New-Forest...]]> </description> <slug><![CDATA[new-forest]]></slug> </response> $xmlEncoder = new SerializerEncoderXmlEncoder(); $xmlEncoder->setRootNodeName('Pony'); RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 50
  • 51. Validation des entrées  Comme avec symfony 1 :  Nécessaire de valider les entrées  Symfony2 fournit toute une série de validateurs  classes étendant SymfonyComponentValidatorConstraint  aussi complet que les validators symfony1  Doctrine2 ne fournit aucun validateur (par choix) RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 51
  • 52. Déclaration des validateurs  Format de déclaration des validateurs au choix  YML  XML  PHP  annotations class Pony { /** * @validation:NotBlank() * @validation:MinLength(3) */ private $name; ... } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 52
  • 53. Exécution de la validation  activer le service de validation # app/config/config.yml framework: validation: enabled: true  valider dans le contrôleur $validator = $container->get('validator'); if (0 === count($validator->validate($pony))) { // persist the Pony } RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 53
  • 54. Le cache dans Symfony2  Présentation de Fabien demain à 11h30  Sujet majeur !  Conférence pas trop tôt  Conférencier pas trop mauvais :-)  Quelques détails quand même... RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 54
  • 55. Cache HTTP  Le cache est vérifié avant d'instancier le framework. gateway cache, ou reverse proxy RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 55
  • 56. Cache HTTP  Le cache est vérifié avant d'instancier le framework. gateway cache, ou reverse proxy ou ou Symfony2 HttpCache RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 56
  • 57. Expiration du cache HTTP $response = new Response(); $response->setPublic(); $response->setSharedMaxAge(600);  Comme pour symfony1 mais sans invalidation.  Impossible d'invalider programmatiquement le cache  Possible de définir une durée de vie RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 57
  • 58. Validation du cache HTTP $response = new Response(); $response->setETag(md5(serialize($pony))); if ($response->isNotModified($request)) { return $response; // 304 } else { $response->setContent( $this->getSerializer($_format) ->encode($pony, $_format) ); return $response; }  Ressource expirée ? Le meilleur des deux mondes.  Etag modifié ? RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 58
  • 59. Les très nécessaires benchmarks RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 59
  • 60. Méthodologie  même service Pony au format json :  symfony 1 mode dev  symfony 1 mode prod sans cache  symfony 1 mode prod avec cache  Symfony2 mode dev  Symfony2 mode prod sans cache  Symfony2 mode prod sans cache (format XML)  Symfony2 mode prod avec cache HTTP (PHP)  mêmes fonctionnalités  mesures faites à l'aide de siege - http://www.joedog.org/index/siege-home RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 60
  • 61. Résultats bruts  Lecture :  rel = rapport au moins performant,  avg = requêtes par seconde, en moyenne sur les 3 runs framework | rel | avg | 1 | 2 | 3 ------------------------ | -------- | -------- | -------- | -------- | -------- Symfony2Cache | 9.6251 | 373.87 | 369.49 | 373.75 | 378.36 symfony1Cache | 2.2480 | 87.32 | 86.25 | 87.78 | 87.94 Symfony2ProdJson | 1.7970 | 69.80 | 69.64 | 70.71 | 69.04 Symfony2ProdXml | 1.6106 | 62.56 | 60.92 | 62.73 | 64.03 symfony1Prod | 1.2769 | 49.60 | 48.41 | 50.60 | 49.80 Symfony2Dev | 1.1467 | 44.54 | 43.43 | 44.09 | 46.09 symfony1Dev | 1.0000 | 38.84 | 37.85 | 40.90 | 37.78 RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 61
  • 62. Commentaires  Symfony2 + cache HTTP est  4,5 fois plus performant que symfony 1 avec cache  7 fois plus performant que Symfony2 en mode dev (avec profiler)  10 fois plus que symfony 1 en mode dev  Symfony2 sans cache : 40% de gains de perfs face à symfony 1  le profiler Symfony2 est génial mais couteux  Préférez json à XML pour vos APIs  Chiffres à prendre avec précaution  Framework pas terminé vs. Framework vieux de 5 ans  Attention à la configuration RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 62
  • 63. Traces Xdebug et XHProf Nos premiers tests : Symfony2 était plus lent que symfony 1 RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 63
  • 64. Traces Xdebug et XHProf  Xdebug  Kudos à Derick Rethans  Super outil depuis 2002  Traces exploitables par KcacheGrind ou WinCacheGrind  http://www.xdebug.org/  XHProf  Facebook, Mars 2009  Très pratique aussi  Interface Web de consultation des traces  Graphes d'exécution avec graphviz  https://github.com/facebook/xhprof RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 64
  • 65. Traces Xdebug et XHProf Vraiment ? Regardons la trace XHProf RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 65
  • 66. Traces Xdebug et XHProf  Annotations Doctrine parsées à chaque requête ! # app/config/config.yml doctrine: orm: metadata_cache_driver: apc RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 66
  • 67. Traces Xdebug et XHProf  Après : RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 67
  • 68. Encore plus parlant avec XDebug RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 68
  • 69. Encore plus parlant avec XDebug Interprétation de la Query Parsing des annotations Doctrine # app/config/config.yml doctrine: orm: metadata_cache_driver: apc query_cache_driver: apc RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 69
  • 70. Memory usage  memory_get_peak_usage()  symfony 1 mode dev : 4 581 856 b  symfony 1 mode prod sans cache : 4 282 944 b  symfony 1 mode prod avec cache : 3 335 688 b  Symfony2 mode dev : 3 567 632 b  Symfony2 mode prod sans cache d'annotations : 3 027 240 b  Symfony2 mode prod sans cache HTTP : 2 800 600 b  Symfony2 mode prod avec cache HTTP : 1 012 496 b RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 70
  • 71. Autour de Symfony RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 71
  • 72. Des plugins pour symfony 1  sfDoctrineRestGeneratorPlugin :  admin generator like (generator.yml)  Serialization  facile à étendre  Validation  complexe mais puissant  sfRestWebServicePlugin :  Super-module  Template  facile à mettre en place  route api/*  support de PUT hacké (POST et sf_method=PUT) RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 72
  • 73. Des bundles pour Symfony2  EverzetRestfulControllersBundle  création automatique des routes à partir de noms d'actions  pas de lien avec le modèle (actions à développer)  https://github.com/everzet/EverzetRestfulControllersBundle # app/config/routing.yml ponys: type: restful resource: CleverAgeSymfponyBundleControllerPonysController class PonysController extends Controller [GET] { public function getPonyAction($slug) /pony/connemara { // etc. } [POST] public function postPonysAction() /ponys { RESTful avec symfony 1 et Symfony2 – 3 mars 2011 // etc. Clever Age | Damien Alexandre, Xavier Lacot } 73
  • 74. Pour conclure... REST c'est bien. Et avec symfony c'est facile. Avec Symfony2, c'est encore mieux. http://symfpony-project.org/ https://github.com/xavierlacot/symfpony-project.org https://github.com/xavierlacot/symfpony-benchmark RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 74
  • 75. Time to REST Des questions ? RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 75
  • 76. Récompense des projets Web innovants  HTML5  OpenData  Mobilité  etc.  Appel à candidatures : candidat_awards@clever-age.com  Détails sur http://awards.clever-age.com/ RESTful avec symfony 1 et Symfony2 – 3 mars 2011 Clever Age | Damien Alexandre, Xavier Lacot 76