- 6 minutes

PHP : À quoi servent les PSR ?

PHP
Markdown logo

Les PSR sont des PHP Standard Recommendations édités par le PHP-FIG (PHP Framework Interop Group).

Le but est d’éditer des recommandations publiques afin d’assurer une compatibilité accrue entre projets, ainsi qu’une compréhension commune de certains concepts.

A l’heure actuelle, on a de PSR-0 à PSR-22, certaines d’entre elles étant dépréciées, abandonnées, ou en cours de réalisation.

Style de code

Parmi les différentes PSR, on peut en trouver présentant des recommandations sur le style de code à adopter.

Le but visé est que ce soit plus simple, pour des développeurs qui travaillent sur plusieurs projets, de lire du code ayant les mêmes règles d’écriture. Bien sûr, personne n’est obligé de les suivre, mais pour des projets open-source par exemple, il peut être intéressant d’adhérer à des standards publiques, pour attirer davantage de contributeurs et garder une cohérence de style dans le code de l’outil open-source.

Nous retrouverons donc PSR-1 ou PSR-12, nous indiquant quelles règles à suivre, par exemple :

  • Si un fichier PHP ne contient que du PHP, ne pas fermer avec la balise ?>
  • Laisser une ligne vide à la fin d’un fichier PHP
  • Le mot-clé abstract doit apparaître avant la visibilité pour un membre de classe
  • La visibilité est obligatoire pour un membre de classe
  • etc…

Il est très compliqué d’apprendre et se rappeler toutes les règles d’écriture d’une recommandation. Ainsi, des outils permettent d’automatiser la vérification et la mise en forme du code. Par exemple, PHP-CS-Fixer (Coding Standards Fixer) peut être utilisé pour ce genre de choses.

Auto-chargement (autoloading)

Une autre recommandation standard concerne l’auto-chargement des classes.

Le moyen de charger des classes “à l’ancienne” consiste simplement à require_once le fichier. Mais cela peut vite devenir très pénible à organiser.

Pour répondre à cette problématique, on utilisera la recommandation PSR-4, qui se base sur l’arborescence des fichiers.

Espaces de noms

PSR-4 nous propose d’organiser nos classes sous divers namespaces (espaces de noms).

Ainsi, on pourra trouver des classes comme Symfony\Component\Mailer\Mailer ou bien App\Shop\OrderService par exemple.

Les classes dont il s’agit ici sont respectivement Mailer et OrderService. Elles se trouvent simplement sous des namespaces.

On déclarera donc, pour chaque classe, un namespace :

<?php
namespace App\Shop;

//...

class OrderService {
  /* ... */
}

Et dans un autre fichier, qui veut utiliser OrderService :

<?php
// namespace... (autre namespace si besoin)

use App\Shop\OrderService; // inclusion de la classe

// ...

FQCN

Pour chaque classe de notre application, la recommandation PSR-4 permet d’établir un FQCN ou Fully Qualified Class Name. Il s’agit du chemin complet depuis le namespace racine jusqu’au nom de la classe.

Comment organiser ses fichiers ?

PSR-4 fonctionne avec l’arborescence des fichiers.

En réalité, pour chaque namespace racine, PSR-4 aura besoin d’un chemin correspondant. Ensuite, pour tout namespace qui se trouve en-dessous, elle suivra simplement l’arborescence correspondante, jusqu’au nom de la classe.

Exemples

Dans un projet

Dans nos propres projets, on pourra déclarer et utiliser un autoload PSR-4.

Pour ce faire, on aura simplement à déclarer, dans le fichier composer.json, la section autoload avec la structure appropriée, par exemple, une structure couramment utilisée (dans Symfony, par exemple…) consiste à déclarer un namespace racine App\ associé à un dossier src/. Ainsi, toutes nos classes et notre logique se trouveront dans le dossier src/. Libre à nous de créer de nouveaux répertoires pour organiser des sous espaces de noms.

// composer.json
{
  //...
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
  //...
}

Par la suite, on pourra alors créer nos classes dans le répertoire src de l’application.

Dans un composant Symfony : le Mailer

Dans beaucoup de projets, on trouvera également la règle d’autoloading PSR-4.

Dans le composant Mailer de Symfony par exemple, si on s’intéresse au composer.json :

{
  "autoload": {
    "psr-4": { "Symfony\\Component\\Mailer\\": "" }
    //...
  }
}

Cette déclaration nous indique donc que toutes les classes ou interfaces qui se trouvent à la racine du projet Mailer auront le namespace Symfony\Component\Mailer.

Interfaces

Les PSR peuvent également définir des interfaces générales, que n’importe qui peut décider d’implémenter.

Prenons l’exemple de PSR-11 : il s’agit d’une interface ContainerInterface présentant un contrat à implémenter si on souhaite être compatible PSR-11. Le but visé, comme indiqué sur la page de la recommandation, est d’établir un standard concernant la manière dont les bibliothèques et frameworks utilisent un container de services pour obtenir des objets et des paramètres au sein d’une application.

Ainsi, on trouvera par exemple le composant d’injection de dépendance de Symfony déclarant en première phrase :

The DependencyInjection component implements a PSR-11 compatible service container that allows you to standardize and centralize the way objects are constructed in your application.

Et si on s’intéresse au code du composant, on y trouvera également toutes les informations qu’on recherche.

Exemple : symfony/dependency-injection

Partons en exploration au sein du composant d’injection de dépendance de Symfony. Il respecte l’autoloading PSR-4 donc on naviguera très facilement dedans.

On trouve donc, dans le fichier composer.json, une dépendance vers le package psr/container. Il s’agit du package contenant l’interface PSR-11 à implémenter.

Par la suite, si on se rend à la racine du projet, dans la classe ContainerInterface, on pourra constater qu’elle hérite de l’interface de PSR. Par ailleurs, ils ont attribué un alias à l’interface de PSR, puisqu’ils voulaient créer leur propre ContainerInterface, portant le même nom que celle de PSR :

namespace Symfony\Component\DependencyInjection;

use Psr\Container\ContainerInterface as PsrContainerInterface;
//...

interface ContainerInterface extends PsrContainerInterface
{
  //...
}

On retrouvera donc les méthodes get et has, mais aussi d’autres méthodes comme getParameter ou set, par exemple.

Enfin, si on se rend dans la classe Container, on remarquera qu’elle-même déclare implémenter ContainerInterface.

Si on se rend à présent sur le repository de PHP-DI, un autre conteneur d’injection de dépendances connu dans l’écosystème PHP, alors on retrouve la dépendance psr/container et la classe principale implémente le même contrat :

namespace DI;

//...
use Psr\Container\ContainerInterface;

class Container implements ContainerInterface, FactoryInterface, InvokerInterface
{
    //...
}

Il existe bien d’autres interfaces dans les PSR, par exemple :

En gros, on saura à présent ce que signifie une bibliothèque indiquée “compatible PSR-XX”.