Namespace

Pourquoi avons-nous besoin d’espaces de noms ?

Comme la taille de votre bibliothèque de code PHP augmente, il existe un risque accru de redéfinition accidentelle d’une fonction ou d’un nom de classe qui a été déclaré auparavant. Le problème est exacerbant lorsque vous tentez d’ajouter des composants tiers ou plugins. Que se passe-t-il si vous instaurez une classe Database ou User qui existe déjà ? Jusqu’à présent, la seule solution a été d’utiliser des noms de fonctions longs. Par exemple, WordPress préfixe chaque nom avec “wp_ “. Le Zend Framework utilisais une convention de nommage très descriptif afin de donner des noms de classes longs comme la classe

Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive.

Les problèmes de collision de noms peuvent être résolus avec les espaces de noms. Les constantes PHP, classes et fonctions peuvent être regroupées en bibliothèques d’espace de noms.

Comment définir les espaces de noms ?

Par défaut, toutes les constantes, les classes, et les noms de fonction sont placés dans l’espace global - comme elles l’étaient avant que PHP supporte les espaces de noms.

Le code des espaces de noms est défini en utilisant un mot-clé namespace unique en haut de votre fichier PHP. Ce doit être la première commande (à l’exception de declare) et pas de code non-PHP, HTML, ou d’espaces blancs ne peuvent précéder la commande, par exemple.

<?php
// défini ce code dans l'espace de noms 'MonProjet'
namespace MonProjet;
// ... code ...

Le code suivant sera affecté à l’espace de noms MonProjet. Il n’est pas possible de définir des nids d’espaces de noms ou plusieurs espaces de noms pour le même bloc de code (seul le dernier sera reconnu). Toutefois, vous pouvez définir un espace de noms de code différents dans le même fichier, par exemple :

<?php
namespace MonProjet1;
// code PHP pour l'espace de nom MonProjet1
namespace MonProjet2;
// code PHP pour l'espace de nom MonProjet2
// Syntaxe alternative
namespace MonProjet3 {
// // code PHP pour l'espace de nom MonProjet3
 } ?>

Bien que cela soit possible, je le déconseille : conserver votre santé mentale en définissant un seul espace de noms par fichier.

Sous-espaces de noms

PHP vous permet de définir une hiérarchie d’espaces de noms afin de subdiviser vos bibliothèques. Les sous-espaces de noms sont séparés par une barre oblique inverse (\), par exemple :

  • MonProjet \ Nom
  • MonProjet \ Database \ MySQL
  • NomEntreprise \ MonProjet \ Bibliothèque \ FichiersCommuns \ widget1

Appeler du code des espaces de noms

Dans un fichier nommé lib1.php, nous allons définir une constante, une fonction et une classe au sein de l’espace de noms App \ LIB1 :

<?php
// application library 1
namespace App\Lib1;
const MYCONST = 'App\Lib1\MYCONST';
function MyFunction() {
    return __FUNCTION__;
}
class MyClass {
     static function WhoAmI() {
         return __METHOD__;
     }
} ?>

Nous pouvons maintenant inclure ce code dans un autre fichier PHP, parexemple :

<?php
header('Content-type: text/plain');
require_once('lib1.php');
echo \App\Lib1\MYCONST . "\n";
echo \App\Lib1\MyFunction() . "\n";
echo \App\Lib1\MyClass::WhoAmI() . "\n";
?>

Aucun espace de noms n’est défini dans myapp.php afin que le code existe dans l’espace global. Toute référence directe à MYCONST, MyFunction ou MyClass va échouer parce qu’elles existent dans l’espace de noms App \ LIB1. Pour appeler du code dans lib1.php, on peut ajouter un préfixe \ App \ LIB1 pour définir des noms pleinement qualifiés. Le résultat suivant est émis quand on charge myapp.php :

App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI

Les noms pleinement qualifiés peuvent devenir assez longs et il y a peu d’avantages évidents par rapport à la définition des noms de classe longs tels que App-LIB1-MyClass. Par conséquent, dans le prochain article, nous allons discuter du cumul et jeter un oeil de plus près sur le fonctionnement de PHP quant à la résolution des noms d’espace de noms.

A titre d’exemple, nous allons définir deux blocs de code presque identique, la seule différence est leur espace de noms :

<?php
// application library 1
namespace App\Lib1;

const MYCONST = 'App\Lib1\MYCONST';

function MyFunction() {
    return __FUNCTION__;
}

class MyClass {
    static function WhoAmI() {
        return __METHOD__;
    }
}
?>
<?php
// application library 2
namespace App\Lib2;

const MYCONST = 'App\Lib2\MYCONST';
function MyFunction() {
    return __FUNCTION__;
}

