Home > Blog > Geek > Bonnes pratiques de développement PHP/symfony

Bonnes pratiques de développement PHP/symfony

01/01/2011 — Mots-clés : , — 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 protected viennent après les méthodes public (jamais de private) ;
  • Pour faciliter la navigation dans les sources, toujours utiliser le même nom de fichier que la classe qu’il contient : classe sfSessionStorage dans sfSessionStorage.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.yml par 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/lib et apps/xxx/modules/yyy/lib, sauf pour des cas vraiment spécifiques ;
  • Ne pas réinventer la roue, toujours utiliser (ou étendre) sfUser pour gérer la session utilisateur ;
  • Ne pas créer un myTools.class.php avec 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 *_dev de 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.yml et 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(), ou postExecute() doit être protected ;
  • 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 classe UserForm (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 vrai databases.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_gpc et magic_quotes_runtime ne doivent pas être activées dans la configuration de PHP ;
  • En production, l’option PHP error_reporting doit 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:coverage pour 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 sfLogger pour 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 routes default et default_index).

4 commentaires

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 [...]

Xavier 07/06/2011 — 17h20

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.

Sylvain 07/06/2011 — 17h39

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

Xavier 09/06/2011 — 17h50

Merci pour cette réponse, cela n’est donc possible que quand un second champs de la relation est unique.

Rédiger un commentaire