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

На хабре можно найти немало статей о пространствах имен в PHP, но мне помогла именно эта, и именно ей я хочу поделится. Наверняка найдутся те, кому этот перевод поможет.

Путь к поддержке пространств имен в PHP был тернистым. Но к счастью она была добавлена к языку в версии PHP 5.3, и структура PHP кода значительно улучшилась с тех пор. Но как именно нам их использовать?

Что такое пространства имен?


«Не забывайте обратный слеш, когда Вы храните имя пространства имен в виде строки!»

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

Ранее разработчикам приходилось использовать префиксы с нижним подчеркиванием в своих классах, функциях и константах для разделения кода. Это эквивалентно тому, что каждый будет маркировать свои вещи и храниться они будут в одном большом ящике. Конечно, это по крайней мере хоть какая-то организации, но это очень неэффективно.

Пространства имен, помогут! Вы можете объявить одни и те же функцию, класс, интерфейс и определить константу в отдельных пространствах имен, не получая фатальных ошибок. По своей сути, пространство имен не более чем иерархически маркированные блоки кода содержащие обычный PHP код.

Вы используете их!


Важно понимать, что Вы косвенно используете пространства имен; Начиная с PHP 5.3, все определения, которые еще не объявлены в определенных пользователем пространствах имен, подпадают под глобальное пространство имен.

Глобальное пространство имен также хранит все внутренние определения PHP, такие как mysqli_connect(), и класс Exception. Поскольку глобальное пространство имен не имеет уникального идентифицирующего названия, его наиболее часто называют глобальное пространство.
Обратите внимание что использование пространства имен не является обязательным.
Ваш PHP скрипт будет прекрасно работать без них, и такое поведение не очень скоро изменится.

Определение пространства имен


Файл, содержащий пространство имен, должен содержать его объявление в начале перед любым другим кодом. Единственное что может объявляться перед пространством имен это зарезервированное слово declare, выражение declare может находиться перед объявлением пространства имен для указания кодировки файла.

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

<?php
namespace MyProject {
    // Обычный PHP код пишется тут, что-то происходит
    function run()
    {
        echo 'Running from a namespace!';
    }
}

Если Вы хотите определить блок кода в глобальное пространство, можно использовать ключевое слово namespace, не добавляя имени.

<?php
namespace {
    // Глобальное пространство!
}

Вы можете использовать несколько пространств имен в одном файле.

<?php
namespace MyProject {
}
 
namespace MySecondProject {
}
 
namespace {
}

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

Обратите внимание, что фигурные скобки, совершенно необязательны. На самом деле, используя правило одного пространства имен в одном файле и опуская фигурные скобки Вы делаете ваш код намного чище — нет никакой необходимости, делать отступ(табуляцию) для вложенного кода.

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


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

Для обеспечения гибкости, разумно хранить вложенные пространства имен в подкаталогах. Это способствует структурированию проекта и позволяет гораздо проще использовать его автозагрузчикам, которые следуют стандарту PSR-4.

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

image

Интересный факт: в RFC, чтобы решить, какой разделитель пространства имен следует использовать, рассматривался вариант использования смайлика.

// myproject/database/connection.php
<?php 
namespace MyProject\Database
 
class Connection {
    // Обработчик подключения к базе данных
}

Вы можете использовать столько вложенных пространств имен, сколько хотите.

<?php 
namespace MyProject\Blog\Auth\Handler\Social;
 
class Twitter {
    // Обработчик аутентификации в Twitter
}

Определение подпространства имен с вложенными блоками кода не поддерживается. Следующий пример будет возвращать фатальную ошибку: «Объявления пространств имен не могут быть вложенными (Namespace declarations cannot be nested)».

<?php
namespace MyProject {
    namespace Database {
        class Connection { }
    }
}

Вызов кода из пространства имен


Если Вы хотите, создать новый экземпляр объекта, вызвать функцию или использовать константы из разных пространств имен, Вы используете обратный слэш. Существует три типа определений имени пространства имен:

  • Неполное имя (Unqualified name)
  • Полное имя (Qualified name)
  • Абсолютное имя (Fully qualified name)



Неполное имя


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

<?php
namespace MyProject;
 
class MyClass {
    static function static_method()
    {
        echo 'Hello, world!';
    }
}
 
// Неполное имя, используется в текущем пространстве имен (MyProject\MyClass)
MyClass:static_method();

Полное имя


Так мы получаем доступ к иерархии подпространства имен; разделяется обратным слэшем.

<?php
namespace MyProject;
 
require 'myproject/database/connection.php';
 
// Полное имя, экземпляр класса из подпространства имен MyProject
$connection = new Database\Connection();

Пример ниже возвратит фатальную ошибку: «Fatal error: Class 'MyProject\Database\MyProject\FileAccess\Input' not found», потому что MyProject\FileAccess\Input не имеет отношения к пространству имен в котором Вы находитесь.

<?php
namespace MyProject\Database;
 
