Продолжаем знакомиться с пространствами имен. В предыдущей статье мы узнали, что такое пространства имен, зачем они нужны, узнали что инклюдить файлы все равно придется, и рассмотрели пример о том, как же ими пользоваться, как определять и т.д.
В этой статье рассмотрим остальные аспекты пространств имен, которые не были затронуты ранее.
Глобальное пространство имен
В конце прошлой статьи я говорил об относительности путей подключения пространств имен и сравнивал их с директориями в операционке. Так вот, как и в UNIX системах, например, есть корневой элемент. Адрес корня сервера имеет вид — /
Аналогично этому существует корневое пространство имен — \
Все стандартные функции языка, все ваши классы и функции, константы, описанные без привязки к пространству имен, на самом деле будут принадлежать глобальному пространству имен.
Если вы определили функцию вне пространства имен, то обратиться к ней можно будет двумя способами:
1 2 3 4 5 6 7 8 9 |
<?php function foo() { return 'Абсолютно бесполезная функция'; } echo foo(); // = Абсолютно бесполезная функция echo \foo(); // = Абсолютно бесполезная функция |
Поэтому вы всегда можете быть уверенными, что если вы вызываете функцию \print_r() — это будет всегда функция, встроенная в php.
Да да — вы не ослышались — внутри своего пространства имен можно определить функцию, которая будет иметь название, аналогичное встроенной функции. Вот пример:
1 2 3 4 5 6 7 8 9 10 |
<?php namespace Space; function print_r($s) { echo $s . ' - а вот и не print_r :Р'; } $toPrint = 'На печать'; print_r($toPrint); // = На печать - а вот и не print_r :Р \print_r($toPrint); // = На печать |
Поэтому — не стоит бездумно переопределять встроенные функции, т.к. это может ввести в заблуждение неопытных разработчиков.
Ну и будьте благоразумны при построении пути из неймспейсов при разработке. Если в вашем файле определено пространство имен, то все вызовы будут строиться от текущего пространства имен. Находясь в namespace Space вы должны осознавать, что вызов функции без префикса из пространств имен приведет к вызову функции из текущего пространства, а если вы вызываете функцию с пространством имен, то либо пространство должно быть относительным от текущего, либо должно начинаться с глобального
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace Universe; function wow() { echo 'Universe'; } namespace Universe\Galaxy; function wow() { echo 'Universe\Galaxy'; } wow(); // = Universe\Galaxy \Universe\Galaxy\wow(); // = Universe\Galaxy Universe\Galaxy\wow(); // = Fatal Error \Universe\wow(); // = Universe |
Таким образом какой вывод можно сделать из того, что мы уже изучили — то что пространство имен является частью имени сущности php. Эти самые имена теперь делятся на 3 вида:
- Неполные имена. Это привычные нам имена без пространств имен. Например — $a = new ClassName; function functionName() {}
- Полные (ваш КО) имена. Имена, которые содержат в названии пространство имен. Например — $a = new Galaxy\ClassName;
Причем, если текущее пространство имен — Universe, то данное имя класса будет приведено к Universe\Galaxy\ClassName. Такие имена я бы предпочитал называть относительными (мое имхо, по аналогии со ссылками и путями) - Абсолютные имена. Это те имена, которые начинаются с глобального пространства имен и содержат полный путь из неймспейсов. Например — $a = new \Universe\Galaxy\ClassName;
__NAMESPACE__ и namespace
Как и для многих сущностей в php, для пространства имен существует зарезервированная константа, именуется она как __NAMESPACE__ и содержит она в себе имя пространства имен, в контексте которого вызывается. Если в предыдущем примере подменить строку после echo на __NAMESPACE__ , то получится точно такой-же вывод.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php namespace Universe; function wow() { echo __NAMESPACE__; } namespace Universe\Galaxy; function wow() { echo __NAMESPACE__; } wow(); // = Universe\Galaxy \Universe\Galaxy\wow(); // = Universe\Galaxy \Universe\wow(); // = Universe |
Соответственно, усваивая информацию о том, что пространство имен является частью имени сущностей, можно проверить, что в константах __CLASS__, __METHOD__ и __FUNCTION__ тоже теперь содержится пространство имен.
Кстати, если использовать вывод __NAMESPACE__ находясь в глобальном пространстве имен, то будет выведена пустая строка, а не обратный слеш.
Есть еще ключевое слово namespace, которое указывает на необходимость явного указания текущего пространства имен. Хороший пример приведен в оф. документации, транслирую его сюда:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php namespace MyProject; use blah\blah as mine; // см. "Использование пространств имен: импорт/создание псевдонима имени" blah\mine(); // вызывает функцию MyProject\blah\mine() namespace\blah\mine(); // вызывает функцию MyProject\blah\mine() namespace\func(); // вызывает функцию MyProject\func() namespace\sub\func(); // вызывает функцию MyProject\sub\func() namespace\cname::method(); // вызывает статический метод "method" класса MyProject\cname $a = new namespace\sub\cname(); // Создает экземпляр класса MyProject\sub\cname $b = namespace\CONSTANT; // присваивает значение константы MyProject\CONSTANT переменной $b |
А также использование этого ключевого слова в глобальном пространстве имен:
1 2 3 4 5 6 7 |
<?php namespace\func(); // вызывает функцию func() namespace\sub\func(); // вызывает функцию sub\func() namespace\cname::method(); // вызывает статический метод "method" класса cname $a = new namespace\sub\cname(); // Создает экземпляр класса sub\cname $b = namespace\CONSTANT; // присваивает значение константы CONSTANT переменной $b |
Прочие тонкости
Если вы используете динамические имена сущностей, то стоит учесть одну небольшую особенность. В строке, содержащей имя, например, класса каждый обратный слеш должен быть экранирован, если вы хотите использовать его для пространства имен.
1 2 3 |
<? $className = '\\Bitrix\\Iblock\\ElementTable'; $className::query()->addFilter('IBLOCK_ID',1)->exec(); |
Нельзя использовать пространство имен, вложенное в пространство имен (скобочная нотация)
1 2 3 4 5 6 7 8 9 10 11 |
<?php // Не правильно namespace A { namespace B { function c() {}; } } //Правильно namespace A\B; function c() {}; |
Об остальных аспектах читайте в официальной документации, там есть еще моменты не рассмотренные мной, но я считаю их достаточно редкими кейсами, и если вы с ними сталкиваетесь. значит вы достаточно опытны для самостоятельного их разрешения.
Про неймспейсы — все