В PHP, начиная с версии 5.3 появились пространства имен. Большинство современных языков уже давно заимели себе такой функционал, но PHP немного опаздывал. Тем не менее, у каждой новой функции есть свое предназначение, давайте выясним, какие выгоды мы можем извлечь, используя namespace.
В PHP у вас не может быть два класса, названных одинаково, все они должны быть уникальны. Проблема этого ограничения в том, что если вы используете чью-либо стороннюю библиотеку, предоставляющую класс с именем User
, то вы не можете создать свой собственный класс, также названный User
. Если Вы уже создали свой класс User,
то после загрузки библиотеки PHP вызовет ошибку (Fatal error), так как в данный момент у нас объявляется две функции, с одинаковыми именами.
Пространства имен позволяют нам обойти эту проблему, и мы можем создать столько классов User
, сколько нам понадобится. Кроме того пространства имен позволят нам организовать код в удобные пакеты, а также обозначить свои права владения этим кодом.
Давайте взглянем на самый обычный класс.
<?php
// app/models/Eddard.php
class Eddard
{
}
Ничего особенно, как видите, и если вы хотите использовать его, просто сделайте так:
<?php
// app/routes.php
$eddard = new Eddard();
Сразу хотел бы отметить, что сейчас мы еще не говорим о пространствах имен и ни коем образом их не объявляем. А значит, по умолчанию используется глобальное пространство имен.
Давайте создадим еще одного Эддарда, рядом с тем, глобальным.
<?php
namespace Stark;
// app/models/another.php
class Eddard
{
}
Здесь у нас очень похожий класс с одним небольшим изменением, добавлена директива пространства имен. Строка namespace Stark;
говорит PHP что мы работаем в пространстве имен Stark и любой код (объявление классов, функций, переменных и т.д.) будет относиться к нему.
Итак, нам нужно создать нового Эдда, если вы решили что это нужно сделать вот так:
<?php
// app/routes.php
$eddard = new Eddard();
То нет, это не так. Здесь мы получаем экземпляр класса из первого примера, который мы создали ранее. Не тот, в пространстве имен Stark. Давайте попробуем создать экземпляр Эддарда Старка.
<?php
// app/routes.php
$eddard = new Stark\Eddard();
Для создания экземпляра класса нам нужно предварить имя класса префиксом из названия пространства имен, которому класс принадлежит, а в качестве разделителя использовать обратную косую черту. В итоге у нас есть экземпляр именно того класса, что нам нужен.
К слову сказать, пространства имён могут образовывать сколь угодно сложную иерархию, используя столько уровней, сколько потребуется. Например:
This\Namespace\And\Class\Combination\Is\Silly\But\Works
Помните, как я сказал вам, что PHP всегда работает относительно текущего пространства имен. Давайте взглянем на это в действии:
<?php
namespace Stark;
// app/routes.php
$eddard = new Eddard();
Добавив директиву пространства имён, мы дали понять PHP, что мы находимся в пространстве имён Stark. Так как именно в нем мы определили класс Eddard, то именно его мы и получим. Видите — все относительно.
Сейчас, когда мы изменили пространство имён, у нас возникла одна маленькая проблема. А как нам теперь получить наш оригинальный класс Eddard? Ну тот, который в глобальном пространстве?
К счастью в PHP есть трюк, который позволит нам решить эту проблему — просто добавив \
к имени класса.
<?php
// app/routes.php
$eddard = new \Eddard();
Видя ведущий слеш PHP понимает, что нужно выглянуть за пределы текущего namespace и создает экземпляр нужного нам класса.
А сейчас представь, что у нас есть класс из другого пространства имен, названный Tully\Edmure. Сейчас нам нужно использовать его внутри пространства Stark. И как нам это сделать?
<?php
namespace Stark;
// app/routes.php
$edmure = new \Tully\Edmure();
И снова нам пришлось использовать обратный слеш, чтобы перейти к глобальной видимости, прежде чем создать экземпляр класса в пространстве Tully.
Вообще, ссылаться вот так на классы из других пространств имен, используя полную иерархию в названии, может быть довольно утомительно. Но к счастью, есть возможность сделать ярлык, давайте посмотрим:
<?php
namespace Stark;
use Tully\Edmure;
// app/routes.php
$edmure = new Edmure();
Используя директиву use
, мы можем получить класс из другого пространства имён.
А, еще один маленький трюк! Мы можем дать нашим импортируемым классам "прозвища":
<?php
namespace Stark;
use Tully\Brynden as Blackfish;
// app/routes.php
$brynden = new Blackfish();
Используя ключевое слово as
, мы присвоили классу Tully/Brynden прозвище Blackfish, что позволяет нам использовать новое прозвище для его идентификации в текущем пространстве имен. Ловко, не так ли? Это также очень удобно, если вам нужно использовать два класса, названных одинаково, в пределах одного пространства имён:
<?php
namespace Targaryen;
use Dothraki\Daenerys as Khaleesi;
// app/routes.php
class Daenerys
{
}
// Targaryen\Daenerys
$daenerys = new Daenerys();
// Dothraki\Daenerys
$khaleesi = new Khaleesi();
Давая Daenerys из пространства Dothraki прозвище Khaleesi, мы можем использовать оба класса Daenerys. Довольно удобно, там мы можем использовать все необходимые классы в нашем приложении.
<?php
namespace Targaryen;
use Dothraki\Daenerys;
use Stark\Eddard;
use Lannister\Tyrion;
use Snow\Jon as Bastard;
Пространства имен также могут помочь нам в организации нашего кода. Позвольте, я продемонстрирую.
Скажем, необходимо создать библиотеку с открытым исходным кодом. Необходимо, чтобы другие могли использовать этот код.
Беда в том, что имена классов в этом коде будут конфликтовать с собственным приложением пользователя библиотеки. Это недопустимо. Вот как можно решить эту проблему:
Dayle\Blog\Content\Post
Dayle\Blog\Content\Page
Dayle\Blog\Tag
Здесь можно использовать любое имя, чтобы показать принадлежность кода , и отделить свой код от кода пользователя библиотеки. Внутри базового пространства имен создается структура классов в соответствии с их иерархией.
Начав использовать composer, вы узнаете, как использовать пространства имён для упрощения автозагрузки кода. Я настоятельно рекомендую вам взглянуть на этот полезный механизм.
Дело в том, что в других языках функционал пространств имён реализован похожим образом, и при этом языки обеспечивают дополнительный функционал для взаимодействия с пространствами имён.
В Java, например, вы можете импортировать несколько классов в текущее пространство имен, используя оператор импорта. В Java, import
является аналогом use
и он использует точки, чтобы отделить вложенные пространства имен (или пакеты). Вот пример:
import dayle.blog.*;
Здесь произойдет импорт всех классов, находящихся в пакете ‘dayle.blog
.
В PHP у вас так не выйдет. Вы должны импортировать каждый класс в отдельности.
Вот изящный трюк, чтобы немного сгладить озвученную проблему. Представьте себе, что у нас есть структура классов из предыдущего примера. Мы можем взять часть подпространства и дать ему псевдоним.
<?php
namespace Baratheon;
use Dayle\Blog as Cms;
// app/routes.php
$post = new Cms\Content\Post;
$page = new Cms\Content\Page;
$tag = new Cms\Tag;
Это может быть полезным при использовании большого числа классов.
Комментарии