Revenir
en haut

Introduction à Symfony 2

04/01/2011 13

Résumé de la situation

Comme vous le savez, je suis un gros consommateur de Zend Framework, il y a une explication à celà. Lorsque je me suis décidé à adopter un framework PHP, mes choix s’étaient réduits à deux prétendants, Zend Framework et symfony. Alors pourquoi j’ai choisi ZF ? La réponse est très simple, pour sa flexibilité.

Bien que symfony premier du nom soit un très bon framework, il souffre malheureusement d’une rigidité trop importante. Pour résumer grossièrement, je dirais qu’avec symfony, soit on fait tout avec le framework, soit on ne fait rien (ce qui n’est pas nécessairement une mauvaise chose). J’avais besoin d’un framework qui me laisse la liberté d’implémenter tout ce que je peux imaginer. D’autant plus qu’à ce moment là, les frameworks PHP étaient loin de proposer tout l’arsenal nécessaire, et il était évident que j’allais avoir une surcouche de code importante.

Ensuite, je crois que la force de Zend a été de mener une campagne de communication plutôt efficace, et j’avoue avoir été séduit.

Mais ce n’est pas pour autant que je m’enferme dans un outil sans me soucier de ce qui se passe autour. Je surveille de très près l’évolution de PHP et son éco-système. J’essaie autant que possible d’effectuer une veille régulière et j’ai toujours quelques expérimentations en cours :)

Au delà de ça, je me nourris de tous les concepts qui gravitent autour du développement web, qu’ils viennent de frameworks JAVA, .NET, Ruby ou autre et m’en inspire beaucoup pour nos développements autour de Zend Framework.

Mais voilà qu’est annoncé Symfony 2, depuis maintenant une dizaine de mois (ça passe vite !), et quelle était ma surprise quand j’ai vu que tout ce que j’attendais (ou presque) d’un framework PHP abouti était sur le point d’être intégré dans Symfony 2.

Entre promesses et réalité, où en est-on aujourd’hui et pourquoi Symfony 2 est-il si prometteur ? Je vais tenter de répondre à cette question dans ce billet qui fera office d’introduction à ce nouveau framework.

On casse tout et on recommence

Je suis persuadé que la meilleure initiative a été de tout remettre en question.

Pour proposer un framework flexible, il faut que son cœur soit lui même flexible et conçu dans cette optique. Il doit être léger, simple et suffisamment intelligent pour permettre aux développeurs de laisser aller leur imagination et ne pas se retrouver bloqués par une trop grande rigidité du framework. Il n’y a rien de plus frustrant qu’avoir des idées mais ne pas pouvoir les mettre en œuvre correctement.

Avec Symfony 2, Fabien Potencier a pris le parti de tuer la magie (« kill the magic ») et d’emprunter et utiliser moins de concepts mais de manière plus efficace. Tout a été repensé et ré-écrit « from scratch ».

Une nouvelle orientation était aussi de vouloir transformer un objet requête en un objet réponse (qui j’entends dire « Ha ben oui, c’est logique en fait ! » ?).

Je ne pourrai malheureusement pas faire l’analogie avec symfony 1 car je ne le connais pas assez, mais ce n’est pas très grave étant donné que Symfony 2 n’a plus rien à voir, sauf dans sa philosophie (Fabien).

Pourquoi Symfony 2 est-t-il flexible ?

Je ne vais pas détailler tous les composants du framework mais plutôt me concentrer sur les trois grandes composantes de l’architecture de Symfony 2 qui en font un noyau simple et flexible.

Injection de dépendances

De tous les concepts utilisés en programmation, celui qui me manquait le plus dans les frameworks PHP était l’inversion de contrôle (IOC) ou dans sa forme spécifique l’injection de dépendances. Pour pallier à ce manque j’ai écrit un conteneur très léger de dépendances pour Zend Framework (incomplet) en espérant voir arriver un jour un conteneur abouti au sein des frameworks.

