Пространство имён PHP

Пространства имён PHP были введены в PHP 5.3 для решения проблемы дублирования имён. В PHP две функции или класса не могут иметь одинаковое имя; в противном случае возникает фатальная ошибка.

Пространства имён PHP могут решить следующие два типа проблем:

  1. Конфликты имён между кодом, написанным пользователем, и внутренними классами/функциями/константами PHP или сторонними классами/функциями/константами.
  2. Создание псевдонима (или короткого) имени для очень длинных имён идентификаторов (обычно определяемых для смягчения первой проблемы), повышающее читаемость исходного кода.

Определение пространств имён

По умолчанию все константы, классы и имена функций помещаются в глобальное пространство, точно так же, как до того, как PHP стал поддерживать пространства имён.

Пространства имён объявляются с помощью ключевого слова `namespace`. Если файл содержит пространство имён, он должен объявить пространство имён до всего остального кода.

Синтаксис выглядит так:

<?php  
// 定义代码在 'MyProject' 命名空间中  
namespace MyProject;  
 
// ... 代码 ...  

Вы также можете определять код разных пространств имён в одном файле, например:

<?php  
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
?>  

Не рекомендуется использовать этот синтаксис для определения нескольких пространств имён в одном файле. Рекомендуется использовать синтаксис с фигурными скобками, приведённый ниже.

<?php
namespace MyProject {
    const CONNECT_OK = 1;
    class Connection { /* ... */ }
    function connect() { /* ... */  }
}

namespace AnotherProject {
    const CONNECT_OK = 1;
    class Connection { /* ... */ }
    function connect() { /* ... */  }
}
?>

Чтобы объединить глобальный код, не относящийся к пространствам имён, с кодом пространства имён, можно использовать только синтаксис с фигурными скобками. Глобальный код должен быть заключён в безымянный оператор `namespace` с фигурными скобками, например:

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

Единственный допустимый код перед объявлением пространства имён — это оператор `declare`, используемый для определения кодировки исходного файла. Любой не-PHP код, включая пробельные символы, не может находиться перед объявлением пространства имён.

<?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();
}
?>

Следующий код вызовет синтаксическую ошибку:

<html>
<?php
namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句
?>

Подпространства имён

Подобно связи между каталогами и файлами, пространства имён PHP также позволяют задавать иерархические имена пространств имён. Поэтому имена пространств имён можно определять иерархическим способом:

<?php
namespace MyProject\Sub\Level;  //声明分层次的单个命名空间

const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */  }

?>

Приведённый выше пример создаёт константу `MyProject\Sub\Level\CONNECT_OK`, класс `MyProject\Sub\Level\Connection` и функцию `MyProject\Sub\Level\Connect`.


Использование пространств имён

На имена классов в пространствах имён PHP можно ссылаться тремя способами:

  1. Неквалифицированные имена, или имена классов без префикса,Например, `$a = new foo();` или `foo::staticmethod();`. Если текущее пространство имён — `currentnamespace`, `foo` будет разрешено в `currentnamespace\foo`. Если код, использующий `foo`, является глобальным и не содержится ни в каком пространстве имён, `foo` будет разрешено в `foo`. Предупреждение: если функция или константа в пространстве имён не определена, неквалифицированное имя функции или имя константы будет разрешено в глобальное имя функции или имя константы.

  2. Квалифицированные имена, или имена с префиксом,Например, `$a = new subnamespace\foo();` или `subnamespace\foo::staticmethod();`. Если текущее пространство имен — `currentnamespace`, `foo` будет разрешено в `currentnamespace\subnamespace\foo`. Если код, использующий `foo`, является глобальным и не содержится ни в каком пространстве имен, `foo` будет разрешено в `subnamespace\foo`.

  3. Полностью квалифицированные имена, или имена, включающие оператор глобального префикса,Например, `$a = new \currentnamespace\foo();` или `\currentnamespace\foo::staticmethod();`. В этом случае `foo` всегда разрешается в буквальное имя `currentnamespace\foo` в коде.

Ниже приведен пример использования этих трех методов:

код файла file1.php

<?php
namespace Foo\Bar\subnamespace; 

const FOO = 1;
function foo() {}
class foo
{
    static function staticmethod() {}
}
?>

код файла 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
?>

Обратите внимание, что к любому глобальному классу, функции или константе можно обратиться, используя полностью квалифицированное имя, например `\strlen()`, `\Exception` или `\INI_ALL`.

Доступ к глобальным классам, функциям и константам внутри пространства имен:

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

Пространства имен и динамические возможности языка

На реализацию пространств имен PHP влияют динамические возможности самого языка. Поэтому, чтобы преобразовать следующий код в пространства имен, нужно динамически обращаться к элементам.

код файла 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
?>

Вы должны использовать полностью квалифицированные имена (имена классов, включая префикс пространства имен). Обратите внимание, что, поскольку в динамических именах классов, именах функций или именах констант нет разницы между квалифицированными и полностью квалифицированными именами, ведущий обратный слеш не нужен.

Динамический доступ к элементам пространства имен

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

Ключевое слово namespace и константа __NAMESPACE__

PHP поддерживает два абстрактных метода для доступа к элементам в текущем пространстве имён: магическую константу `__NAMESPACE__` и ключевое слово `namespace`.

Значение константы `__NAMESPACE__` — это строка, содержащая имя текущего пространства имён. В глобальном коде, не включённом ни в какое пространство имён, она содержит пустую строку.

