Pull to refresh

Пространства имен в PHP, разъяснение

Reading time 5 min
Views 122K
Original author: Dayle Rees
Прим.пер.: Я в курсе, что на момент перевода актуальная версия PHP — 5.5, а также что есть мудрая книга мануал. Но мне показалось интересным, то как автор преподносит namespace функционал, возможно кому-то из тех, кто только постигает азы (да и не только, есть немало бородатых разработчиков, остановившихся на PHP 5.2), статья поможет проникнуться. Ходор.

В PHP, начиная с версии 5.3 появились пространства имен. Большинство современных языков уже давно заимели себе такой функционал, но PHP немного опаздывал. Тем не менее, у каждой новой функции есть свое предназначение, давайте выясним, какие выгоды мы можем извлечь, используя namespace.

В PHP у вас не может быть два класса, названных одинаково, все они должны быть уникальны. Проблема этого ограничения в том, что если вы используете чью-либо стороннюю библиотеку, предоставляющую класс с именем User, то вы не можете создать свой собственный класс, также названный User. Это по настоящему скверно, ведь User — очень удобно имя для класса, не так ли?

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

Давайте взглянем на самый обычный класс. Да… я знаю, что вы уже использовали их, при чем здесь пространства имен? Просто доверьтесь мне в этом, ок?

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


Вот такой, очень простой класс:
<?php

// app/models/Eddard.php

class Eddard
{

}

Ничего особенно, как видите, и если вы хотите использовать его, просто сделайте так:
<?php

// app/routes.php

$eddard = new Eddard();

Дейл, я как бы, знаю PHP...

Хорошо, хорошо, извини. Суть в том, что мы можем думать об этом классе, как находящимся в глобальном пространстве имён.Я не совсем уверен, что это правильный термин, но на мой взгляд это звучит вполне уместно. Собственно, это значит то, что класс не принадлежит никакому пакету, просто обычный класс.

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


Давайте создадим еще одного Эддарда, рядом с тем, глобальным.
<?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, но я прошу вас — будьте вежливы. Они сделали много интересного в последнее время.

Вот изящный трюк, чтобы немного сгладить озвученную проблему. Представьте себе, что у нас есть структура классов из предыдущего примера. Мы можем взять часть подпространства и дать ему псевдоним.
<?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;

Это может быть полезным при использовании большого числа классов. Добра всем!



Все пожелания и предложения с радостью приму в личку, спасибо.
Tags:
Hubs:
+33
Comments 62
Comments Comments 62

Articles