С этого поста хочу начать цикл статей о возможностях языка, которые начинающие разработчики часто игнорируют, а потом в ежедневной суете не остается времени на их изучение или применение и часто люди избегают использования таких крутых возможностей языка, что становится странно. Все-таки php сейчас - один из самых динамично развивающихся языков, но иногда мне кажется, что многие разработчики просто застряли на эпохе зарождения php 5.

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

В этой статье я хочу поведать о пространствах имен. Все-таки php 5.3 давно уже закрепился на наших проектах как минимальная версия (за исключением совсем уж древних мамонтов). Однако не все еще понимают что это вообще такое и как ими пользоваться.

Была раньше такая проблематика - предположим, что в вашем проекте есть определенная библиотека, которая содержит класс Order, который организует удобную работу с заказами. Она глубоко интегрирована в систему, стабильно работает уже давно. Теперь Вам понадобилось сделать страницу, на которой отображается список заказов для покупателя. И тут вы наткнулись на классную библиотеку, которая может быстро в 2 строчки генерировать любые списки с фильтрами и сортировкой. Вы подключаете, пишете эти 2 строчки, хотите увидеть список заказов в красивой табличке, жмете F5 и - Fatal Error … Покопавшись узнаете, что в библиотеке содержится внутренний класс Order, который отвечает за возможность сортировки списков … тоскапичаль … Вам придется либо изменять класс в библиотеке (а как же обновления? нет, это не выход 🙁 ), либо менять везде в проекте имя своего класса.

Увы, до 5.3 это было реальностью, приходилось выкручиваться, обзывать классы длинными именами, чтобы по максимуму их унифицировать. Но в 5.3 появились пространства имен.

Я не буду заострять внимание на том, что пространства имен - это один из способов инкапсуляции сущностей языка (классов, функций). Если говорить простым языком, то можно сравнить пространство имен с директориями в операционной системе.
Например, в windows мы знаем, что программы сохраняются в директорию C:/Program Files, а программа Adobe Photoshop хранится по адресу C:/Program Files/Adobe/Photoshop (к примеру.. я не знаю вообще как они хранятся)
Можно сказать, что С/Program Files/Adobe/Photoshop - это именованное пространство, в котором хранится все необходимое для работы такой программы как Photoshop. Но файлы то все равно хранятся на едином жестком диске, он же не разбит на подпапки. Просто каждый файл указывает на определенную директорию.

Вот также можно и ваши классы раскидать по «виртуальным директориям», заставить их указывать на директории так, чтобы их имена не пересекались с другими одноименными классами. Просто если добавить какой-то класс в пространство имен, его полное имя видоизменяется. Если раньше вы для подключения класса писали что-то вроде этого:

include LIBRARY_DIR . '/MyClass.php';
$instance = new MyClass;

, то теперь, если содержимое класса определено в пространстве имен Classes, придется писать вот так:

include LIBRARY_DIR . '/MyClass.php';
$instance = new Classes\MyClass;

Давайте посмотрим на содержимое файла MyClass.php, чтобы понять как определить пространство имен:

namespace Classes;

class MyClass 
{
  private bar = 5;

  public getBar()
  {
    return $this->bar;
  }
}

Как мы видим, для определения пространства имен достаточно добавить в начало файла строку namespace Classes;
Важно, чтобы до определения пространства имен не было кода в файле (за исключением выражения declare, определяющего кодировку файла, которое я ни разу в жизни не использовал 🙂 ), иначе будет Fatal Error
Есть альтернативный синтаксис, можно определить пространство имен в скобочной нотации, тогда весь код, относящийся к этому пространству имен должен находиться внутри фигурных скобок. Тот же пример:

namespace Classes
{
  class MyClass 
  {
    private bar = 5;

    public getBar()
    {
      return $this->bar;
    }
  }
}

Одно и то же пространство имен может быть разделено по нескольким файлам. Например можно сделать файл MyOtherClass.php и снова определить там пространство Classes - и все будет работать.

Предвидя вопрос о подключении файлов с неймспейсами - инклюдить файлы все-равно придется, не обольщайтесь. Лучше настроить Autoload (тема следующей статьи), и не париться насчет ручного подключения.

Пространства имен, как и директории, могут иметь вложенность. Давайте сделаем файл с тем же названием - MyClass.php, положим его в соседнюю с нашим существующим классом директорию и определим для него вложенное пространство имен

namespace Classes\Other;

class MyClass 
{
  private foo = 'I\'m is Foo';

  public getFoo()
  {
    return 'Hello! '.$this->foo;
  }
}

Дополним предыдущий пример обращения к классу:

include LIBRARY_DIR . '/MyClass.php';
include LIBRARY_DIR . '/other/MyClass.php';

$instance = new Classes\MyClass;
$otherInstance = new Classes\Other\MyClass;

Как видим - базовое имя классов одно и то же, но пространства имен у них разные, и это будет работать.

Чем больше вложенность, тем длиннее будет выглядеть подключение класса. Это, конечно неудобно. Можно взглянуть на новое ядро битрикс, к примеру. Чтобы воспользоваться сущностью, которая получит опцию из конфига, нужно написать вот такую монструозину:

$paramValue = \Bitrix\Main\Config\Configuration::getValue("http_auth_realm");

Длинновато … но есть выход! Можно в текущем файле импортировать какое-либо пространство имен или класс, и тогда не нужно будет писать длинное имя класса. Для примера выше это будет выглядеть так:

use Bitrix\Main\Config\Configuration;
$paramValue = Configuration::getValue("http_auth_realm");

Можно дать пространству имен псевдоним, и обращаться по псевдониму (а лучше давать адекватные имена неймспейсам).

Опять же - для примера выше можно сделать так:

use Bitrix\Main\Config\Configuration as Conf;
$paramValue = Conf::getValue("http_auth_realm");

Последнее, на чем я хочу заострить внимание - это то, как правильно подключать неймспейсы.
Помните, что к каждому файлу в php можно обратиться несколькими способами - используя только имя файла (когда мы находимся в одной директории с файлом), используя относительный путь до файла (относительно какой-то директории), и абсолютный путь до файла (относительно корня)?
Точно также работают namespace - есть корневой namespace - \ (кстати в нем тоже можно определить свои сущности. либо написав их внутри namespace { /* тут ваши сущности */} без имени, либо без объявления namespace). Все остальные неймспейсы являются вложенными в него.

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