require 'myproject/fileaccess/input.php';
 
// Попытка доступа к MyProject\FileAccess\Input классу
$input = new MyProject\FileAccess\Input();

Абсолютное имя


Полные и неполные имена используются по отношению к пространству имен в котором Вы находитесь в настоящее время. Они могут быть использованы только для определения доступа на этом уровне или нырять глубже в иерархию пространства имен.

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

<?php
namespace MyProject\Database;
 
require 'myproject/fileaccess/input.php';
 
// Попытка доступа к MyProject\FileAccess\Input классу
// На этот раз это сработает, потому что мы используем полное имя, используя впереди обратный слэш
$input = new \MyProject\FileAccess\Input();

Нам не обязательно использовать полное имя для внутренних функций PHP. Неполные имена для функций и констант будут определяться в глобальном пространстве имен, если они не определены в текущем пространстве имен.

Зная это, мы можем теперь перегружать внутренние функции PHP, при этом имея возможность вызвать первоначальную функцию (или константу).

<?php
namespace MyProject;
 
var_dump($query);  // Перегруженный
\var_dump($query); // Встроенный
 
// Мы хотим получить доступ к глобальному Exception классу
// Приведенный ниже код не будет работать, так как класса Exception нет в пространстве имен MyProject\Database и неполное имя класса не имеет доступа к глобальному пространству
// throw new Exception('Query failed!');
 
// Вместо этого, мы используем обратный слеш, чтобы указать, что мы хотим работать с глобальным пространством
throw new \Exception('ailed!');
 
function var_dump() {
    echo 'Overloaded global var_dump()!<br />';
}

Динамические вызовы


PHP — динамический язык программирования; так что Вы можете применять этот функционал для вызова кода из пространства имён. Это, по существу тоже, что использование динамического имени класса или подключение динамического файла используя переменную для хранения его имени. Разделитель имен PHP использует те же метасимволы в строках. Не забывайте про обратный слеш, когда Вы храните имя пространства имен в виде строки!

<?php
namespace OtherProject;
 
$project_name = 'MyProject';
$package_name = 'Database';
$class_name = 'Connection';
 
// Подключение динамического файла
require strtolower($project_name . '/'. $package_name .  '/' . $class_name) . '.php';
 
// Динамическое имя класса в динамическом пространстве имен. Обратите внимание как следует использовать обратные слэши
$fully_qualified_name = $project_name . '\\' . $package_name . '\\' . $class_name;
 
$connection = new $fully_qualified_name();

Ключевое слово namespace


Ключевое слово namespace используется не только для определения пространства имен, оно также может быть использован для вызова в текущем пространстве имен, функционально аналогичный ключевому слову self для классов.

<?php
namespace MyProject;
 
function run() 
{
    echo 'Running from a namespace!';
}
 
// Resolves to MyProject\run
run();
// Explicitly resolves to MyProject\run
namespace\run();

Константа __NAMESPACE__


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

<?php
namespace MyProject\Database;
 
// 'MyProject\Database'
echo __NAMESPACE__;

Эта константа является весьма полезной в начале изучения пространства имен; также она очень полезна при отладке. Так как это строка, она может быть использована в сочетании с динамическими вызовами кода, рассмотренные выше.

Импорт или создание псевдонима имени


не обязательно использовать в пространствах имен

Важная особенность пространств имен в PHP — это возможность ссылаться на внешнее абсолютное имя по псевдониму, или импортирование.

Импорт является очень полезным и фундаментальным аспектом пространства имен. Это дает вам возможность использовать внешние пакеты, например, библиотеки, не беспокоясь о конфликте имен. Импорт осуществляется с помощью ключевого слова use. При желании, Вы можете указать пользовательский псевдоним с помощью ключевого слова as.

use [name of class, interface or namespace] as [optional_custom_alias]

Как это использовать


Абсолютное имя может быть привязаны к более короткому неполному имени, так что вам не придется писать его абсолютное имя каждый раз, когда Вы хотите его использовать. Создание псевдонима или импорт должны происходить в родительском пространстве имен или в глобальном. Попытка сделать это в рамках метода или функции является недопустимым синтаксисом (invalid syntax).

<?php
namespace OtherProject;
 
// This holds the MyProject\Database namespace with a Connection class in it
require 'myproject/database/connection.php';
 
// Если мы хотим подключится к базе данных из MyProject, мы должны использовать его абсолютное имя, так как мы находимся в другом пространстве имен
$connection = new \MyProject\Database\Connection();
 
// Импорт класса Connection (это так же применимо к интерфейсам)
use MyProject\Database\Connection;
 
// Теперь это сработает! Если бы перед классом Connection не было псевдонима, PHP не нашел класс OtherProject\Connection
$connection = new Connection();
 
// Импорт пространства имен MyProject\Database
use MyProject\Database;
 
$connection = new Database\Connection()

Альтернативой, является возможность присвоения псевдонима с другим именем

<?php
namespace OtherProject;
 