Mon souhait se réalise puisque Symfony 2 repose entièrement sur un conteneur de dépendances léger, puissant et simple à utiliser. Ce composant indépendant, de Sensio, existait déjà mais les frameworks n’étaient pas étudiés pour ce type de pratique. L’utilisation du conteneur n’avait alors qu’un intérêt limité. Dans Zend Framework 1, le coeur du système ne permet par exemple pas d’injecter certaines dépendances qui sont fortement liées. Ou encore l’utilisation abusive du pattern Singleton et l’accès statique à certains objets sont une contrainte limitant fortement la flexibilité du framework.

Dans Symfony 2 le conteneur de dépendances est utilisé dans le coeur du framework et tout est pensé pour un découplage total des composants. Ceci apporte en plus d’autres avantages, tout est par exemple plus explicite, il est très facile de connaitre les dépendances d’un composant en jetant un oeil à la configuration du conteneur.

Pour parler du conteneur lui même, baptisé tout simplement « Dependency Injection », il est très simple à utiliser et permet tout type d’injection (par mutateur, par constructeur, etc.). Je ne rentre pas dans le détail dans ce billet et vous laisse consulter le site officiel du composant pour vous familiariser avec son utilisation.

Je citerai juste au passage la possibilité de « dumper » le conteneur et la liberté d’implémenter son propre driver de configuration (loader). Le premier point est très apprécié et permet de visualiser facilement le graph des dépendances des objets, ce qui facilite vraiment le débuggage :)

Le système de Bundle

Le succès de symfony 1 est en partie dû à sa communauté et notamment à la possibilité de partager des plugins. Il était logique que Symfony 2 conserve cet aspect. Mais la réflexion a été poussée et les petits gars de chez Sensio nous servent un système qui fait preuve d’une très grande flexibilité.

Derrière le nom « Bundle » qui peut être assez déroutant se cache un système modulaire très bien maîtrisé. Une fois le terme accepté (je suis peut-être fou mais j’ai eu du mal), c’est un régal pour le développement.

La documentation officielle assimile un bundle à un plugin, non pas un module, et à raison. En effet, un bundle dans Symfony 2 est totalement indépendant, mais est surtout très facile à intégrer au sein de l’application. Il suffit de coller le dossier du bundle dans l’application, d’ajouter sa configuration à l’environnement et ça roule tout seul.

Mais toute la puissance de la solution vient du fait que dans Symfony 2 tout est bundle, même les fonctionnalités du cœur sont réunies dans plusieurs bundles dont le chargement est explicite. Tout est donc modulable !

Ce point pourra d’ailleurs perturber certains d’entre vous car même la couche web est un bundle. Et c’est là que nous faisons la différence entre un module, un plugin et un bundle et que l’arrivée d’un nouveau terme prend du sens.

Pour ce qui est du partage, le coup est déjà prévu puisqu’une pré-version du site http://symfony2bundles.org/ est disponible et permet déjà de trouver quelques bundles intéressants. Le site renforce l’aspect collaboratif puisqu’il est en intéraction direct avec github et facilite la participation au développement d’un projet ou d’un bundle.

A noter que le site est réalisé à l’aide de Symfony 2 et Doctrine 2 et qu’une API est déjà disponible pour accéder aux données des bundles et de leurs développeurs. Une bonne vitrine donc :)

Le dispatcheur d’événements (Event Dispatcher)

Troisième atout dans l’architecture de Symfony 2, une implémentation nouvelle du pattern Observer pour le composant Event Dispatcher.

Un des problèmes les plus difficiles à résoudre en programmation lorsque plusieurs développeurs travaillent en parallèle sur différentes fonctionnalités d’un système est de permettre l’intéraction des différents composants sans avoir à les modifier pour qu’ils puissent fonctionner ensemble. Plus difficile encore, un plugin peut intervenir à différents moments de l’exécution d’une application.

