Пространства имен

В PHP пространства имен используются для решения двух проблем, с которыми сталкиваются авторы библиотек и приложений при создании повторно используемых элементов кода, таких как классы и функции:

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

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

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

Хотя любой исправный PHP-код может находиться внутри пространства имен, только классы (включая абстрактные и трейты), интерфейсы, функции и константы зависят от него.

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

Так же как файлы и каталоги, пространства имен 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
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() { /* ... */  }
}
?>

Настоятельно не рекомендуется при программировании комбинировать несколько пространств имен в один файл. Основным применением этому может быть объединение нескольких PHP файлов в один файл.

Для объединения кода в глобальном пространстве имен с кодом в других пространствах имен, используется только синтаксис со скобками. Глобальный код должен быть помещен в конструкцию описания пространства имен без указания имени:

<?php
namespace MyProject {

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

namespace {       // глобальный код
    session_start();
    $a = MyProject\connect();
    echo MyProject\Connection::start();
}
?>

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

До обсуждения использования пространств имен важно понять как PHP узнает какие элементы из пространства имен запрашиваются в вашем коде. Можно провести аналогию между пространствами имен PHP и файловой системой. Есть три способа обратиться к файлу в файловой системе:

  1. Относительное имя файла, такое как foo.txt, преобразуемое в currentdirectory/foo.txt, где currentdirectory текущая директория, в которой мы находимся. Тогда, если текущая директория /home/foo, то имя преобразуется в /home/foo/foo.txt.

  2. Относительное имя пути, такое как subdirectory/foo.txt, преобразуется в currentdirectory/subdirectory/foo.txt.

  3. Абсолютное имя пути, такое как /main/foo.txt, которое остается таким же: /main/foo.txt.

Тот же принцип применим и к элементам из пространств имен PHP. Для примера, имя класса может быть указано тремя способами:

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

  2. Полные имена (имена классов с префиксами), такие как $a = new subnamespace\foo(); или subnamespace\foo::staticmethod();. Если текущее пространство имен currentnamespace, то эти имена преобразуются в currentnamespace\subnamespace\foo. Если код находится в глобальном пространстве имен, то имена преобразуются в subnamespace\foo.

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

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

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

Возможность ссылаться на внешнее абсолютное имя по псевдониму или импортирование - это важная особенность пространств имен. Это похоже на возможность файловых систем unix создавать символические ссылки на файл или директорию.

Все версии PHP, поддерживающие пространства имен, поддерживают три вида создания псевдонима имени или импорта: создание псевдонима для имени класса, создание псевдонима для имени интерфейса и для имени пространства имен. PHP 5.6+ также поддерживает импорт функций и имен констант.

В PHP создание псевдонима имени выполняется с помощью оператора use. Вот пример, показывающий 5 типов импорта:

<?php
namespace foo;
use My\Full\Classname as Another;

// это тоже самое, что и использование My\Full\NSname как NSname
use My\Full\NSname;

// импортирование глобального класса
use ArrayObject;

// импортирование функции (PHP 5.6+)
use function My\Full\functionName;

// псевдоним функции (PHP 5.6+)
use function My\Full\functionName as func;

// импортирование константы (PHP 5.6+)
use const My\Full\CONSTANT;

$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
func(); // вызывает функцию My\Full\functionName
echo CONSTANT; // выводит содержимое константы My\Full\CONSTANT
?>

Обратите внимание, что для имен в пространстве имен (абсолютные имена, содержащие разделитель пространств имен, такие как Foo\Bar, в отличие от глобальных имен, которые его не содержат, такие как FooBar) нет необходимости в начальном обратном слеше (\) и его присутствие там не рекомендуется, так как импортируемые имена должны быть абсолютными и не обрабатываются относительно текущего пространства имен. PHP дополнительно поддерживает удобное сокращение для задания нескольких операторов 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
?>

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

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

<?php
namespace A\B\C;

/* Эта функция является A\B\C\fopen */
function fopen() { 
     /* ... */
     $f = \fopen(...); // вызов глобальной функции fopen
     return $f;
} 
?>

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

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

<?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 будет прибегать к глобальным функциям или константам, если функция или константа не существует в пространстве имен.

<?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')) { // выводит строку "это не массив"
    echo "это массив\n";
} else {
    echo "это не массив\n";
}
?>

Слово namespace можно использовать при указании имени класса во время инициализации объекта:

namespace myApp\db;
class mysql {}

$a = new namespace\mysql();