Пример `__NAMESPACE__`, код в пространстве имён

<?php
namespace MyProject;

echo '"', __NAMESPACE__, '"'; // 输出 "MyProject"
?>

Пример `__NAMESPACE__`, глобальный код

<?php

echo '"', __NAMESPACE__, '"'; // 输出 ""
?>

Константа `__NAMESPACE__` полезна при динамическом создании имён, например:

Использование `__NAMESPACE__` для динамического создания имён

<?php
namespace MyProject;

function get($classname)
{
    $a = __NAMESPACE__ . '\\' . $classname;
    return new $a;
}
?>

Ключевое слово `namespace` можно использовать для явного доступа к элементам в текущем пространстве имён или подпространстве имён. Оно эквивалентно оператору `self` в классах.

Оператор `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
?>

Оператор `namespace`, глобальный код

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

Использование пространств имён: псевдонимы/импорты

Поддержка пространств имён в PHP имеет два способа использования псевдонимов или импортов: использование псевдонима для имён классов или использование псевдонима для имён пространств имён.

В PHP псевдонимы реализуются с помощью оператора `use`. Ниже приведён пример, использующий все три возможных способа импорта:

1. Импорт/использование псевдонимов с помощью оператора `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. Включение нескольких операторов `use` в одну строку

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
?>

Операция импорта выполняется на этапе компиляции, но не для динамических имён классов, функций или констант.

3. Импорт и динамические имена

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化一个 My\Full\Classname 对象
$a = 'Another';
$obj = new $a;      // 实际化一个 Another 对象
?>

Кроме того, операция импорта влияет только на неквалифицированные и квалифицированные имена. Полностью квалифицированные имена, будучи детерминированными, не подвержены влиянию импорта.

4. Импорт и полностью квалифицированные имена

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

Использование пространств имён: резервные глобальные функции/константы

В пространстве имён, когда PHP встречает неквалифицированное имя класса, функции или константы, он использует разные стратегии приоритета для разрешения имени. Имена классов всегда разрешаются в имена в текущем пространстве имён. Поэтому при доступе к внутренним системным именам классов или именам классов, не содержащимся в пространстве имён, необходимо использовать полностью квалифицированные имена, например:

1. Доступ к глобальным классам в пространстве имён

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

Для функций и констант, если функция или константа не существует в текущем пространстве имён, PHP выполнит откат к использованию функции или константы в глобальном пространстве.

2. Резервные глобальные функции/константы в пространствах имён

<?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";
}
?>

Глобальное пространство

Если пространство имён не определено, все определения классов и функций находятся в глобальном пространстве, как и до того, как PHP ввёл концепцию пространств имён. Добавление префикса `\` перед именем указывает, что имя принадлежит глобальному пространству, даже если имя находится в других пространствах имён.

Объяснение использования глобального пространства

<?php
namespace A\B\C;

/* 这个函数是 A\B\C\fopen */
function fopen() { 
     /* ... */
     $f = \fopen(...); // 调用全局的fopen函数
     return $f;
} 
?>

Порядок разрешения пространств имён

С появлением пространств имён самым частым источником ошибок стало использование классов: каков путь поиска класса?

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

Разрешение имён подчиняется следующим правилам:

  1. Вызовы функций, классов и констант с полностью квалифицированными именами разрешаются во время компиляции. Напримерnew \A\Bразрешается в классA\B。
  2. Все неквалифицированные имена и квалифицированные имена (не полностью квалифицированные имена) преобразуются во время компиляции в соответствии с текущими правилами импорта. Например, если пространство имёнA\B\Cимпортируется какC, то вызовC\D\e()будет преобразован вA\B\C\D\e()。
  3. Внутри пространства имён все квалифицированные имена, которые не были преобразованы в соответствии с правилами импорта, получают префикс в виде имени текущего пространства имён. Например, в пространстве имёнA\Bпри вызовеC\D\e(), тогдаC\D\e()будет преобразован вA\B\C\D\e() 。
  4. Неквалифицированные имена классов преобразуются во время компиляции в соответствии с текущими правилами импорта (полное имя заменяет краткое импортированное имя). Например, если пространство имёнA\B\Cимпортируется как C, тогдаnew C()преобразуется вnew A\B\C() 。
  5. Внутри пространства имён (например, A\B) вызовы функций с неквалифицированными именами разрешаются во время выполнения. Например, для функцииfoo()вызов разрешается следующим образом:
    1. Поиск в текущем пространстве имён функции с именемA\B\foo()функция
    2. Попробуйте найти и вызвать функциюGlobalв глобальном пространствеfoo()。
  6. Внутри пространства имен (например,A\B), вызовы неквалифицированных или квалифицированных имен классов (не полностью квалифицированных имен) разрешаются во время выполнения. Ниже приведен процесс разрешения для вызоваnew C()иnew D\E()процесс разрешения: new C()разрешение:
    1. ПоискA\B\Cкласса в текущем пространстве имен.
    2. Попытка автозагрузки классаA\B\C。
    new D\E()разрешение:
    1. Припишите имя текущего пространства имен к имени класса, в результате получится:A\B\D\E, затем ищите этот класс.
    2. Попытка автозагрузки классаA\B\D\E。
    Чтобы сослаться на глобальный класс в глобальном пространстве имен, необходимо использовать полностью квалифицированное имяnew \C()。
Другие расширения