Imaginez par exemple que vous souhaitez modifier l’objet requête avant que votre action de contrôleur soit exécutée. Ceci doit être réalisable sans avoir à mettre la main dans le code du contrôleur. D’autant plus que ce code aura peut-être été écrit par un autre développeur.

Le pattern Observer permet de résoudre ce problème, et Symfony 2 nous propose une implémentation simple et efficace. Une classe Event permet de créer des événements identifiés par une chaine de caractères unique et de lui assigner des attributs eux aussi identifiés par une chaine. Il sera donc inutile dans la plupart des cas de créer une classe spécifique pour vos événements.

Pour connecter un « listener » à l’événement c’est trivial, il suffit de créer une instance du dispatcher et d’utiliser une méthode connect qui prend en paramètre le nom de l’événement, une méthode de callback à exécuter au moment de la notification et un ordre de priorité.

Il existe trois manières de notifier l’événement, que je ne détaillerai pas ici, mais si nous reprenons l’exemple du contrôleur et de l’objet requête, lors de la construction de l’objet réponse à partir de l’objet requête, le cœur du framework utilise la méthode de notification filter() qui demande à tous les « listeners » connectés à l’événement de filtrer la valeur donnée (ici un objet Request) et de la retourner (filtrée). Très facile donc d’altérer l’état de l’objet avant de le récupérer dans notre action de contrôleur. Et ceci en toute transparence.

Symfony 2 nous permet donc de créer très facilement des « Hooks » (le terme est peut-être plus parlant pour certains d’entre vous) et le framework permet déjà d’intervenir à certains moments clés de l’exécution de la requête.

Vous me direz que ceci n’est pas nouveau, Zend Framework 1 permet par exemple de créer des plugins de contrôleur ou des aides d’action, mais ceci reste très spécifique. Un dispatcheur générique tel que dans Symfony 2 permet une ouverture plus grande puisqu’il pourra être utilisé par n’importe quel objet et à tout moment, selon vos besoins.

Autres éléments intéressants

Je me concentre sur les trois éléments clés de l’architecture du framework mais il en existe quelques autres intéressants et renforçant encore la flexibilité.

  • la structure du projet (dossiers) : la structure proposée offre une meilleure séparation des différents éléments de l’application, la configuration, la couche web (MVC) et les différents plugins (bundles) fonctionnels.
  • le chargement automatique des classes : un seul fichier php utilisant une classe UniversalClassLoader permet de configurer de manière explicite tous les espaces de noms ou les préfixes (PEAR) des différentes sources tiers.
  • une classe de bootstrap (AppKernel) unifiée : encore une fois la classe de bootstrap de l’application permet de charger explicitement les différents bundles et la configuration de l’environnement.

Le mot qui revient souvent est « explicite », et ce n’est pas pour rien. Tout est limpide, et il faut mettre les doigts dans le code pour configurer certaines parties du système (« convention over configuration »), ce qui permet d’avoir au passage une vue d’ensemble de ce qui est exécuté au moment de l’initialisation de l’application.

Et les performances ?

Encore une fois c’est une surprise. Symfony 2 est rapide, je dirai même très rapide.

« Kill the magic »

Je citais en début d’article le leitmotiv « kill the magic ». Dans les premières versions de Zend Framework et symfony, il y avait beaucoup de « magie ». C’est à dire que beaucoup d’éléments étaient accessibles de manière obscure pour économiser du code et limiter le nombre de caractères à saisir par ces paresseux de développeurs (ok je me moque un peu).

Le problème est que la « magie » a un coût assez élevé en performances. L’utilisation abusive des méthodes magiques __call, __get, __set, etc. demande une introspection du code et des fichiers importante, ce qui a impact très négatif non seulement sur les performances mais aussi sur la qualité et la compréhension du code.

Les développeurs de Symfony 2 ont pris le parti de tuer cette magie et de rendre les choses plus explicites et moins obscures pour les utilisateurs du framework. Ceci améliore non seulement les performances de manière significative mais adoucit aussi la courbe d’apprentissage du framework car tout y est beaucoup plus clair et compréhensible pour les développeurs. D’une pierre deux coups.

