В этом уроке вы узнаете, что делать при совпадении имен в проекте и как работают пространства имен.
Если мы попытаемся определить две разные функции с одинаковыми именами, то PHP выдаст ошибку во время запуска кода:
<?php
// file: index.php
function foo()
{
print_r('one');
}
function foo()
{
print_r('two');
}
php index.php
# Невозможно переопределить существующую функцию
# Ошибка падает на втором определении
PHP Fatal error: Cannot redeclare foo() (previously declared in /private/var/tmp/index.php:3)
А если разнести определения этих функций по разным файлам и включить один в другой? Чуда не произойдет — как только интерпретатор встретит новое определение с уже известным именем, он завершит работу с ошибкой Cannot redeclare — «невозможно переопределить существующую функцию».
Давайте порассуждаем над тем, в чем здесь проблема.
Реальные приложения на PHP состоят из многих тысяч строк кода. Значительная часть этого кода приходит из библиотек, написанных другими людьми. Почти наверняка, мы не знаем, как они там внутри устроены.
Представьте ситуацию: вы используете много библиотек, над проектом работает множество разработчиков, растет объем кода в проекте. Чем больше становится проект, тем выше вероятность столкнуться с повторяющимися названиями.
Усложним ситуацию. Представьте, что вы используете две разные библиотеки с одинаковыми именами функций. Таким образом, вы физически не сможете использовать вместе эти две библиотеки.
Еще включение каждой новой библиотеки в код проекта почти наверняка заставит переименовывать функции самого проекта из-за конфликта имен. Когда-то эта проблема была настолько серьезной, что функции в PHP именовались с префиксами, например:
mylibnameSendEmail()
mylibnameAddNewUser()
Таким образом разработчики пытались избежать коллизий — пересечений по именам.
Начиная с версии 5.3, в PHP появился механизм пространств имен. Их задача — изоляция кода разных файлов друг от друга.
Это новый для нас механизм, но сама концепция встречается в нашей жизни повсеместно, например:
- В файловой системе роль пространств имен выполняют директории
- В телефонах — код страны
- В адресах — страны, города и улицы
Последний пример демонстрирует вложенные пространства имен. Недостаточно знать только номер дома, чтобы понять, в какой квартире живет человек. Кроме этого, надо знать улицу, город и страну.
Вложенные пространства имен создают подобную вложенную структуру:
В подавляющем большинстве языков есть встроенные механизмы, аналогичные пространствам имен. Причем в этих языках они существовали с самого начала, а не были добавлены потом. Часто они имеют другие имена — например, модули или пакеты. В каждом языке подобный механизм работает чуть-чуть по-своему, но все они предназначены для разрешения коллизий при именовании.
К сожалению, в силу исторических причин, пространства имен в PHP появились не с самого начала существования языка, как это принято при разработке языков. Из-за этого получилось, два независимых механизма борьбы с коллизиями:
- Включение файлов как таковых
- Пространства имен
Пора переходить к примерам:
<?php
// math.php
namespace math;
function sum($a, $b)
{
return $a + $b;
}
Пространство имен задается с помощью ключевого слова namespace
, за которым следует имя пространства имен. Теперь посмотрим, как использовать функции, определенные в пространстве имен:
<?php
// index.php
require_once 'math.php';
// Обратиться напрямую к функции sum не получится:
// PHP Fatal error: Uncaught Error: Call to undefined function sum()
sum(3, 2);
// Если указать пространство имен, то все работает
\math\sum(5, 8); // 13
Попытка обратиться к функции по имени приведет к ошибке, так как функция скрыта за пространством имен. Правильный вызов выглядит так:
\<имя пространства имен>\<имя функции>
Рассмотрим еще один пример, когда функция с одним и тем же именем определена в разных пространствах имен:
<?php
// index.php
require_once 'text.php';
require_once 'number.php';
// Эта функция повторяет строчку, переданную первым параметром столько раз, сколько указано во втором параметре
\text\multiply('hi', 3); // hihihi
\number\multiply(3, 2); // 6
Два пространства имен имеют одинаковые функции, но это не создает неудобств. Каждая функция вызывается с указанием собственного пространства имен.
Более того, мы можем определить функцию multiply()
прямо в том месте, куда производится включение других пространств имен:
<?php
// main.php
require_once 'text.php';
require_once 'number.php';
namespace main;
function multiply($a, $b)
{
print_r('it works!');
}
function all()
{
\text\multiply('ho', 2); // hoho
\number\multiply(3, 2); // 6
// Такой вызов неявно подставляет текущее пространство \main\
multiply(2, 3); // it works!
}
Обратите внимание на последний вызов — функции внутри одного пространства имен могут вызывать друг друга напрямую.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты