(PHP 8)
Les attributs permettent d'ajouter des informations de métadonnées structurées et lisibles par la machine sur les déclarations dans le code: les classes, les méthodes, les fonctions, les paramètres, les propriétés et les constantes de classe peuvent être la cible d'un attribut. Les métadonnées définies par les attributs peuvent ensuite être inspectées au moment de l'exécution à l'aide de l'API de Réflexion. Les attributs peuvent donc être considérés comme un langage de configuration intégré directement dans le code.
Avec les attributs, il est possible de découpler la mise en œuvre générique d'une fonctionnalité et son utilisation concrète dans une application. D'une certaine manière, ils sont comparables aux interfaces et à leurs implémentations. Mais là où les interfaces et les implémentations portent sur le code, les attributs concernent l'annotation d'informations supplémentaires et la configuration. Les interfaces peuvent être implémentées par des classes, mais les attributs peuvent également être déclarés sur sur les méthodes, les fonctions, les paramètres, les propriétés et les constantes de classe. Ils sont donc plus flexibles que les interfaces.
Un exemple simple d'utilisation d'attributs consiste à convertir une interface
qui a des méthodes optionnelles pour utiliser des attributs. Supposons qu'une
interface ActionHandler
représentant une opération dans une application
où certaines implémentations d'un gestionnaire d'action nécessitent une configuration et d'autres non.
Au lieu d'exiger que toutes les classes qui implémentent ActionHandler
implémentent
une méthode setUp()
, un attribut peut être utilisé. L'un des avantages
de cette approche est que nous pouvons utiliser l'attribut plusieurs fois.
Exemple #1 Implémentation de méthodes optionnelles d'une interface avec des attributs
<?php
interface ActionHandler
{
public function execute();
}
#[Attribute]
class SetUp {}
class CopyFile implements ActionHandler
{
public string $fileName;
public string $targetDirectory;
#[SetUp]
public function fileExists()
{
if (!file_exists($this->fileName)) {
throw new RuntimeException("Le fichier n'existe pas.");
}
}
#[SetUp]
public function targetDirectoryExists()
{
if (!file_exists($this->targetDirectory)) {
mkdir($this->targetDirectory);
} elseif (!is_dir($this->targetDirectory)) {
throw new RuntimeException("Le répertoire cible $this->targetDirectory n'est pas un répertoire.");
}
}
public function execute()
{
copy($this->fileName, $this->targetDirectory . '/' . basename($this->fileName));
}
}
function executeAction(ActionHandler $actionHandler)
{
$reflection = new ReflectionObject($actionHandler);
foreach ($reflection->getMethods() as $method) {
$attributes = $method->getAttributes(SetUp::class);
if (count($attributes) > 0) {
$methodName = $method->getName();
$actionHandler->$methodName();
}
}
$actionHandler->execute();
}
$copyAction = new CopyFile();
$copyAction->fileName = "/tmp/foo.jpg";
$copyAction->targetDirectory = "/home/user";
executeAction($copyAction);