Utilisation interne du cache

Symfony 2 utilise beaucoup de cache (fichiers) en interne et notamment pour la configuration.

Lors de l’exécution de la première requête, certaines classes dépendantes de la configuration sont « compilées » (ou plutôt « empilées » :-) ) en PHP dans le cache. Lors des exécutions suivantes, le cache sera utilisé. Pour vous donner une idée, sur une application de type « Hello World », la première requête prendra 600ms et toutes les suivantes 25ms.

Certains autres composants utilisent abondamment le cache, comme Twig (moteur de templates) ou Doctrine (ORM), et le résultat est indéniable, l’impact sur les performances est énorme.

C’est donc un bon point pour Symfony 2 car le développeur n’aura pas à se soucier de ces problématiques et à intégrer sa propre gestion du cache pour cette partie de l’application.

Le conteneur de dépendances

Je reviens sur ce composant qui a également un impact positif sur les performances.

Grâce au conteneur, les éléments sont chargés à la demande, seulement quand nécessaire (lazy loading). Ceci réduit considérablement le nombre de classes chargées dès l’initialisation du système. C’est vrai pour le cœur du framework mais aussi pour vos propres développements.

Autres atouts

Je ne peux malheureusement pas trop m’étaler, cet article est déjà très long.

J’ajouterai simplement que certaines couches de Symfony 2 sont inspirées de très bons frameworks tels que Spring (JAVA) ou Django (Python) et que certains concepts que nous n’avions pas encore dans l’univers PHP font leur apparition.

Je pense notamment à Twig, le moteur de templates livré avec le framework, qui est très prometteur. J’ai d’ailleurs écrit une intégration du moteur pour Zend Framework 1, et nous (anonymation) allons migrer la couche présentation de certains de nos projets sur ce système.

Je pense aussi à des composants spécifiques, par exemple le composant « Form » pour la gestion des formulaires, qui intègre un data binder pour mapper automatiquement les valeurs d’un formulaire HTML sur un Objet PHP. Ou encore la couche de sécurité pour l’authentification et la gestion des droits des utilisateurs qui est nouvelle (en PHP) et puissante. Et j’en passe…

Tout est également « testable » et l’intégration des tests unitaires et fonctionnels a été simplifiée et renforcée. Rappelons que la pratique des tests unitaires en PHP est très récente, c’est donc une bonne chose que les frameworks professionnels encouragent les développeurs vers de telles pratiques en leur proposant des outils adaptés.

Pour finir, Symfony 2 intègre des outils facilitant le développement et qui ont fait le succès de symfony premier du nom. La debug bar, des exceptions travaillées et plus explicites, des logs, et bien sûr une commande CLI très utile.

Où est l’anguille ?

Jusqu’ici on pourrait croire que j’ai été soudoyé par Sensio pour vous expliquer combien Symfony 2 est merveilleux. Il n’en est rien !

Le framework se montre vraiment très prometteur, mais (et oui), j’ai bien sûr quelques réticences envers certains éléments.

Lorsque j’ai vu apparaître certains concepts dans Symfony 2, j’ai sauté de joie, mais malheureusement rien n’est jamais parfait ou comme on le voudrait.

Je pense par exemple au « data binder » dont l’implémentation est exclusive au composant « Form ». J’aurais aimé voir apparaître un composant dédié à cette tâche qui puisse être utilisable dans d’autres contextes qu’un formulaire HTML. J’ai essayé d’aborder le sujet sur le groupe de discussion mais sans succès. Je sais ce qu’il me reste à faire :)

Autre chose que je n’aime pas, l’intégration trop poussée de certaines librairies tiers, comme Doctrine 2 par exemple. C’était vrai également pour symfony 1. Lorsque des nouvelles versions de ces librairies sont disponibles il faut compter sur la réactivité du développeur du Bundle en question pour bénéficier de l’intégration des nouvelles fonctionnalités. Mais ce point est discutable car une telle intégration apporte aussi des avantages ; dans le cas de Doctrine, pas besoin de jongler par exemple entre les lignes de commandes de Symfony et de Doctrine, tout peut-être fait par la CLI de Symfony.