require 'myproject/database/connection.php';
 
use MyProject\Database\Connection as MyConnection;
 
$connection = new MyConnection();
 
use MyProject\Database as MyDatabase;
 
$connection = new MyDatabase\Connection();

Вы также можете импортировать такие глобальные классы, как Exception. При импорте, вам не придется писать его абсолютное имя.

Обратите внимание, что для имен в пространстве имен нет необходимости в начальном обратном слеше и его присутствие там не рекомендуется, так как импортируемые имена должны быть абсолютными и не обрабатываются относительно текущего пространства имен.

<?php
namespace MyProject;
 
// Fatal error: Class 'SomeProject\Exception' not found
throw new Exception('An exception!');
 
// OK!
throw new \Exception('An exception!');
 
// Импорт глобального Exception.
use Exception;
 
// OK!
throw new Exception('An exception!');

Хотя есть поддержка динамического вызова пространства имен, динамический импорт не поддерживается.

<?php
namespace OtherProject;
 
$parser = 'markdown';
 
// Это валидно
require 'myproject/blog/parser/' . $parser . '.php';
 
// Это не валидно
use MyProject\Blog\Parser\$parser;

Вывод


Пространства имен используются, чтобы избежать конфликта определений и ввести больше гибкости и организации в программный код. Помните, что Вы не обязаны использовать пространства имен; это функция используется в сочетании с объектно-ориентированным подходом. Надеюсь, Вы рассмотрите возможность поднятия Вашего (будущего) PHP проекта на новый уровень за счет использования пространства имен.
  • +22
  • 36k
  • 9
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 9

    +9
    Глобальное пространство имен также хранит все внутренние определения PHP, такие как echo()
    echo — конструкция, с чего бы ей «храниться» в глобальном пространстве?
      +1
      Неуглядел, поправил.
      +12
      статья конечно хорошая, но сдается мне лет так на 5 опоздала(5.3.0 вышло в июне 2009). Все то же самое можно прочитать в доках php.net/manual/ru/language.namespaces.php
        +3
        Категорически поддерживаю запоздалость статьи, но особо ничего не изменилось, а порог вхождения на php.net помоему не самый низкий. И эта статья показалась мне вполне доходчиво описывающей тот минимум понимания, с которым можно дальше работать.
          0
          Типа не все понимают что такое «инкапсуляция»?
        0
        «Конечно, это по крайней мере хоть какая-то организации, но это очень неэффективно.»
        В видении новичков, здесь лишь заменили _ на \ и увеличили использование оперативной памяти для работы с пространством имен.
        Прошу пояснить, почему это неэффективно, да к тому же еще и очень? Статья этого не раскрывает.
          0
          Эффективность применения пространства имен здесь не рассматривается со стороны потребления ресурсов (и вряд ли стоит делать это в контексте перевода, ведь это перевод).
          Речь идет об организации (как видно из цитаты) и структуре проекта, в котором будут применяться пространства имен.
          При разработке даже небольшого проекта я использую Composer, который рекомендует придерживаться стандарта PSR4 (о чем кстати сказано в тексте).
          Говоря о проектах которые построены на современных фреймворках, вряд ли можно говорить о применении префиксов.
          А если взять в расчет то, что у Вас может быть более 10 подключенных сторонних библиотек, и Вы будете пытаться связать все это руками, да еще и маркируя каждую своим префиксом (делая названия состоящие из 5-8 слов), а плюс вас еще 4 человека на проекте — вряд ли можно говорить об эффективности.
          Если говорить о том что классы, расширяющие другие классы или реализующие интерфейсы, должны объявлять свои зависимости на той же строке, а рекомендуемая длина строки в PHP составляет 80 символов, а максимальная длина любой строки PHP-кода не должна превышать 120 символов, то наследование от одного класса и реализация пары интерфейсов заставит Вас нарушить эти правила.
          Если у Вас не стоит задача сделать Ваш код читабельным, и Вы не будете в будущем его кому-то показывать, а уж тем более где-нибудь публиковать, Вы вправе выбирать между потреблением ресурсов и «феншуем» кода который Вы пишете.
            0
            Прочитав
            Если у Вас не стоит задача сделать Ваш код читабельным, и Вы не будете в будущем его кому-то показывать, а уж тем более где-нибудь публиковать, Вы вправе выбирать между потреблением ресурсов и «феншуем» кода который Вы пишете

            в голове сразу мелькнула мысль о инструменте, кторый пережует легко читаемый код с пространствами имен в оптимизированный с точки зрения потребления ресурсов, на подобие SCSS или LESS.
            0
            На самом деле, основное преимущество пространств имен, на мой взгляд, это их изолированность. Т.е. если вы используете третье-стороннюю библиотеку в которой есть класс с именем, которое уже есть и у вас — без пространств имен будут конфликты, и прийдется один из них переименовывать. А в случае с пространствами имен — они прекрасно уживутся друг с другом.

          Only users with full accounts can post comments. Log in, please.