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

Для перемещения по курсу нужно зарегистрироваться

1. Введение ↳ теория

2. Hello, World! ↳ теория / тесты / упражнение

3. Инструкции ↳ теория / тесты / упражнение

4. Арифметические операции ↳ теория / тесты / упражнение

5. Линтер ↳ теория / тесты / упражнение

6. Строки ↳ теория / тесты / упражнение

7. Типы данных ↳ теория / тесты / упражнение

8. Переменные ↳ теория / тесты / упражнение

9. Выражения в определениях ↳ теория / тесты / упражнение

10. Интерполяция ↳ теория / тесты / упражнение

11. Извлечение символов из строки ↳ теория / тесты

12. Функции и их вызов ↳ теория / тесты / упражнение

13. Сигнатура функции ↳ теория / тесты / упражнение

14. Вызов функции — выражение ↳ теория / тесты / упражнение

15. Функции с переменным числом параметров ↳ теория / тесты / упражнение

16. Детерминированность и побочные эффекты ↳ теория / тесты / упражнение

17.

Стандартная библиотека ↳ теория / тесты / упражнение

18. Определение функции ↳ теория / тесты / упражнение

19. Возврат значений из функции ↳ теория / тесты / упражнение

20. Параметры функций ↳ теория / тесты / упражнение

21. Необязательные параметры функций ↳ теория / тесты / упражнение

22. Окружение ↳ теория / тесты / упражнение

23. Именование ↳ теория / тесты / упражнение

24. Логические операции ↳ теория / тесты / упражнение

25. Условные конструкции if и if-else ↳ теория / тесты / упражнение

26. Тернарный оператор и Элвис ↳ теория / тесты / упражнение

27. Конструкция Switch ↳ теория / тесты / упражнение

28. Цикл while ↳ теория / тесты / упражнение

29. Использование циклов ↳ теория / тесты / упражнение

30. Пограничные случаи ↳ теория / тесты / упражнение

31. Цикл for ↳ теория / тесты / упражнение

32. Погружаясь в строки ↳ теория / тесты / упражнение

33. Дата и время ↳ теория / тесты / упражнение

34. Отладка ↳ теория / тесты / упражнение

35. Ошибки ↳ теория / тесты / упражнение

36. Включение файлов ↳ теория / тесты / упражнение

37. Пространство имен ↳ теория / тесты / упражнение

38. Вложенные пространства имен ↳ теория / тесты / упражнение

39. Импорт функций ↳ теория / тесты / упражнение

40. Описание типов ↳ теория / тесты / упражнение

41. Ссылки ↳ теория / тесты / упражнение

42. История PHP ↳ теория

Испытания

1. Сумма двоичных чисел

2. Степень тройки

3. Фибоначчи

4. Добавляем цифры

5. Сбалансированные скобки

6. Совершенные числа

7. Счастливый билет

8. Физзбазз

Порой обучение продвигается с трудом. Сложная теория, непонятные задания… Хочется бросить. Не сдавайтесь, все сложности можно преодолеть. Рассказываем, как

Не понятна формулировка, нашли опечатку?

Выделите текст, нажмите ctrl + enter и опишите проблему, затем отправьте нам. В течение нескольких дней мы улучшим формулировку или исправим опечатку

Что-то не получается в уроке?

Загляните в раздел «Обсуждение»:

  1. Изучите вопросы, которые задавали по уроку другие студенты — возможно, ответ на ваш уже есть
  2. Если вопросы остались, задайте свой. Расскажите, что непонятно или сложно, дайте ссылку на ваше решение. Обратите внимание — команда поддержки не отвечает на вопросы по коду, но поможет разобраться с заданием или выводом тестов
  3. Мы отвечаем на сообщения в течение 2-3 дней. К «Обсуждениям» могут подключаться и другие студенты. Возможно, получится решить вопрос быстрее!

Подробнее о том, как задавать вопросы по уроку

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

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

Для примера рассмотрим следующую ситуацию: у вас есть сайт, на котором есть пользователи и админ. При этом в папке users хранятся классы для юзеров, а в папке admin — классы для админа.

Пусть и для юзеров, и для админа нужен некий класс Page, отвечающий за какие-то страницы сайта.

При этом для юзеров будет свой класс, а для админа — свой. В таком случае нас и поджидает конфликт имен.

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

В PHP существует и другой путь решения проблемы — пространства имен. Суть в следующем: каждый класс может относится к какому-то пространству имен и при этом уникальность имен классов должна соблюдаться только внутри этого пространства.

То есть, для решения нашей проблемы мы можем сделать следующее: отнести один класс Page к какому-нибудь пространству имен, например,

Users, а второй класс Page отнести к другому пространству имен, например, Admin.

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

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

Если класс относится к какому-нибудь пространству имен, то для создания объекта класса нужно будет указать не только имя класса, но и его пространство имен, разделенные обратным слешем. Давайте посмотрим на примере.

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

<?php $page = new Page; ?>

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

<?php $page = new \Admin\Page; ?>

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

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

Для класса

Page из файла /admin/page. php укажем пространство имен Admin:

<?php namespace Admin; class Page { } ?>

А для класса Page из файла /users/page.php укажем пространство имен Users:

<?php namespace Users; class Page { } ?>

Давайте теперь в файле /index.php создадим объект одного и второго класса

Page:

<?php require_once '/admin/page.php'; require_once '/users/page.php'; $adminPage = new \Admin\Page; $usersPage = new \Users\Page; ?>

Пусть у вас есть папка core и папка project. В каждой из папок есть свой класс Controller. Сделайте так, чтобы эти классы принадлежали разным пространствам имен. В файле index.php создайте объекты одного и второго классов.

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

Пусть теперь у нас есть более сложная ситуация: для админа нужно сделать два класса

Page — один с данными страницы, а второй — с представлением этих данных. Пусть первый класс находится в файле /admin/data/page.php, а второй — в файле /admin/view/page.php.

Выше мы уже решили, что все классы из папки admin будут относится к пространству имен Admin. Однако, теперь в этом самом пространстве у нас конфликт двух классов. Для решения проблемы можно сделать дополнительные подпространства имен. Например, можно сделать пространство имен Admin, а в нем подпространства Data и View. В таком случае имена этих подпространств просто записываются через обратный слеш — как при задании пространства имен, так и при создании объекта класса.

Здесь следует уточнить, что уровень вложенности подпространств не ограничен (можно создавать под под пространства в подпространствах и так далее).

Итак, давайте доделаем наш описанный выше пример. Для класса Page из файла /admin/data/page.php укажем пространство имен Admin\Data:

<?php namespace Admin\Data; class Page { } ?>

Для класса Page

из файла /admin/view/page.php укажем пространство имен Admin\View:

<?php namespace Admin\View; class Page { } ?>

Создадим объекты наших классов:

<?php require_once '/admin/data/page.php'; require_once '/admin/view/page.php'; $adminDataPage = new \Admin\Data\Page; $adminViewPage = new \Admin\View\Page; ?>

Пусть у вас есть папка modules/cart. Сделайте так, чтобы все классы из этой папки относились к пространству имен Modules\Cart.

Пусть у вас есть папка modules/shop/cart/. Сделайте так, чтобы все классы из этой папки относились к пространству имен Modules\Shop\Cart.

Некоторые замечания

В примерах выше имена пространств имен совпадают с именами папок, в которых хранятся файлы. Делать так — хорошая практика, но обязательным это не является.

Пусть у вас есть папка modules, а в ней файл marketCart.php и файл shopCart.php. Пусть в обоих файлах размещается класс Cart. Сделайте так, чтобы класс из первого файла принадлежал пространству имен Market\Cart, а из второго файла — пространству Shop\Cart.

Настройка пространств имен PHP в проекте

PhpStorm поставляется с настраиваемым соглашением, которое указывает, что корневая папка проекта также является корневой для пакетов и пространств имен. Другими словами: корневая папка проекта по умолчанию помечена как Source, и каждый каталог, созданный в ней, считается отдельным пространством имен. Это соответствует стандарту PSR-0, также известному как стандарт автозагрузки, который предписывает, чтобы классы и пространства имен в PHP соответствовали структуре каталогов и файлов, и наоборот.

В соответствии с PSR-4 любому каталогу можно явно назначить префикс пространства имен. С такой структурой проекта автозагрузчики в разных PHP-фреймворках становятся совместимыми.

Автоматическое обнаружение корней пространства имен

Когда вы открываете проект, содержащий хотя бы один файл с пространством имен, PhpStorm отображает сообщение с предложением установить корень пространства имен.

Соответственно, когда еще не настроен корень пространства имен и вы создаете класс, PhpStorm предлагает настроить корень пространства имен.

Вы также можете активировать обнаружение корня пространства имен, выбрав Код | Обнаружение корней пространства имен PSR-0 из главного меню. В открывшемся диалоговом окне «Каталоги» отображаются папки в корневой папке проекта, при этом корневая папка проекта помечена как «Источник», что означает, что она является корневой для всех пространств имен в ней. Примите настройки, нажав OK, или настройте корень пространства имен вручную, как описано ниже.

Дополнительные сведения о маркировке папок см. в разделе Настройка папок в корне содержимого.