Je n’ai malheureusement pas encore assez de recul pour apporter d’autres critiques, mais ça ne saurait tarder. Sans compter que le framework est encore en phase de développement et que certaines choses peuvent encore changer d’ici la première version stable prévue pour Mars 2011.

Conclusion

Vous l’aurez compris, je suis séduit. Sensio nous propose un framework très mature et très bien fichu. Une grosse réflexion a été menée sur le fond et ça se ressent dès les premières utilisations.

Je peux déjà vous dire que chez anonymation nous avons adopté Symfony 2 et nous prévoyons déjà la migration de certains « gros » projets sur le framework.

Mais attention, ça ne veut pas dire que j’abandonne Zend Framework. Certains projets démarrent encore sur ZF et j’attends avec impatience la refonte du MVC prévue pour Zend Framework 2 dont je suis de près le développement.

Je m’excuse pour la longueur de cet article, mais je vous promets que je me suis retenu de rentrer dans le détail sur certains points :-) Cet article fait office d’introduction et d’autres articles plus techniques et spécifiques seront publiés au fur et à mesure de mes aventures.

Edit : Je me rend compte que je n’ai pas du tout parlé de PHP 5.3 mais évidemment Symfony 2 tire profit de toutes les nouveautés du langage. Il n’est d’ailleurs compatible qu’avec PHP 5.3.2 et supérieur. Les conventions sont respectées, les espaces de noms propres. Vous verrez même certaines utilisations des closures plutôt ingénieuses. Je tenais à préciser ce point important.

