Пространства имён в PHP

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

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

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

Рассмотрим стандартный класс.

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

Вот пример простого класса:

<?php
// app/models/user.php
class User
{
}

Ничего особенного. Вот пример его использования:

<?php
// app/user.php
$user = new User();

Итак, этот класс доступен в «глобальном» пространстве имен. Может я немного путаю термины, но идея, надеюсь, понятна. Таим образом, этот класс существует вне пространства имен, то есть, опять-таки, обычный класс.

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

Создадим еще один класс по соседству с первым:

<?php
namespace Model;
// app/model/user.php
class User
{
}

Мы видим, класс с тем же названием, но есть одно отличие — мы добавили директиву namespace. Строка namespace Model указывает на то, что весь код этого файла относится к пространству имен Model. То есть все классы, описанные в этом файле, будут принадлежать этому пространству.

Теперь, снова используем класс User.

<?php
$user = new User();

Мы получим экземпляр первого класса. А вот таким образом можно создать объект класса из нужного нам пространства имен:

<?php
$user = new Model\User();

То есть достаточно указать название пространства в виде приставки к классу через разделитель \. Теперь мы получили экземпляр класса User из пространства имен Model. Довольно просто, не так ли?

Отмечу, что глубина вложенности пространств практически неограниченна. Например:

<?php
This\Namespace\And\Class\Combination\Is\Silly\But\Works

Теория относительности

PHP всегда работает относительно текущего пространства имен. Рассмотрим пример:

<?php
namespace Model;
$user = new User();

Добавив директиву пространства имен, мы автоматически отнесли исполнение скрипта к этому пространству. Теперь, при создании объекта типа User, мы получаем экземпляр класса из пространства имен Model. В этом и заключается относительность.

Но таким образом мы получили побочный эффект, не догадываетесь о чем я? А как же мы создадим первый класс User, тот что без указания пространства?

Существует маневр для использования классов принадлежащих к глобальному пространству. Надо просто добавить обратный слеш \ к имени класса.

<?php
$user = new \User();

По этому символу PHP определяет в каком пространстве имен искать указанный класс. Продолжаем рассматривать различные варианты. Представьте, что у нас есть еще один класс Auth\Member. Как нам использовать его, находясь в пространстве Model?

<?php
namespace Model;
$member = new \Auth\Member();

Все просто, на помощь приходит тот же самый обратный слеш \. Довольно утомительно было бы ставить такой символ перед каждым объявлением класса. Разработчики PHP позаботились об этом и предоставили нам такую директиву, как use. Вот пример:

<?php
namespace Model;
use Auth\Member;
$member = new Member();

При помощи этой директивы мы перемещаем класс из одного пространства в другое, а именно — в наше. Теперь мы можем создавать объекты без каких-либо приставок. Покажу вам еще один трюк. Мы можем задавать добавленным классам свои имена. Рассмотрите пример:

<?php
namespace Model;
use Auth\Member as AuthMember;
$member = new AuthMember();

При помощи директивы as мы задали классу Auth\Member имя AuthMember и теперь можем к нему обращаться по этому имени. Довольно удобно, не так ли? Такой же способ можно применить при использовании одноименных классов, например:

<?php
namespace Model;
use Auth\User as AuthUser;
class User
{
}
// Auth\User
$authUser = new AuthUser();
// Model\User
$modelUser = new User();

Таким образом, в нашем скрипте присутствуют два одноименных класса, но мы их разделили при помощи директивы as. То есть мы избежали конфликта имен.

При помощи use вы можете подключить сколько угодно классов.

<?php
namespace Model;
use Auth\User;
use Blog\Post;
use Blog\Tag;
use Api\User as ApiUser;

Структура

Пространства имен помогают нам не только избежать конфликтов, но и структурировать наш проект. Приведу пример: допустим, я создаю библиотеку и при этом не хочу причинить вреда уже написанному коду пользователя.

Так можно обойти все связанные с этим проблемы:

MyNamespace\Blog\Content\Post
MyNamespace\Blog\Content\Page
MyNamespace\Blog\Tag

Мы использовали своё имя для пространства имён, чтобы разъединить код библиотеки и код пользователя, который будет её использовать. Внутри корневого пространства имен своей библиотеки я использую другие подпространства для логического деления функционала.

Ограничения

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

Например, в Java вы можете одной строкой подключить несколько классов, входящих в одно пространство имен. В этом языке import — аналог use, а точка — разделитель. Вот пример как это сделать в Java:

import mynamespace.blog.*;

Именно так мы подключаем все классы, принадлежащие пространству имен mynamespace.blog.

В PHP такой возможности нет. Каждый класс приходится подключать отдельно. Просьба жалобы отправлять не в мой адрес, а в адрес разработчиков PHP. Только аккуратно, все-таки они постоянно добавляют отличные возможности в свой язык. Но и тут я покажу вам небольшую лазейку. Допустим, структура вашего проекта выглядит следующим образом:

MyNamespace\Blog\Content\Post
MyNamespace\Blog\Content\Page
MyNamespace\Blog\Tag

Дадим новое имя подпространствам и будем обращаться к ним так:

<?php
namespace App;
use MyNamespace\Blog as Cms;
$post = new Cms\Content\Post;
$page = new Cms\Content\Page;
$tag  = new Cms\Tag;

Думаю, это упростит вам работу при использовании большого количества классов из одного пространства имен. Спасибо за внимание!