class MyClass {
    static function WhoAmI() {
        return __METHOD__;
    }
}
?>

Il y a un peu de terminologie PHP à comprendre avant de commencer...

Nom pleinement qualifié

Tout code PHP peut se référer à un nom pleinement qualifié - un identifiant commençant l’espace de noms et par le séparateur de barre oblique inverse - par exemple \App\LIB1\MYCONST, \App\LIB2\MyFunction(), etc.

Les noms pleinement qualifiés n’ont pas d’ambiguïté. La première barre oblique inverse fonctionne de manière semblable à un chemin de fichier, il signifie l’espace global du «root». Si nous avons implémenté une fonction MyFunction() différente dans notre espace global, elle pourrait être appelée à partir de lib1.php ou lib2.php en utilisant \MyFunction().

Les noms pleinement qualifiés sont utiles pour des appels uniques de fonctions ou d’initialisation objet. Toutefois, ils peuvent devenir impraticables lorsque vous faites beaucoup d’appels. Comme nous allons le découvrir, PHP offre d’autres options pour nous sauver des crampes de frappe des espaces de nommage.

Nom qualifié

Un identifiant avec au moins un séparateur d’espace de noms, par exemple LIB1\MyFunction().

Nom non qualifié

Un identifiant, sans séparateur d’espace de noms, par exemple MyFunction().

Travail au sein du même espace de noms

Considérons le code suivant :

<?php
namespace App\Lib1;

require_once('lib1.php');
require_once('lib2.php');

header('Content-type: text/plain');
echo MYCONST . "\n";
echo MyFunction() . "\n";
echo MyClass::WhoAmI() . "\n";
?>

Même si l’on inclut à la fois lib1.php et lib2.php, les identificateurs MYCONST, MyFunction, et MyClass référencent seulement le code dans lib1.php. Cela se produit parce que le code myapp1.php est dans le même espace de noms \LIB1 :

Résultat :

App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI

Importation d’espace de noms

Les espaces de noms peuvent être importés grâce à l’opérateur use, par exemple :

<?php
use App\Lib2;

require_once('lib1.php');
require_once('lib2.php');

header('Content-type: text/plain');
echo Lib2\MYCONST . "\n";
echo Lib2\MyFunction() . "\n";
echo Lib2\MyClass::WhoAmI() . "\n";
?>
Vous pouvez définir autant de use que voulu ou vous pouvez séparer les espaces de noms par une virgule.
Dans cet exemple, nous avons importé l’espace de noms App\LIB2. Nous ne pouvons toujours pas faire référence directement à MYCONST, MyFunction ou MyClass parce que notre code se trouve dans l’espace global, et PHP va les chercher là-bas. Toutefois, si l’on ajoute un préfixe de LIB2\, ils deviennent des noms qualifiés.
PHP va chercher dans les espaces de noms importés jusqu’à ce qu’il trouve une correspondance.

Résultat :

App\Lib2\MYCONST
App\Lib2\MyFunction
App\Lib2\MyClass::WhoAmI

Les alias d’espace de noms

Les alias d’espace de noms sont peut-être les implémentations les plus utiles. Les alias nous permettent de référencer les espaces de noms en utilisant un nom plus court.

<?php
use App\Lib1 as L;
use App\Lib2\MyClass as Obj;

header('Content-type: text/plain');
require_once('lib1.php');
require_once('lib2.php');

echo L\MYCONST . "\n";
echo L\MyFunction() . "\n";
echo L\MyClass::WhoAmI() . "\n";
echo Obj::WhoAmI() . "\n";
?>

La première déclaration de use définit App\LIB1 comme « L ». Les noms qualifiés utilisant des «L » seront traduits par App\LIB1 au moment de la compilation. On peut donc se référer à L\MYCONST et L\MyFunction() plutôt qu’à leurs noms pleinement qualifiés.

La seconde déclaration de use est plus intéressante. Elle définit ‘obj‘ comme alias pour la classe MyClass au sein de l’espace de noms App\Lib2\. Cela n’est possible que pour les classes, pas pour des constantes ou des fonctions. Nous pouvons maintenant utiliser new Obj() ou exécuter des méthodes statiques, comme indiqué ci-dessus.

Résultat

App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI
App\Lib2\MyClass::WhoAmI

Les règles de résolution de noms en PHP

