PHP namespaces (namespace)
Los namespaces de PHP se introdujeron en PHP 5.3 para resolver el problema de los nombres duplicados. En PHP no se permite que dos funciones o clases tengan el mismo nombre, de lo contrario se produce un error fatal.
Los namespaces de PHP pueden resolver los siguientes dos tipos de problemas:
- Conflictos de nombres entre el código escrito por el usuario y las clases/funciones/constantes internas de PHP o las clases/funciones/constantes de terceros.
- Crear un alias (o un nombre corto) para nombres de identificadores muy largos (definidos generalmente para aliviar el primer tipo de problema), mejorando la legibilidad del código fuente.
Definir namespaces
De forma predeterminada, todas las constantes, clases y nombres de funciones se colocan en el espacio global, igual que antes de que PHP admitiera namespaces.
Los namespaces se declaran mediante la palabra clave namespace. Si un archivo contiene un namespace, debe declararse antes que cualquier otro código.
El formato de sintaxis es el siguiente:
<?php // 定义代码在 'MyProject' 命名空间中 namespace MyProject; // ... 代码 ...
También puedes definir diferentes códigos de namespace en el mismo archivo, por ejemplo:
<?php
namespace MyProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
namespace AnotherProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
No se recomienda usar esta sintaxis para definir múltiples namespaces en un solo archivo. Se recomienda usar la sintaxis con llaves que se muestra a continuación.
<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace AnotherProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
?>
Para combinar código global sin namespace con código en un namespace, solo se puede usar la sintaxis con llaves. El código global debe encerrarse entre llaves con una sentencia namespace sin nombre, por ejemplo:
<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>
El único código legal antes de declarar un namespace es la sentencia declare que define la codificación del archivo fuente. Ningún código que no sea PHP, incluidos los espacios en blanco, puede aparecer antes de la declaración del namespace.
<?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();
}
?>
El siguiente código producirá un error de sintaxis:
<html> <?php namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句 ?>
Sub-namespaces
Muy parecido a la relación entre directorios y archivos, los namespaces de PHP también permiten especificar nombres de namespace jerárquicos. Por lo tanto, los nombres de namespace se pueden definir de manera jerárquica:
<?php
namespace MyProject\Sub\Level; //声明分层次的单个命名空间
const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */ }
?>
El ejemplo anterior crea la constante MyProject\Sub\Level\CONNECT_OK, la clase MyProject\Sub\Level\Connection y la función MyProject\Sub\Level\Connect.
Uso de namespaces
Los nombres de clase en los namespaces de PHP se pueden referenciar de tres maneras:
Nombre no calificado, o nombre de clase sin prefijo,por ejemplo $a=new foo(); o foo::staticmethod();. Si el namespace actual es currentnamespace, foo se resolverá como currentnamespace\foo. Si el código que usa foo es global, es decir, no está incluido en ningún namespace, entonces foo se resolverá como foo. Advertencia: si una función o constante no está definida en el namespace, el nombre no calificado de la función o constante se resolverá como el nombre de la función o constante global.
Nombre calificado, o nombre con prefijo,por ejemplo $a = new subnamespace\foo(); o subnamespace\foo::staticmethod();. Si el namespace actual es currentnamespace, entonces foo se resolverá como currentnamespace\subnamespace\foo. Si el código que usa foo es global, es decir, no está incluido en ningún namespace, foo se resolverá como subnamespace\foo.
Nombre totalmente calificado, o nombre que incluye el operador de prefijo global,por ejemplo, $a = new \currentnamespace\foo(); o \currentnamespace\foo::staticmethod();. En este caso, foo siempre se resuelve como el nombre literal en el código currentnamespace\foo.
A continuación se muestra un ejemplo que utiliza estas tres formas:
Código del archivo file1.php
<?php
namespace Foo\Bar\subnamespace;
const FOO = 1;
function foo() {}
class foo
{
static function staticmethod() {}
}
?>
Código del archivo 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
?>
Ten en cuenta que para acceder a cualquier clase, función o constante global se puede usar el nombre totalmente calificado, por ejemplo \strlen(), \Exception o \INI_ALL.
Acceder a clases, funciones y constantes globales dentro de un namespace:
<?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
?>
Namespaces y características dinámicas del lenguaje
La implementación de los namespaces de PHP se ve afectada por las características dinámicas del propio lenguaje. Por lo tanto, si se desea convertir el siguiente código a un namespace, se debe acceder a los elementos de forma dinámica.
Código del archivo 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
?>
Se debe usar el nombre totalmente calificado (nombre de clase que incluye el prefijo del namespace). Ten en cuenta que en los nombres dinámicos de clase, función o constante no hay diferencia entre el nombre calificado y el totalmente calificado, por lo que la barra invertida inicial no es necesaria.
Acceso dinámico a elementos de un namespace
<?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
?>
La palabra clave namespace y la constante __NAMESPACE__
PHP admite dos formas abstractas de acceder a los elementos dentro del namespace actual: la constante mágica __NAMESPACE__ y la palabra clave namespace.
El valor de la constante __NAMESPACE__ es una cadena que contiene el nombre del namespace actual. En el código global, es decir, el que no está incluido en ningún namespace, contiene una cadena vacía.
Ejemplo de __NAMESPACE__, código en un namespace
<?php namespace MyProject; echo '"', __NAMESPACE__, '"'; // 输出 "MyProject" ?>
Ejemplo de __NAMESPACE__, código global
<?php echo '"', __NAMESPACE__, '"'; // 输出 "" ?>
La constante __NAMESPACE__ es útil al crear nombres dinámicamente, por ejemplo:
Usar __NAMESPACE__ para crear nombres dinámicamente
<?php
namespace MyProject;
function get($classname)
{
$a = __NAMESPACE__ . '\\' . $classname;
return new $a;
}
?>
La palabra clave namespace se puede usar para acceder explícitamente a elementos del namespace actual o de un sub-namespace. Es equivalente al operador self en las clases.
Operador namespace, código en un namespace
<?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 ?>
Operador namespace, código 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 ?>
Usar namespaces: alias/importación
Los namespaces de PHP admiten dos formas de usar alias o importación: usar alias para nombres de clase, o usar alias para nombres de namespace.
En PHP, los alias se implementan mediante el operador use. A continuación se muestra un ejemplo que utiliza las tres formas posibles de importación:
1. Importar/usar alias con el operador 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. Múltiples sentencias use en una sola línea
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // 实例化 My\Full\Classname 对象 NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func ?>
La operación de importación se ejecuta en tiempo de compilación, pero los nombres dinámicos de clase, función o constante no.
3. Importación y nombres dinámicos
<?php use My\Full\Classname as Another, My\Full\NSname; $obj = new Another; // 实例化一个 My\Full\Classname 对象 $a = 'Another'; $obj = new $a; // 实际化一个 Another 对象 ?>
Además, la operación de importación solo afecta a los nombres no calificados y calificados. Los nombres totalmente calificados, al ser determinados, no se ven afectados por la importación.
4. Importación y nombres totalmente calificados
<?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 类 ?>
Usar namespaces: funciones/constantes globales de respaldo
Dentro de un namespace, cuando PHP encuentra un nombre no calificado de clase, función o constante, utiliza diferentes estrategias de prioridad para resolver dicho nombre. Los nombres de clase siempre se resuelven al nombre en el namespace actual. Por lo tanto, al acceder a clases internas del sistema o a nombres de clase que no están en un namespace, se debe usar el nombre totalmente calificado, por ejemplo:
1. Acceder a clases globales en un namespace
<?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 类
?>
Para funciones y constantes, si la función o constante no existe en el namespace actual, PHP recurrirá a la función o constante del espacio global.
2. Funciones/constantes globales de respaldo en un namespace
<?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";
}
?>
Espacio global
Si no se define ningún namespace, todas las clases y funciones se definen en el espacio global, igual que antes de que PHP introdujera el concepto de namespaces. Anteponer el prefijo \ a un nombre indica que dicho nombre pertenece al espacio global, incluso si el nombre se encuentra en otro namespace.
Explicación del uso del espacio global
<?php
namespace A\B\C;
/* 这个函数是 A\B\C\fopen */
function fopen() {
/* ... */
$f = \fopen(...); // 调用全局的fopen函数
return $f;
}
?>
Orden de los namespaces
Desde que existen los namespaces, lo que más fácilmente causa errores es al usar clases: cuál es la ruta de búsqueda de dicha clase.
<?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 resolución de nombres sigue las siguientes reglas:
- Las llamadas a funciones, clases y constantes con nombres totalmente calificados se resuelven en tiempo de compilación. Por ejemplonew \A\Bse resuelve como claseA\B。
- Todos los nombres no calificados y calificados (no totalmente calificados) se transforman en tiempo de compilación según las reglas de importación actuales. Por ejemplo, si el namespaceA\B\Cse importa comoC, entonces la llamada aC\D\e()se convertirá enA\B\C\D\e()。
- Dentro de un namespace, a todos los nombres calificados que no se hayan transformado según las reglas de importación se les antepone el nombre del namespace actual. Por ejemplo, en el namespaceA\Bllamar aC\D\e(), entoncesC\D\e()se convertirá enA\B\C\D\e() 。
- Los nombres de clase no calificados se convierten en tiempo de compilación según las reglas de importación actuales (sustituyendo el nombre de importación corto por el nombre completo). Por ejemplo, si el espacio de nombresA\B\Cse importa como C, entoncesnew C()se convierte ennew A\B\C() 。
- Dentro de un espacio de nombres (por ejemplo, A\B), las llamadas a funciones con nombres no calificados se resuelven en tiempo de ejecución. Por ejemplo, la llamada a la funciónfoo()se resuelve de la siguiente manera:
- En el espacio de nombres actual, buscar el nombreA\B\foo()de la función
- Intentar buscar y llamaren el espacio globalla funciónfoo()。
- En el espacio de nombres (por ejemplo,A\B), las llamadas a clases con nombres no calificados o calificados (nombres no totalmente calificados) se resuelven en tiempo de ejecución. A continuación, la llamada anew C()ynew D\E()se resuelve de la siguiente manera: new C()se resuelve así:
- En el espacio de nombres actual, buscar el nombreA\B\Cde la clase.
- Intentar autocargar la claseA\B\C。
- Se antepone el nombre del espacio de nombres actual al nombre de la clase, quedando:A\B\D\E, y luego buscar esa clase.
- Intentar autocargar la claseA\B\D\E。