13 commentaires :

  1. Merci pour ce retour d’expérience !

  2. Mr_g33k :

    Très bon article, qui me met (encore plus) l’eau à la bouche mais, qui d’un autre coté me fait prendre conscience qu’il va falloir s’adapter à un tas de nouvelles choses ;)

  3. truffo :

    Très intéressant, la suppression est une bonne chose. C’était l’un des gros point noir. La fin du fullstack (enfin) rend le framework très intéressant.

    Par contre, la présence d’un moteur de template me fait hérisser le poil. J’ai pas regardé mais j’espère qu’il sera optionnel, et l’utilisation de PHP sera possible facilement.

    • Benjamin Dulau :

      Hello,

      L’utilisation de Twig n’est pas obligatoire, tu pourras utiliser le moteur de rendu PHP sans problème.

      Le rendu des « vues » est maintenant explicite et c’est à ce moment qu’est déterminé le moteur en fonction de l’extension du fichier que tu lui demandes de rendre.

      Exemple (tiré de la doc.) :

      public function indexAction($name)
      {
          return $this->render('HelloBundle:Hello:index.php', array('name' => $name));
      }

      Chacun pourra choisir ce qui lui convient le mieux.

      Je n’ai jamais voulu utiliser de moteur de templates tiers mais je reviens sur mes positions avec les mauvaises expériences et je suis convaincu que c’est un gain en productivité sur le long terme (pas la première fois ça c’est sur). A condition bien sûr que les performances tiennent la route, et c’est le cas avec Twig.

    • @truffo : Symfony2 sera toujours un framework full-stack mais il est architecturé de telle manière qu’on peut utiliser ses composants complètement indépendamment les uns des autres. Ce sera d’ailleurs l’objet de ma conférence au Symfony Live à Paris en mars prochain au cours de laquelle je présenterai comment tirer profit des meilleurs composants de SF2 sans le framework.

      Concernant l’utilisation d’un moteur de templating, j’y étais également réfractaire car mes expériences passées avec la PHPLib, Smarty ou PHPTal ne m’ont jamais convaincu et je me suis toujours retrouvé assez limité par leurs fonctionnalités.

      Aujourd’hui ce n’est plus le cas. Le moteur de templates de Symfony2 est un vrai moteur de templates. C’est une adaptation du moteur de templating Jinja de Django en PHP. Twig est un moteur de template moderne qui supporte de nombreuses fonctionnalités par défaut :

      • Héritage de templates
      • Slots
      • Auto échappement des variables
      • Boucles
      • Conditions
      • Opérations arithmétiques
      • Opérations logiques
      • Fonctions
      • Filtres
      • Macros
      • Inclusion de templates dans un autre
      • Mise en cache automatique du code PHP généré

      L’autre gros point fort de Twig c’est sa flexibilité et son extensibilité. Il est très facile d’étendre Twig en lui ajoutant de la grammaire supplémentaire (fonctions, macros, tags…). Les autres moteurs de templates du marché n’ont pas cette richesse de la syntaxe et cette possibilité d’étendre l’outil à sa guise.

      Symfony2 encouragera les développeurs à utiliser Twig plutôt que le moteur de templates PHP pour plein de raisons : sécurité renforcée, cache, richesse de la syntaxe, séparation de la couche vue… Chez Sensio, nos formations Symfony2 en cours de préparation seront toutes basées sur Twig. Nous prenons clairement partis pour l’utilisation d’un moteur de templating, ce qui n’était pas le cas à l’époque de symfony 1.x.

  4. Pareil que Mr_g33k.

    Je n’ai, pour l’instant, jamais utilisé de Framework php en production, mais il est vrai que cela fait de plus en plus envie.

    Je crois que je vais me mettre sérieusement à Symfony2 :)

  5. Mich972 :

    Merci Merci et merci. Continu comme ça. Tu aides beaucoup de personne. Nous attendons les autres articles avec impatience :)

  6. Emmanuel C. :

    Bonjour,

    Je découvre ce blog et je tiens à vous féliciter pour la qualité et l’objectivité de vos articles.

    Un flux de plus dans mon RSS Reader ;)

    Bonne continuation à vous.

  7. Merci beaucoup pour ce retour d’expérience. Même si effectivement l’article sonne comme un éloge de Symfony2, il n’en est pas moins crédible et me donne vraiement envie de tout casser et recommencer avec une version 2. Encore merci :D

  8. jcv :

    Bonjour, Cela fait longtemps que je voulais expérimenter un framework. Ne programmant qu’occasionnellement, je perds beaucoup à chaque reprise. Il me semble que cette flexibilité de Symfony2 peut me sortir de cette ornière. Mais en me penchant sur le sujet, une question de fond subsiste.

    Comment Symfony2 fait t-il pour intégrer tous les bundles dans son MVC général. Je m’explique : la notion de dunble prime sur celle du MVC puisque que chaque dunble est indépendant. Mais il faut bien une cohérence de la vue qui va être renvoyé au client.

    Je ne vois pas trop où se fait cette intégration. Si cela se fait par l’instance de la classe liée au dunble ou dans le routing ou bien dans autre chose qui serait caché. En plus je ne vois pas comment TWIG agit dans cette interaction.

    Merci d’avance s’il y a réponse. En espérant également que mon interrogation puisse servir à d’autres.

  9. TOETIP :

    Bonjour,

    Je crois qu’il y a une coquille « convention over configuration » devrait être « configuration over convention », non ? D’autant que juste avant il est dit qu’il faut mettre les mains dans le cambouis :)

    merci pour cette exploration de symfony 2 dont la sortie stable se fait toujours attendre :(

    Bon code :)

  10. Julien Breux :

    Oulala, j’arrive après la guerre. Alors un grand merci ;)

  11. honorat Niamkey :

    Tres bon article, a l\’heure actuelle je pense que le framework est tres abouti. Il permet au developpeur php de respecter ce qui a fait le succes des grands framework tels que spring ou strut. Merci encore.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

*

Tags HTML autorisés : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Tag code : [cc lang="langage"][/cc] (ex. [cc lang="php"][/cc])