Les noms d’identifiants en PHP sont résolus en utilisant les règles d’espace de noms suivant. Reportez-vous au manuel PHP pour plus d’information.

  1. Les appels à des fonctions pleinement qualifiées, des classes ou des constantes sont résolues au moment de la compilation.
  2. Les noms qualifiés ou non sont convertis selon les règles d’importation, par exemple si le nom A\B\C est importé en tant que C, un appel à C\D\e() est traduit en A\B\C\D\e().
  3. L’intérieur d’un espace de noms, tous les noms qualifiés qui ne sont pas déjà convertis selon les règles d’importation ont l’espace de noms courant préfixé, par exemple si un appel à C\D\e() est effectué au sein de l’espace de noms A\B, il est traduit à A\B\C\D\e().
  4. Les noms de classes non qualifiées sont traduits selon les règles actuelles d’importation et le nom complet est remplacé par le nom importé court, par exemple si la classe C dans un espace de noms \B est importée sous X, new X() est traduit en new A\B\C().
  5. Les appels de fonctions non qualifiées au sein d’un espace de noms sont résolus au moment de l’exécution. Par exemple, si MyFunction() est appelée dans l’espace de noms A\B, PHP cherche d’abord la fonction \A\B\MyFunction(). Si elle n’est pas trouvée, il cherche \MyFunction() dans l’espace global.
  6. Les appels à des noms de classes non qualifiés ou qualifiés sont résolus au moment de l’exécution. Par exemple, si nous appelons new C() au sein de l’espace de noms A\B, PHP va chercher la classe A\B\C. Si elle n’est pas trouvée, il va tenter de charger automatiquement A\B\C.

La constante _NAMESPACE_

_NAMESPACE_ est une chaîne PHP qui retourne toujours le nom de l’espace de noms courant. Dans l’espace global, elle renverra une chaîne vide.

<?php
namespace App\Lib1;
echo __NAMESPACE__;
// sortie : App\Lib1
?>

La valeur présente des avantages évidents durant le débogage. Elle peut également être utilisée pour générer dynamiquement un nom de classe entièrement qualifié, par exemple :

<?php
 namespace App\Lib1;

 class MyClass {
     public function WhoAmI() {
         return __METHOD__;
     }
 }

 $c = __NAMESPACE__ . ' \\MyClass';
 $m = new $c;
 echo $m->WhoAmI(); // sortie : App\Lib1\MyClass::WhoAmI
 ?>

Le mot clé namespace

Le mot clé namespace peut être utilisé pour référencer explicitement un élément dans l’espace de nom courant ou un sous-espace de noms. C’est l’équivalent dans les espaces de noms de self au sein des classes :

<?php
namespace App\Lib1;

class MyClass {
    public function WhoAmI() {
        return __METHOD__;
    }
}

$m = new namespace\MyClass;
echo $m->WhoAmI(); // sortie : App\Lib1\MyClass::WhoAmI
?>

Autoloading des classes d’un espace de noms

L’un des meilleurs moyens de gagner du temps avec PHP 5 est l’autoloading. À l’échelle globale (non-espace de noms) du code PHP, une fonction de chargement automatique standard pourrait être écrite comme ceci :

<?php
$obj1 = new MyClass1(); // classes/MyClass1.php is auto-loaded
$obj2 = new MyClass2(); // classes/MyClass2.php is auto-loaded

// autoload function
function __autoload($class_name) {
    require_once("classes/$class_name.php");
}
?>

En PHP 5.3, vous pouvez créer une instance d’une classe d’espace de noms. Dans cette situation, l’espace de noms entièrement qualifié et le nom de classe sont passés à la fonction __autoload(), par exemple la valeur de $class_name pourrait être App\LIB1\MyClass. Vous pouvez continuer à mettre tous vos fichiers de classe PHP dans le même dossier et y chainer l’espace de nom, cependant, cela pourrait entraîner des conflits de nom de fichier.

Alternativement, la hiérarchie de vos fichiers de classes pourraient être organisée de la même manière que la structure de votre espace de noms. Par exemple, un fichier MyClass.php pourrait être créé dans le dossier /classes/App/Lib1 :

<?php
namespace App\Lib1;

class MyClass {
    public function WhoAmI() {
        return __METHOD__;
    }
}
?>

Un fichier dans le dossier racine pourrait alors utiliser le code suivant :

<?php
use App\Lib1\MyClass as MC;

$obj = new MC();
echo $obj->WhoAmI();

// autoload function
function __autoload($class) {

// convert namespace to full file path
$class = 'classes/' . str_replace('\\', '/', $class) . '.php';
require_once($class);

}
?>

Explications :

  • La classe \LIB1\MyClass est aliassée en tant que MC.
  • new MC() est traduit par new App\Lib1\MyClass() pendant la compilation.
  • La chaîne App\LIB1\MyClass est passé à la fonction __autoload. Cela remplace tous les antislash de l’espace de noms avec le chemin du fichier avant le slash, et modifie la chaîne si le fichier classes\App\LIB1\ MyClass.php est chargé.