Статьи

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

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


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

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

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

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

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

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

Обратите внимание, что не обязательно использовать пространство имен.

Ваш PHP-скрипт будет прекрасно работать без них, и это поведение не скоро изменится.


Определение пространства имен — это первое утверждение, которое интерпретатор PHP должен встретить в файле PHP. Единственный оператор, которому разрешено находиться над объявлением пространства имен, — это оператор declare , и опять же, только если он объявляет кодировку сценария.

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

1
2
3
4
5
6
7
8
<?php
namespace MyProject {
    // Regular PHP code goes here, anything goes!
    function run()
    {
        echo ‘Running from a namespace!’;
    }
}

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

1
2
3
4
<?php
namespace {
    // Global space!
}

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

1
2
3
4
5
6
7
8
9
<?php
namespace MyProject {
}
 
namespace MySecondProject {
}
 
namespace {
}

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

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

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

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

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

PHP использует обратную косую черту в качестве разделителя пространства имен.


Интересный факт: в RFC, чтобы решить, какой разделитель пространства имен следует использовать, они даже решили использовать смайлик.
1
2
3
4
5
6
7
// myproject/database/connection.php
<?php
namespace MyProject\Database
 
class Connection {
    // Handling database connections
}

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

1
2
3
4
5
6
<?php
namespace MyProject\Blog\Auth\Handler\Social;
 
class Twitter {
    // Handles Twitter authentification
}

Определение подпространств имен с вложенными блоками кода не поддерживается. Следующий пример выдаст очень описательную фатальную ошибку: «Объявления пространства имен не могут быть вложенными».

1
2
3
4
5
6
<?php
namespace MyProject {
    namespace Database {
        class Connection { }
    }
}

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

  • Неквалифицированное имя
  • Квалифицированное имя
  • Полностью квалифицированное имя

Это имя класса, функции или константы без ссылки на какое-либо пространство имен. Если вы новичок в пространстве имен, это точка зрения, с которой вы привыкли работать.

01
02
03
04
05
06
07
08
09
10
11
12
<?php
namespace MyProject;
 
class MyClass {
    static function static_method()
    {
        echo ‘Hello, world!’;
    }
}
 
// Unqualified name, resolves to the namespace you are currently in (MyProject\MyClass)
MyClass:static_method();

Вот как мы получаем доступ к иерархии подпространств имен; он использует нотацию с обратной косой чертой.

1
2
3
4
5
6
7
<?php
namespace MyProject;
 
require ‘myproject/database/connection.php’;
 
// Qualified name, instantiating a class from a sub-namespace of MyProject
$connection = new Database\Connection();

В приведенном ниже примере выдается неустранимая ошибка: «Неустранимая ошибка: класс« MyProject \ Database \ MyProject \ FileAccess \ Input »не найден», поскольку MyProject\FileAccess\Input приближен относительно пространства имен, в котором вы находитесь в данный момент.

1
2
3
4
5
6
7
<?php
namespace MyProject\Database;
 
require ‘myproject/fileaccess/input.php’;
 
// Trying to access the MyProject\FileAccess\Input class
$input = new MyProject\FileAccess\Input();

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

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

1
2
3
4
5
6
7
8
<?php
namespace MyProject\Database;
 
require ‘myproject/fileaccess/input.php’;
 
// Trying to access the MyProject\FileAccess\Input class
// This time it will work because we use the fully qualified name, note the leading backslash
$input = new \MyProject\FileAccess\Input();

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

Имея это в виду, теперь мы можем перегрузить внутренние функции PHP, в то же время сохраняя возможность вызова исходной функции (или константы в этом отношении).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<?php
namespace MyProject;
 
var_dump($query);
\var_dump($query);
 
// We want to access the global Exception class
// The following will not work because there’s no class called Exception in the MyProject\Database namespace and unqualified class names do not have a fallback to global space
// throw new Exception(‘Query failed!’);
 
// Instead, we use a single backslash to indicate we want to resolve from global space
throw new \Exception(‘ailed!’);
 
function var_dump() {
    echo ‘Overloaded global var_dump()!<br />’;
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?php
namespace OtherProject;
 
$project_name = ‘MyProject’;
$package_name = ‘Database’;
$class_name = ‘Connection’;
 
// Include a variable file
require strtolower($project_name . ‘/’. $package_name . ‘/’ . $class_name) .
 
// Name of a variable class in a variable namespace.
$fully_qualified_name = $project_name .
 
$connection = new $fully_qualified_name();

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

01
02
03
04
05
06
07
08
09
10
11
12
<?php
namespace MyProject;
 
function run()
{
    echo ‘Running from a namespace!’;
}
 
// Resolves to MyProject\run
run();
// Explicitly resolves to MyProject\run
namespace\run();

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

1
2
3
4
5
<?php
namespace MyProject\Database;
 
// ‘MyProject\Database’
echo __NAMESPACE__;

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


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

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

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

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<?php
namespace OtherProject;
 
// This holds the MyProject\Database namespace with a Connection class in it
require ‘myproject/database/connection.php’;
 
// If we want to access the database connection of MyProject, we need to use its fully qualified name as we’re in a different name space
$connection = new \MyProject\Database\Connection();
 
// Import the Connection class (it works exactly the same with interfaces)
use MyProject\Database\Connection;
 
// Now this works too!
$connection = new Connection();
 
// Import the MyProject\Database namespace
use MyProject\Database;
 
$connection = new Database\Connection()

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

01
02
03
04
05
06
07
08
09
10
11
12
<?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 . При импорте вам больше не нужно писать полностью определенное имя.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?php
namespace MyProject;
 
// Fatal error: Class ‘SomeProject\Exception’ not found
throw new Exception(‘An exception!’);
 
// OK!
throw new \Exception(‘An exception!’);
 
// Import global Exception.
use Exception;
 
// OK!
throw new Exception(‘An exception!’);

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

01
02
03
04
05
06
07
08
09
10
<?php
namespace OtherProject;
 
$parser = ‘markdown’;
 
// This is valid PHP
require ‘myproject/blog/parser/’ .
 
// This is not
use MyProject\Blog\Parser\$parser;

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