Настройка корней пространства имен вручную

  1. В диалоговом окне «Настройки» ( Ctrl+Alt+S ) перейдите в «Каталоги».

  2. На правой панели открывшейся страницы Каталоги отображаются все корневые каталоги содержимого, настроенные в проекте. Как правило, есть один корень контента, который является корневой папкой текущего проекта.

    На центральной панели отображаются все папки в выбранном корневом каталоге содержимого. Выберите папку, которая будет рассматриваться как корень пространства имен, и нажмите «Источники».

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

  3. Чтобы настроить префикс пространства имен в соответствии с PSR-4, щелкните рядом с соответствующей исходной папкой (корень пространства имен), и укажите префикс для использования в диалоговом окне «Редактировать корневые свойства». Чтобы префикс применялся и к автоматически сгенерированному коду, установите флажок Для сгенерированных источников. 9

    Краткое введение в пространство имен PHP тот же глобальный уровень, что и другие классы.

    Класс Пользователь , класс Контакт , класс StripeBiller — все они вместе в глобальном пространстве имен.

    Это может показаться простым, но это усложняет организацию, поэтому разработчики PHP начали использовать символы подчеркивания для разделения имен своих классов. Например, если бы я разрабатывал пакет под названием "Cacher" , я мог бы назвать класс Mattstauffer_Cacher , чтобы отличить его от чьего-либо Cacher -- или Mattstauffer_Database_Cacher , чтобы отличить его от кэшера API.

    Это работало прилично, и были даже стандарты автозагрузки, которые отделяли символы подчеркивания в именах классов для папок в файловой системе; например, предполагается, что Mattstauffer_Database_Cacher находится в файле Mattstauffer/Database/Cacher. php 9.0050 .

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

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

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

    Пространства имен подобны структуре виртуальных каталогов для ваших классов. Таким образом, класс Mattstauffer_Database_Cacher может стать классом Cacher в пространстве имен Mattstauffer\Database :

     

    теперь:

     

    И мы будем называть его в другом месте приложения как Mattstauffer\Database\Cacher .

    Реальный пример

    Возьмем Karani — это CRM с финансовой составляющей, так что среди прочего она отслеживает доноров и поступления.

    Давайте установим Karani в качестве пространства имен верхнего уровня (что-то вроде родительской папки, обычно называемой в честь вашего приложения или пакета). Это может иметь некоторые классы, связанные с контактами, и некоторые, связанные с выставлением счетов, поэтому мы собираемся создать подпространство имен для каждого, Karani\Billing и Karani\Contacts .

    Давайте создадим класс или два в каждом:

     
     
     

    Итак, мы представляем себе такую ​​структуру каталогов:

     Карани
        Выставление счетов
            Квитанция
            Подписка
        Контакты
            Донор
     

    Ссылка на другие классы в том же пространстве имен

    Итак, если подписка может отправить квитанцию, на нее легко ссылаться:

     

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

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

    Хорошо, но что, если я хочу сослаться на Receipt внутри Donor?

     

    Вы уже догадались: это не сработает.

    Мы находимся в пространстве имен Karani\Contacts , поэтому, когда мы написали new Receipt , PHP предполагает, что мы говорим о Karani\Contacts\Receipt . Но этого класса не существует, и это не то, что мы ищем.

    Итак, вы получите ошибку Class Karani\Contacts\Receipt not found .

    У вас может возникнуть соблазн изменить его, сказав вместо этого $receipt = new Karani\Billing\Receipt , но даже это не сработает. Так как мы в Karani\Contacts прямо сейчас, он видит что-либо , которое вы пишете, как относящееся к пространству имен, в котором вы находитесь. Таким образом, он попытается загрузить класс с именем Karani\Contacts\Karani\Billing\Receipt , который также явно не существует.

    Используйте блоки и полные имена классов

    Вместо этого у вас есть два варианта:

    Первый , вы можете поставить перед ним косую черту, чтобы создать его FQCN (полное имя класса): $receipt = новый \Karani\Billing\Receipt; , который отправляет PHP сигнал выйти из текущего пространства имен, прежде чем искать этот класс.

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

    Или, Second , вы можете использовать класс в верхней части файла, а затем просто указать его как Receipt :

     

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

    Псевдоним

    Но что, если вы также имеют класс Receipt в вашем текущем пространстве имен ? Что делать, если вашему классу нужен доступ как к Karani\Contacts\Receipt , так и к Karani\Billing\Receipt ?

    Вы не можете просто импортировать класс Karani\Billing\Receipt , иначе вы не сможете использовать оба - они оба будут иметь одно и то же имя в этом классе.

    Вместо этого вам потребуется псевдоним. Вы можете изменить оператор use на что-то вроде использовать Karani\Billing\Receipt как BillingReceipt; . Теперь вы присвоили классу псевдоним, а затем можете ссылаться на импортированный класс как BillingReceipt во всем классе.

    PSR-0/PSR-4 Автозагрузка

    Вы знаете аналогию с папкой, которую я только что использовал выше?

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

    К счастью, PSR-0 (теперь устаревший) и PSR-4 являются стандартами автозагрузки, которые фактически отображают ваши пространства имен в реальные папки. Таким образом, если вы используете PSR-0 или PSR-4 (что весьма вероятно, если вы используете Composer или любой другой современный фреймворк) и совместимый автозагрузчик, вы можете предположить, что классы на самом деле являются в папках.

    Composer и PSR-4 Автозагрузка

    Итак, допустим, я хочу Пространство имен Karani для размещения в моей папке src .

    Вот моя структура папок для общего, независимого от фреймворка проекта:

     app
    публичный
    источник
        Выставление счетов
        Контакты
    продавец
     

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