Bonnes pratiques de développement PHP/symfony
01/01/2011 — Mots-clés : PHP, symfony — Classé dans Geek
Voici une liste de bonnes pratiques que j’ai pu appliquer depuis que je travaille dans le développement Web avec symfony. J’en rajouterai d’autres de temps en temps.
Présentation du code
- Ne jamais utiliser de tabulations, toujours indenter avec des espaces (2 ou 4 c’est une autre question… personnellement, depuis symfony 1.0, j’en mets 2) ;
- Les accolades ouvrantes et fermantes doivent être respectivement alignées ;
- Toujours revenir à la ligne avant une accolade ;
- Les méthodes
protectedviennent après les méthodespublic(jamais deprivate) ; - Pour faciliter la navigation dans les sources, toujours utiliser le même nom de fichier que la classe qu’il contient : classe
sfSessionStoragedanssfSessionStorage.class.php(une seule classe par fichier) ; - Utiliser des noms de variables/méthodes/classes explicites et en anglais.
Utilisation de librairies externes
- Toutes les librairies externes doivent être insérées dans le dossier
/lib/vendor/xxx; - Ne jamais modifier les fichiers externes au projet (source de symfony, librairies, plugins…) sous peine de casser la compatibilité avec les mises à jour (il est cependant possible de surcharger proprement la plupart des classes) ;
- Essayer de les maintenir à jour (sauf en cas d’incompatibilité).
Organisation
- Utiliser le même encodage partout (même pour la base de données), le plus souvent UTF-8 ;
- Éviter les URLs, chemins ou autres variables dépendantes de l’environnement codés en dur dans l’application, préférer un fichier de configuration spécifique,
/config/app.ymlpar exemple ; - Tout ce qui doit être testable unitairement (librairies, modèle, formulaires…) doit être rangé dans le dossier
/lib; - Éviter d’utiliser les dossiers
apps/xxx/libetapps/xxx/modules/yyy/lib, sauf pour des cas vraiment spécifiques ; - Ne pas réinventer la roue, toujours utiliser (ou étendre)
sfUserpour gérer la session utilisateur ; - Ne pas créer un
myTools.class.phpavec une tonne de méthodes, essayer de ranger les différentes méthodes suivant leur utilité ; - Eviter au maximum l’utilisation de
sfContext::getInstance(), utiliser l’injection de dépendances ; - Supprimer les contrôleurs
*_devde l’instance de production.
Modèle
- Le HTML est uniquement autorisé dans les templates, partials ou components, jamais dans un contrôleur ou une action ;
- Ne pas oublier de créer quelques fixtures pour éviter d’avoir une base de données vierge à chaque réinitialisation ;
- Les “Criteria” Propel, “Doctrine_Query” ou requêtes SQL doivent être exclusivement dans le dossier
/lib; - Vérifier le nombre de requêtes par page, essayer de les réduire au maximum et optimiser les jointures.
Vue
- Ne jamais utiliser la syntaxe PHP courte
<?=$var?>; - Laisser l’output escaping activé (il faut aussi éviter de changer le paramètre de l’output escaping en plein milieu du développement pour éviter les mauvaises surprises) ;
- Toujours placer les slots en bas des templates ;
- Afin d’être lisibles par les intégrateurs, les templates doivent contenir du PHP en syntaxe alternative (
foreach/endforeach,if/endif…) ; - Eviter d’utiliser
view.ymlet préférer les slots pour gérer plus facilement le contenu des balises<title><meta>et optimiser ainsi le SEO ; - Ne pas oublier les pages d’erreur 404, 500 et de maintenance.
Contrôleur
- Les actions ne doivent jamais dépasser 20 lignes de code, et une classe du contrôleur ne doit pas avoir plus d’une quinzaine d’actions ;
- Tout ce qui n’est pas
executeXXX(),preExecute(), oupostExecute()doit êtreprotected; - Ne jamais stocker d’objet en session (même avec
serialize).
Formulaires
- Toujours rediriger la requête après un envoi de données en POST afin d’éviter la fenêtre de confirmation du navigateur (autorisé lors de l’affichage d’erreurs sur un formulaire, car le rafraîchissement entraîne une soumission des données, ce qui est logique) ;
- Ne pas désactiver la protection CSRF ;
- Créer des classes de formulaires différentes pour chaque action (
UserSignUpForm,UserEditForm…) et ne laisser que les propriétés communes dans la classeUserForm(valeurs par défauts, validateurs…).
Versionning
- Essayer d’utiliser un SCM (gestionnaire de versions) sur tous les projets (“Tout ce qui n’est pas versionné n’existe pas” Fabien Potencier) ;
- Les fichiers versionnés ne doivent jamais contenir de mots de passe, URLs ou chemins locaux… Par exemple, il faut versionner un fichier
databases.yml.dist“vierge” et ajouter la propriété “ignore” sur le vraidatabases.yml, ainsi les développeurs pourront le configurer différemment sur chaque instance de l’application sans créer de conflit ni dévoiler de données sensibles ; - Certains répertoires n’ont pas à être versionnés (
/cache,/log,/web/uploads…), il faut leur ajouter la propriétéignore; - Les fichiers générés (classes modèle/formulaires de base) ne doivent pas être versionnés.
Configuration
magic_quotes_gpcetmagic_quotes_runtimene doivent pas être activées dans la configuration de PHP ;- En production, l’option PHP
error_reportingdoit toujours être à 0 ; - Toujours configurer le cache symfony sur l’instance de production (sauf cas particuliers).
Documentation
- Documenter les classes et méthodes en utilisant la syntaxe phpDoc, cela assurera la compatibilité avec la plupart des IDE et permettra l’auto-complétion ;
- Rédiger la documentation en anglais ;
- Ajouter un fichier
READMEà la racine du projet, il devra décrire la procédure de déploiement d’une instance de l’application (création d’une base de données, configuration, insertion de fixtures, tests à exécuter…).
Tests
- Utiliser la commande symfony
test:coveragepour vérifier la couverture de l’application par les tests unitaires ; - Rédiger des tests fonctionnels pour les principales actions clés du site, ne pas tester les fonctions déjà testées par symfony (formulaires…) ;
- Utiliser
sfLoggerpour le stockage/affichage des messages d’erreurs.
Routing
- Ne jamais faire apparaître de clé primaire dans une URL, utiliser au maximum un slug à l’aide des object route classes ;
- Eviter d’utiliser la route par défaut
:module/:action(le premier réflexe à avoir sur un nouveau projet symfony est de supprimer les routesdefaultetdefault_index).
4 commentaires
Bonjour, merci pour cet article. Je ne comprend pas bien l’intérêt de cette bonne pratique :
“Ne jamais faire apparaître de clé primaire dans une URL, utiliser au maximum un slug à l’aide des object route classes”
Xavier.
Bonjour Xavier,
En fait, l’idée est d’éviter d’avoir des URI du type “/article/10″ car elles dévoilent à l’internaute la clé primaire et ne sont pas optimisées pour le référencement. Pour celà, on utilise un slug, qui est un champ unique permettant d’identifier un enregistrement, tout en étant lisible. On peut alors utiliser une URI du type “/article/tout-sur-symfony” par exemple.
Les slugs sont gérés nativement dans Doctrine : http://www.doctrine-project.org/projects/orm/1.2/docs/manual/behaviors/en#core-behaviors:sluggable
Sylvain
Merci pour cette réponse, cela n’est donc possible que quand un second champs de la relation est unique.




Forcer Doctrine à utiliser UTF-8 partout — Sylvain Deloux Freelance 05/02/2011 — 02h15
[...] avec d’autres en utf8_unicode_ci, et ça, c’est moche (tout le contraire d’une bonne pratique). Pire encore, l’utilisation d’encodages différents génère l’erreur MySQL [...]