Aller au contenu principal

PSR

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éresssant d'adhérer à des standards publiques, pour attirer davantage de contributeurs.

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.

Arborescence

Cela signifie qu'il faudra organiser nos fichiers très précisément pour que l'autoloading PSR-4 fonctionne correctement

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éclarerea donc, pour chaque classe, un namespace :

OrderService

<?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.

Différencier les classes

C'est ce qui permettra de différencier des classes comme Symfony\Component\Mailer\Mailer et App\Service\Mailer.

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.

Nommage et respect de la casse

Lorsqu'on nommera puis importera nos classes, on veillera donc particulièrement au respect de la casse (les majuscules et les minuscules dans les noms).

Il faut que le FQCN corresponde lettre pour lettre aux noms des dossiers et du fichier PHP final.

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.

Classes dans notre application

Ainsi, on pourra par exemple créer un fichier src/Currency/Converter.php et la classe correspondante sera App\Currency\Converter.

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.

info

PSR-4 est le successeur de PSR-0, qui est à présent dépréciée

Interfaces​

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

Interopérabilité

Le support d'interfaces définies par les PSR par différents outils open-source permet une plus grande interopérabilité entre les outils. Non seulement on utilise des interfaces connues, car publiques et recommandées, mais on peut également changer une implémentation pour une autre, du moment que les implémentations implémentent la même interface

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 librairies 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 attributé 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.

attention

La classe Container du composant de Symfony implémente l'interface ContainerInterface du même composant, pas celle de PSR ! C'est l'interface ContainerInterface qui elle-même hérite de celle de PSR

Il existe un tas d'autres interfaces dans les PSR. Mais en gros, on saura à présent ce que signifie une librairie indiquée "compatible PSR-XX".