Espace de noms PHP
Les espaces de noms PHP ont été introduits dans PHP 5.3 pour résoudre le problème des noms en double. En PHP, deux fonctions ou classes ne peuvent pas porter le même nom ; sinon, une erreur fatale se produit.
Les espaces de noms PHP peuvent résoudre les deux types de problèmes suivants :
- Les conflits de noms entre le code écrit par l'utilisateur et les classes/fonctions/constantes internes de PHP ou les classes/fonctions/constantes tierces.
- Créer un alias (ou un nom court) pour les noms d'identifiants très longs (généralement définis pour atténuer le premier type de problème), ce qui améliore la lisibilité du code source.
Définition des espaces de noms
Par défaut, toutes les constantes, classes et noms de fonctions sont placés dans l'espace global, exactement comme avant que PHP ne prenne en charge les espaces de noms.
Les espaces de noms sont déclarés à l'aide du mot-clé `namespace`. Si un fichier contient un espace de noms, il doit déclarer l'espace de noms avant tout autre code.
La syntaxe est la suivante :
<?php // 定义代码在 'MyProject' 命名空间中 namespace MyProject; // ... 代码 ...
Vous pouvez également définir du code de différents espaces de noms dans le même fichier, par exemple :
<?php
namespace MyProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
namespace AnotherProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
Il n'est pas recommandé d'utiliser cette syntaxe pour définir plusieurs espaces de noms dans un seul fichier. Il est recommandé d'utiliser la syntaxe avec accolades ci-dessous.
<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace AnotherProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
?>
Pour combiner du code global hors espace de noms avec du code d'espace de noms, vous pouvez uniquement utiliser la syntaxe avec accolades. Le code global doit être entouré d'une instruction `namespace` sans nom suivie d'accolades, par exemple :
<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>
Le seul code autorisé avant de déclarer un espace de noms est l'instruction `declare` utilisée pour définir l'encodage du fichier source. Aucun code non-PHP, y compris les espaces blancs, ne peut apparaître avant la déclaration de l'espace de noms.
<?php
declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>
Le code suivant provoquera une erreur de syntaxe :
<html> <?php namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句 ?>
Sous-espaces de noms
Tout comme la relation entre les répertoires et les fichiers, les espaces de noms PHP permettent également de spécifier des noms d'espaces de noms hiérarchiques. Par conséquent, les noms d'espaces de noms peuvent être définis de manière hiérarchique :
<?php
namespace MyProject\Sub\Level; //声明分层次的单个命名空间
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }
?>
L'exemple ci-dessus crée la constante `MyProject\Sub\Level\CONNECT_OK`, la classe `MyProject\Sub\Level\Connection` et la fonction `MyProject\Sub\Level\Connect`.
Utilisation des espaces de noms
Les noms de classes dans les espaces de noms PHP peuvent être référencés de trois manières :
Noms non qualifiés, ou noms de classes sans préfixe,Par exemple `$a = new foo();` ou `foo::staticmethod();`. Si l'espace de noms actuel est `currentnamespace`, `foo` sera résolu en `currentnamespace\foo`. Si le code utilisant `foo` est global et n'est contenu dans aucun espace de noms, `foo` sera résolu en `foo`. Avertissement : si une fonction ou une constante dans un espace de noms n'est pas définie, le nom de fonction ou de constante non qualifié sera résolu en nom de fonction ou de constante global.
Noms qualifiés, ou noms avec un préfixe,Par exemple `$a = new subnamespace\foo();` ou `subnamespace\foo::staticmethod();`. Si l'espace de noms courant est `currentnamespace`, `foo` sera résolu en `currentnamespace\subnamespace\foo`. Si le code utilisant `foo` est global et n'est contenu dans aucun espace de noms, `foo` sera résolu en `subnamespace\foo`.
Noms pleinement qualifiés, ou noms qui incluent l'opérateur de préfixe global,Par exemple, `$a = new \currentnamespace\foo();` ou `\currentnamespace\foo::staticmethod();`. Dans ce cas, `foo` est toujours résolu en le nom littéral `currentnamespace\foo` dans le code.
Voici un exemple utilisant ces trois méthodes :
code du fichier file1.php
<?php
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}
?>
code du fichier file2.php
<?php
namespace Foo\Bar;
include 'file1.php';
const FOO = 2;
function foo() {}
class foo
{
static function staticmethod() {}
}
/* 非限定名称 */
foo(); // 解析为函数 Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo ,方法为 staticmethod
echo FOO; // 解析为常量 Foo\Bar\FOO
/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo,
// 以及类的方法 staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO
/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>
Notez que toute classe, fonction ou constante globale peut être accédée à l'aide d'un nom pleinement qualifié, tel que `\strlen()`, `\Exception` ou `\INI_ALL`.
Accès aux classes, fonctions et constantes globales à l'intérieur d'un espace de noms :
<?php
namespace Foo;
function strlen() {}
const INI_ALL = 3;
class Exception {}
$a = \strlen('hi'); // 调用全局函数strlen
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception
?>
Espaces de noms et fonctionnalités dynamiques du langage
L'implémentation des espaces de noms de PHP est affectée par les fonctionnalités dynamiques du langage lui-même. Par conséquent, pour convertir le code suivant en espaces de noms, vous devez accéder aux éléments dynamiquement.
code du fichier example1.php :
<?php
class classname
{
function __construct()
{
echo __METHOD__,"\n";
}
}
function funcname()
{
echo __FUNCTION__,"\n";
}
const constname = "global";
$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global
?>
Vous devez utiliser des noms pleinement qualifiés (noms de classes incluant le préfixe d'espace de noms). Notez que, comme il n'y a pas de différence entre les noms qualifiés et les noms pleinement qualifiés dans les noms dynamiques de classes, de fonctions ou de constantes, l'antislash initial est inutile.
Accès dynamique aux éléments d'un espace de noms
<?php
namespace namespacename;
class classname
{
function __construct()
{
echo __METHOD__,"\n";
}
}
function funcname()
{
echo __FUNCTION__,"\n";
}
const constname = "namespaced";
include 'example1.php';
$a = 'classname';
$obj = new $a; // 输出 classname::__construct
$b = 'funcname';
$b(); // 输出函数名
echo constant('constname'), "\n"; // 输出 global
/* 如果使用双引号,使用方法为 "\\namespacename\\classname"*/
$a = '\namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // 输出 namespacename\funcname
$b = '\namespacename\funcname';
$b(); // 输出 namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // 输出 namespaced
echo constant('namespacename\constname'), "\n"; // 输出 namespaced
?>
Le mot-clé namespace et la constante __NAMESPACE__
PHP prend en charge deux méthodes abstraites pour accéder aux éléments de l'espace de noms courant : la constante magique `__NAMESPACE__` et le mot-clé `namespace`.
La valeur de la constante `__NAMESPACE__` est une chaîne contenant le nom de l'espace de noms courant. Dans le code global, non inclus dans un espace de noms, elle contient une chaîne vide.
Exemple avec `__NAMESPACE__`, code dans un espace de noms
<?php namespace MyProject; echo '"', __NAMESPACE__, '"'; // 输出 "MyProject" ?>
Exemple avec `__NAMESPACE__`, code global
<?php echo '"', __NAMESPACE__, '"'; // 输出 "" ?>
La constante `__NAMESPACE__` est utile pour créer dynamiquement des noms, par exemple :
Utilisation de `__NAMESPACE__` pour créer dynamiquement des noms
<?php
namespace MyProject;
function get($classname)
{
$a = __NAMESPACE__ . '\\' . $classname;
return new $a;
}
?>
Le mot-clé `namespace` peut être utilisé pour accéder explicitement aux éléments de l'espace de noms courant ou d'un sous-espace de noms. Il équivaut à l'opérateur `self` dans les classes.
L'opérateur `namespace`, code dans un espace de noms
<?php namespace MyProject; use blah\blah as mine; // 引入了 blah\blah 命名空间,并定义了个别名mine mine\mine(); // 调用函数 blah\blah\mine() namespace\blah\mine(); // 调用函数 MyProject\blah\mine() namespace\func(); // 调用函数 MyProject\func() namespace\sub\func(); // 调用函数 MyProject\sub\func() namespace\cname::method(); // 调用 MyProject\cname 类的静态方法 $a = new namespace\sub\cname(); // 实例化 MyProject\sub\cname 类的对象 $b = namespace\CONSTANT; // 将常量 MyProject\CONSTANT 的值赋给 $b ?>
L'opérateur `namespace`, code global
<?php namespace\func(); // calls function func() namespace\sub\func(); // calls function sub\func() namespace\cname::method(); // calls static method "method" of class cname $a = new namespace\sub\cname(); // instantiates object of class sub\cname $b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b ?>
Utilisation des espaces de noms : alias/importations
La prise en charge des espaces de noms en PHP offre deux manières d'utiliser des alias ou des importations : utiliser un alias pour les noms de classes, ou utiliser un alias pour les noms d'espaces de noms.
En PHP, les alias sont implémentés avec l'opérateur `use`. Voici un exemple utilisant les trois méthodes d'importation possibles :
1. Importer/utiliser des alias avec l'opérateur `use`
<?php namespace foo; use My\Full\Classname as Another; // 下面的例子与 use My\Full\NSname as NSname 相同 use My\Full\NSname; // 导入一个全局类 use \ArrayObject; $obj = new namespace\Another; // 实例化 foo\Another 对象 $obj = new Another; // 实例化 My\Full\Classname 对象 NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func $a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象 // 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象 ?>
2. Inclure plusieurs instructions `use` sur une seule ligne
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // 实例化 My\Full\Classname 对象 NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func ?>
L'opération d'importation est exécutée au moment de la compilation, mais les noms dynamiques de classes, de fonctions ou de constantes ne le sont pas.
3. Importations et noms dynamiques
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // 实例化一个 My\Full\Classname 对象 $a = 'Another'; $obj = new $a; // 实际化一个 Another 对象 ?>
De plus, l'opération d'importation n'affecte que les noms non qualifiés et les noms qualifiés. Les noms pleinement qualifiés, étant déterministes, ne sont pas affectés par les importations.
4. Importations et noms pleinement qualifiés
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // 实例化 My\Full\Classname 类 $obj = new \Another; // 实例化 Another 类 $obj = new Another\thing; // 实例化 My\Full\Classname\thing 类 $obj = new \Another\thing; // 实例化 Another\thing 类 ?>
Utilisation des espaces de noms : fonctions/constantes globales de repli
Dans un espace de noms, lorsque PHP rencontre un nom de classe, de fonction ou de constante non qualifié, il utilise différentes stratégies de priorité pour résoudre le nom. Les noms de classes sont toujours résolus vers des noms de l'espace de noms courant. Par conséquent, lors de l'accès à des noms de classes internes du système ou à des noms de classes non contenus dans un espace de noms, vous devez utiliser des noms pleinement qualifiés, par exemple :
1. Accéder à des classes globales dans un espace de noms
<?php
namespace A\B\C;
class Exception extends \Exception {}
$a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
$b = new \Exception('hi'); // $b 是类 Exception 的一个对象
$c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类
?>
Pour les fonctions et les constantes, si la fonction ou la constante n'existe pas dans l'espace de noms courant, PHP se rabattra sur l'utilisation de la fonction ou de la constante dans l'espace global.
2. Fonctions/constantes globales de repli dans les espaces de noms
<?php
namespace A\B\C;
const E_ERROR = 45;
function strlen($str)
{
return \strlen($str) - 1;
}
echo E_ERROR, "\n"; // 输出 "45"
echo INI_ALL, "\n"; // 输出 "7" - 使用全局常量 INI_ALL
echo strlen('hi'), "\n"; // 输出 "1"
if (is_array('hi')) { // 输出 "is not array"
echo "is array\n";
} else {
echo "is not array\n";
}
?>
Espace global
Si aucun espace de noms n'est défini, toutes les définitions de classes et de fonctions sont dans l'espace global, comme avant que PHP n'introduise le concept d'espaces de noms. Ajouter le préfixe `\` devant un nom indique que le nom est un nom de l'espace global, même si le nom se trouve dans d'autres espaces de noms.
Explication de l'utilisation de l'espace global
<?php
namespace A\B\C;
/* 这个函数是 A\B\C\fopen */
function fopen() {
/* ... */
$f = \fopen(...); // 调用全局的fopen函数
return $f;
}
?>
Ordre de résolution des espaces de noms
Depuis l'avènement des espaces de noms, ce qui est le plus sujet aux erreurs, c'est l'utilisation des classes : quel est le chemin de recherche de la classe ?
<?php
namespace A;
use B\D, C\E as F;
// 函数调用
foo(); // 首先尝试调用定义在命名空间"A"中的函数foo()
// 再尝试调用全局函数 "foo"
\foo(); // 调用全局空间函数 "foo"
my\foo(); // 调用定义在命名空间"A\my"中函数 "foo"
F(); // 首先尝试调用定义在命名空间"A"中的函数 "F"
// 再尝试调用全局函数 "F"
// 类引用
new B(); // 创建命名空间 "A" 中定义的类 "B" 的一个对象
// 如果未找到,则尝试自动装载类 "A\B"
new D(); // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
// 如果未找到,则尝试自动装载类 "B\D"
new F(); // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
// 如果未找到,则尝试自动装载类 "C\E"
new \B(); // 创建定义在全局空间中的类 "B" 的一个对象
// 如果未发现,则尝试自动装载类 "B"
new \D(); // 创建定义在全局空间中的类 "D" 的一个对象
// 如果未发现,则尝试自动装载类 "D"
new \F(); // 创建定义在全局空间中的类 "F" 的一个对象
// 如果未发现,则尝试自动装载类 "F"
// 调用另一个命名空间中的静态方法或命名空间函数
B\foo(); // 调用命名空间 "A\B" 中函数 "foo"
B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
// 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"
D::foo(); // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
// 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"
\B\foo(); // 调用命名空间 "B" 中的函数 "foo"
\B::foo(); // 调用全局空间中的类 "B" 的 "foo" 方法
// 如果类 "B" 未找到,则尝试自动装载类 "B"
// 当前命名空间中的静态方法或函数
A\B::foo(); // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
// 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"
\A\B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
// 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>
La résolution des noms suit les règles suivantes :
- Les appels à des fonctions, des classes et des constantes avec des noms pleinement qualifiés sont résolus au moment de la compilation. Par exemplenew \A\Bse résout en la classeA\B。
- Tous les noms non qualifiés et les noms qualifiés (noms non pleinement qualifiés) sont convertis au moment de la compilation selon les règles d'importation actuelles. Par exemple, si l'espace de nomsA\B\Cest importé commeC, alors un appel àC\D\e()sera converti enA\B\C\D\e()。
- À l'intérieur d'un espace de noms, tous les noms qualifiés qui n'ont pas été convertis selon les règles d'importation se verront préfixés par le nom de l'espace de noms courant. Par exemple, dans l'espace de nomsA\B, un appel àC\D\e(), alorsC\D\e()sera converti enA\B\C\D\e() 。
- Les noms de classes non qualifiés sont convertis au moment de la compilation selon les règles d'importation actuelles (le nom complet remplace le nom court importé). Par exemple, si l'espace de nomsA\B\Cest importé comme C, alorsnew C()est converti ennew A\B\C() 。
- À l'intérieur d'un espace de noms (par exemple, A\B), les appels de fonctions à des noms non qualifiés sont résolus à l'exécution. Par exemple, pour la fonctionfoo()l'appel est résolu comme suit :
- Rechercher dans l'espace de noms courant une fonction nomméeA\B\foo()fonction
- Essayez de trouver et d'appeler laGlobalfonction dans l'espace globalfoo()。
- À l'intérieur d'un espace de noms (par exemple,A\B), les appels à des noms de classes non qualifiés ou qualifiés (pas des noms pleinement qualifiés) sont résolus au moment de l'exécution. Voici le processus de résolution pour appelernew C()etnew D\E()le processus de résolution : new C()résolution de :
- Recherchez laA\B\Cclasse dans l'espace de noms actuel.
- Tentative de chargement automatique de la classeA\B\C。
- Préfixez le nom de l'espace de noms actuel au nom de la classe, ce qui donne :A\B\D\E, puis recherchez cette classe.
- Tentative de chargement automatique de la classeA\B\D\E。