Jérémy & Jérôme
Jérôme et Jérémy sont tous deux leads developpeurs Symfony à L'Express. Ils s'amusent avec symfony depuis la fameuse 0.63 et jouent maintenant avec Silex et SF2 dans la joie !
Vous ne rêvez pas, nous allons bien vous parlez de symfony1 dans cet article. Nous n'allons pas vous forcer à l'utiliser et encore moins vous inciter à commencer un nouveau projet avec. Nous allons vous présenter notre fork de symfony1 avec toutes les améliorations que nous y avons apportées (notamment le DIC) et que nous avons récemment taggé : symfony 1.5.
Vous avez peut-être, comme beaucoup d'autres développeurs, des applications en symfony1 à maintenir. Il est souvent complexe de faire une migration vers Symfony2 parce qu'il faut tout réécrire ou presque. Des fois même, votre applicatif est tellement couplé à certains composants de symfony1 (les formulaires, l'admin generator, etc.) qu'il est presque impossible de migrer vers Symfony 2.
La liste des nouveautés et corrections est assez longues, et nous en parlerons brièvement plus bas, mais voici les deux grandes nouveautés de ce fork : le conteneur d'injection de dépendance (DIC) et le support de Composer.
Fabien vous a convaincu par son article sur l'injection de dépendance ? Mais malheureusement vous êtes restés sur un projet symfony1 avec l'ancêtre du conteneur de services : la « factory ».
Tout n'est pas perdu. Nous avons intégré à symfony 1.5 une version basique
du composant sfServiceContainer
développé par Fabien Potencier.
Son utilisation est très simple (certains diront que c'est trop facile pour
être vrai), il suffit de définir un fichier config/services.yml
avec la liste des services à charger :
# config/services.yml
all:
services:
auth:
class: myAuthService
arguments:
# Exemple d'utilisation d'un paramètre défini dans app.yml
- %app_auth_host%
calls:
# Injection d'un service
- [ setEventDispatcher, [ @sf_event_dispatcher ] ]
# Surcharge par environnement
test:
services:
auth:
class: myAuthServiceStub
Le service peut alors être appelé depuis différentes sections de votre code :
Dans une action:
class myModuleActions extends sfActions
{
public function executeIndex()
{
$fs = $this->getService('sf_filesystem');
$fs->...
}
}
Dans une tâche en ligne de commande :
class myTask extends sfTask
{
public function run()
{
$fs = $this->getService('sf_filesystem');
$fs->...
}
}
Ou même depuis n'importe où en utilisant le singleton sfContext
(mais grâce à l'injection de dépendance vous n'aurez bientôt plus besoin de
faire ça).
sfContext::getInstance()->getService('sf_filesystem');
Le framework embarque plusieurs services par défaut :
sf_formatter
sf_event_dispatcher
sf_logger
sf_user
sf_filesystem
Alternative :
Pour intégrer le composant DependencyInjection
de Symfony2,
vous pouvez aussi utiliser le plugin
sfDependencyInjectionPlugin.
Nous ne l'avons pas testé mais il semble répondre au besoin.
L'installation de symfony 1.5 via Composer nécessite tout simplement d'exécuter successivement les commandes suivantes :
composer init
composer require lexpress/symfony1 v1.5.*
composer install
Ensuite il faut ajouter le script d'auto-chargement des classes de Composer
dans le fichier config/ProjectConfiguration.class.php
:
<?php
require_once __DIR__.'/../vendor/autoload.php';
Et le tour est joué. Plus besoin de charger explicitement
sfCoreAutoload
, Composer le fait pour vous.
Grâce à composer/installers, vous pouvez ajouter un plugin à votre projet. Le plugin est un paquet Composer comme un autre, à la seule différence que son type est « symfony1-plugin » et non « library » par défaut.
Configuration d'un plugin (dans votre fichier composer.json
) :
{
"name": "vendor/sf-avent-plugin",
"type": "symfony1-plugin",
"require": {
"composer/installers": "~1.0"
}
}
Exemples de plugins compatibles :
lexpress/sf-doctrine-guard-plugin
lexpress/sf-web-browser-plugin
propelorm/sf-propel-o-r-m-plugin
Vous pouvez ainsi utiliser Composer pour gérer l'installation du framework, des plugins et de tous les autres paquetages.
Ce n'est pas parce que vous faites du symfony1 que vous ne pouvez pas utiliser les fonctionnalités des dernières versions de PHP ; en l'occurrence les espaces de nommage (« namespaces »).
Configurez votre projet pour indiquer à Composer de charger les classes qui
se trouvent dans le dossier src
en suivant la norme
PSR-0 :
{
"autoload": {
"psr-0": {"Acme\\": "src/"}
}
}
En dehors du conteneur d'injection de dépendance et du support de Composer, des améliorations significatives ont été réalisées sur les performances.
Nous avons rendu symfony1 compatible avec PHP 5.5.
Cela semble anodin et ce n'est pas une amélioration en soit de symfony1, mais le fait de supporter la version 5.5 de PHP, augmente considérablement les performances. De plus, il donne la possibilité à votre projet d'être toujours sur des versions récentes de PHP.
Le routage a été la couche à subir le plus d'améliorations. Ainsi, les
routes qui sont définies dans le cache sont désérialisées à la demande.
Avec l'usage combiné de lookup_cache_dedicated_keys
et du
cache
, seules les routes utilisées dans la page seront
désérialisées et chargées en mémoire :
# apps/frontend/config/factories.yml
all:
routing:
class: sfPatternRouting
param:
cache:
class: sfAPCCache
generate_shortest_url: true
extra_parameters_as_query_string: true
lookup_cache_dedicated_keys: true
Sur une application ayant 700 routes différentes, l'utilisation mémoire à chaque requête a été réduit de 10 Mo (soit 10% à 20% de mémoire en moins).
C'est une correction mineure mais maintenant la commande
project:optimize
gère correctement l'environnement utilisé
ainsi que la génération du cache.
SwiftMailer
SwiftMailer
est instancié à chaque requête faite sur votre
application. Il est maintenant très simple de ne plus l'instancier.
Dans votre fichier factories.yml
:
mailer:
class: sfNoMailer
Pour une liste complète, il vaut mieux se référer au WHAT'S NEW, mais voilà un rapide résumé.
La majorité des améliorations faites sur les formulaires concerne les formulaires imbriqués (les fameux « embedded forms »). La liste est assez longue puisque plus d'une dizaine de tickets du Trac de symfony ont été corrigés.
Mais par exemple, on peut maintenant appeler la méthode
sfFormObject::updateObject()
qui va mettre à jour tous les
formulaires imbriqués sans pour autant les sauvegarder.
De nouveaux widgets et validateurs ont été aussi ajoutés, dont :
sfWidgetFormInputRead
pour afficher un champ en lecture
seule,
sfValidatorIp
extrait de Symfony 2.
C'est le petit truc
qu'on aime bien dans cette version de symfony1 : la possibilité d'afficher
une barre de progression dans vos tâches (avec une estimation de temps).
C'est hyper pratique pour avoir une idée de l'avancée de
votre tâche, sans devoir faire un ->logInfo()
à chaque tour
de boucle.
L'information n'est pas présente sur la capture d'écran, mais la tâche affiche son utilisation mémoire. Très pratique pour savoir s'il y a des fuites mémoires.
Il est bon de noter que cette fonctionnalité a depuis été ajoutée à Symfony 2. Youhou on était en avance :) Cependant, des pull requests sont en attente pour afficher le temps restant et la consommation mémoire.
On le rappelle encore : il préférable de ne pas utiliser cette version pour de nouveaux projets mais d'opter pour Symfony2.
Si par contre, vous devez maintenir un projet existant en symfony1, vous pouvez choisir cette version tout de suite (!) sans faire d'énormes modifications. Lisez attentivement le fichier d'UPGRADE même s'il concerne majoritairement les formulaires.
Et si vous voulez contribuer à l'amélioration de ce fork, vous pouvez aussi soumettre des pull requests :)
Merci aux contributeurs et surtout au gros travail de Jérôme Macias sur ce projet !