Как использовать пространства имен в PHP, Часть 1: Основы

http://www.sitepoint.com/blogs/2009/07/13/php-53-namespaces-basics/
  • Перевод
image

Предисловие: На Хабре уже публиковались несколько статей, посвященных пространству имен в PHP (все ссылки на них я привожу в приложении). Однако, этот интересный и полезный вопрос был раскрыт не полностью. Поэтому я привожу перевод первой из трех статей по данной теме (остальные переведу в ближайшее время). P.S. Статья для начинающих

Пространства имен (namespaces) — это одно из самых значительных изменений в PHP 5.3. Они будут хорошо знакомы С# и Java разработчикам, и, вероятно, они изменят к лучшему структуру PHP-приложений.

Почему мы нуждаемся в пространстве имен?


Поскольку размер библиотеки Вашего PHP кода растет, возрастает и риск случайного переопределения функции или имени класса, которые были объявлены ранее. Проблема усугубляется, когда Вы пытаетесь добавлять сторонние компоненты или плагины; что будет, если два или более наборов кода будут выполнять классы «Database» или «User»?

Ранее, единственным решением были длинные имена классов/функций. Например, WordPress добавлял к каждому имени префикс «WP_». Zend Framework обычно дает детально описывающие названия, что приводит к длиннющим именам классов, таким как Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive.

Проблемы совпадения имен снимаются введением пространств имен. PHP константы, классы и функции могут быть сгруппированы в библиотеки пространств имен (namespaced libraries).

Как определяются пространства имен?


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

В коде, пространства имен определяются с помощью единственного слова namespace в самом начале Вашего PHP файла. Это слово должно быть самой первой командой (за исключением declare) и ни не-PHP код, ни HTML, ни даже пробел не должен предшествовать этой команде, например:
<?php
// define this code in the MyProject namespace
namespace MyProject;

Весь код, следующий за этими строками, будет относиться к пространству имен «MyProject». Невозможно вкладывать в него другие пространства имен, или определять более чем одно пространство имен для одной и той же части кода (поскольку распознано будет лишь последнее объявление пространства имен, предшествующие же — игнорируются). Тем не менее, Вы можете определить различные пространства имен в одном и том же файле, например:
<?php
namespace MyProject1;
// PHP code for the MyProject1 namespace

namespace MyProject2;
// PHP code for the MyProject2 namespace

// Alternative syntax
namespace MyProject3 {
	// PHP code for the MyProject3 namespace
}
?>

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

Подпространства имен (Sub-namespaces)

PHP дает Вам возможность определять иерархию пространств имен таким образом, чтобы библиотеки могли быть соподчинены друг другу. Получившиеся подпространства имен разделяются с помощью обратных слэшей \, например:
  • MyProject\SubName
  • MyProject\Database\MySQL
  • CompanyName\MyProject\Library\Common\Widget1

Вызов кода, относящегося к пространству имен


В файле с именем lib1.php мы определим константу, функцию и класс в пределах пространства имен App\Lib1:

lib1.php

<?php
// application library 1
namespace App\Lib1;

const MYCONST = 'App\Lib1\MYCONST';

function MyFunction() {
	return __FUNCTION__;
}

class MyClass {
	static function WhoAmI() {
		return __METHOD__;
	}
}
?>

Теперь мы можем включить этот код в другой PHP файл, например:

myapp.php

<?php
header('Content-type: text/plain');
require_once('lib1.php');

echo \App\Lib1\MYCONST . "\n";
echo \App\Lib1\MyFunction() . "\n";
echo \App\Lib1\MyClass::WhoAmI() . "\n";
?>

Никаких пространств имен не определено в файле myapp.php, поэтому код существует в глобальном пространстве. Любая прямая ссылка на MYCONST, MyFunction или MyClass вызовет сбой, поскольку они существуют только в пространстве имен App\Lib1. Чтобы вызвать код из lib1.php, мы можем добавить префикс \App\Lib1, чтобы определить полное квалифицированное имя. На выходе, когда загрузим myapp.php, мы получим следующий результат:
App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI

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

Читайте также:


Как использовать пространства имён в PHP, Часть 2: Импортирование, псевдонимы и правила разбора имён
Как использовать пространства имён в PHP, Часть 3: Ключевые слова и автозагрузка

Примечания:


A/ Единственная подробная статья по теме на Хабре:

Также имеются упоминания в статьях:

B/ Замечания, поправки, указания на неточности и проч. — приветствуются!
Поделиться публикацией

Комментарии 83

    0
    Хорошее нововведение, но ведь оно не решает проблемы затирания старых библиотек. Используя пространство имен мы практически гарантируем, что наш код не будет затерт, но совершенно не гарантируем, что наш код не затрет чужой код, не использующий пространство имен. Или я что-то не так понял?
      0
      !!! Прошу прощения, ответил ниже…
      +1
      Если я правильно понял Вас, то могу выразится иначе — используя пространство имен, мы гарантируем существование наших библиотек (моего кода и стороннего) в абсолютно разных пространствах, что означает — вообще никто никого затирать не будет.
        +1
        аааа… значит в Вашем примере function MyFunction() можно вызвать только как \App\Lib1\MyFunction() а просто MyFunction() — ничего не даст?
          +2
          Именно!
          Любая прямая ссылка на MYCONST, MyFunction или MyClass вызовет сбой, поскольку они существуют только в пространстве имен App\Lib1

          То есть:
          echo \App\Lib1\MyFunction()

          И не иначе :-)
            +1
            но никто не запрещает обращаться к MyFunction() в lib1.php просто через имя. Т.е. внутри пространства имён всё работает. А снаружи — снаружи обычно довольно редко вызываются объекты (функции, переменные, константы и т.д.) из пространства имён, так как новвоведение специально для библиотек
              +4
              Можно импортировать пространство имен через use, и после этого вызывать как MyFunction.
              • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Не жаль… :-) Вы не обратили внимания, что это не одна статья, там три части. Во второй, которую я опубликую завтра утром, как раз идет речь и о use и о MyFunction.
                    +5
                    Мм, первая статья об операторе namespace, вторая о use. Также можно написать целый цикл статей про функции работы с БД, там ведь непочатый край, первая статья про mysql_connect, вторая про mysql_select_db, третья про mysql_query… уж без обид :)
                      0
                      А что, идея однако! ;-) Надо бы автору написать, еще напишет!
          • НЛО прилетело и опубликовало эту надпись здесь
            +10
            Практическую разницу между «WP_Function()» и «\WP\Function()» углядеть непросто.

            Кое-кому, я уверен, покажется, что её почти и вовсе нéту…
              +1
              Очень верно заметили. Ничего, на ошибках учатся, хотя я даже думал подчеркнуть этот «лишний» слэш :-)))
                0
                Не знаю.
                например: «Пространство имён определяется блоком инструкций.… Внутри этого блока идентификаторы могут вызываться именно так, как они были объявлены.» — это довольно хорошо. Правда этого в статье не написано
                  +2
                  На днях опубликую еще два перевода статей этого же автора — 2-ую и 3-ю часть.
                    +1
                    Это стандартное поведение PHP кода если namespace не используется. В чем же достоинство namespace если он даже умеет работать так, как если бы его не было.
                      0
                      Читай пост с WP_Function
                  • НЛО прилетело и опубликовало эту надпись здесь
                      –1
                      Не стоит прогибаться под изменчивый Хабр. (почти ©)
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Пациент выздоравливает, уже начал цитировать расового еврейского Макаревича:)
                        0
                        Ну тут смысл несколько в другом если сделать ґ
                        use WP
                        То писать надо будет только Function()

                        0
                        Главное теперь, чтобы имена пространств имён ещё не совпадали
                          0
                          Верно! Вот во избежание этого автор и взывает:
                          будьте благоразумны, определяя только одно пространство имен для каждого файла...
                          • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            можно как в яве делать, используя в имени неймспейса написанные наоборот домены — \ru\site\Controllers например :)
                            +1
                            Становиться удобно применять простарнства имён в больших проектах, в скромных это начинает приобретать склонность к преувеличению…
                              0
                              ага, скромнее надо быть =)
                              но обычно маленькие проекты иногда со временем перетекают в крупные
                              +5
                              Мне честно говоря не очень нравится синтаксис этих неймспейсов. В некотором роде префикс был даже лучше, потому как WP_Function() — тут всего один символ разделения, а \WP\Function() — тут два. Кончено, это не сильный показатель, но лучше меньше да лучше. Более того тут прямые ассоциации с файловой системой, что опять же не будет украшать код. Сделали бы они точку типа WP.Function() тогда еще ничего (да я знаю, что точку нельзя использовать).
                                +6
                                все-таки это уродство, использовать обратный слеш для разделения пространства имен.
                                вытащили бы палец из жопы и сделали бы по-человечески ::
                                  +1
                                  :: занято
                                  . занято
                                  -> занято
                                  | занято

                                  какие еще предложения?

                                  # — кстати, всё равно хотят отменять как комментарий :)

                                  Понятно, что это изначальная ошибка при проектировании языка, но рушить всё уже поздно, обратная совместимость в любом случае при такой популярности языка нужна.
                                    0
                                    \ тоже занят — это типа спецсимвол экранирования
                                    \n, \t всякие
                                      0
                                      Ну это только внутри строковых констант, да, приходится имя класса, если уж понадобилось, писать как «SomeSpace\\SomeSubSpace\\SomeClass», только когда это нужно? Только в случае если нужно составить имя класса динамически, причем вместе с пространством имен, не такой уж это частый случай, у меня в личных наработках на php 5.3 такой встречается единожды, для подключения контроллеров модулей, итого 2 лишних слеша на всю систему.
                                        0
                                        конечно, если у тебя нет — то ни у кого уж точно не будет…
                                        0
                                        это внутри "" и '' просто
                                        +1
                                        MyNameSpace:::MyClass::staticMethod();
                                        MyNameSpace:::MYCONST;
                                        MyNameSpace:::myFunc();
                                        MyNameSpace:::SubName:::blabla
                                          +12
                                          MyNameSpace:)myFunc();
                                            +3
                                            Да ладно, хорошо бэкслэш, а не бэкспейс :)
                                          +1
                                          Можно было бы хотя бы прямой слеш. Обратный как-то уж совсем…
                                          Реализовать перегрузку :: так чтобы работало и для неймспейсов тоже не представляется таким уж сложным.
                                            0
                                            "/" — деление :)
                                            "::" занято, и неоднозначности точно бы появились.
                                              0
                                              Ну так чем черт не шутит — подчеркивание не занято. Тогда бы всем стал очевиден смысл и удобство (если есть) затеи :)
                                                0
                                                подчёркивание можно использовать в теле идентификатора, так что тоже занято
                                            0
                                            Использовать хотя бы не обратный слеш, а прямой, т.к. обратный явно ассоциируется с экранированием символов.
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                              +5
                                              Я бы использовал тильду — WP~Function(); или «волнистую стрелку»: WP~>Function();
                                                0
                                                Идея «волнистой стрелки» не плохая, что-то новенькое, правда имхо не подходит именно для пространства имен.
                                                  +1
                                                  тильду писать неудобно
                                                –11
                                                Вот-вот, слава уродского языка останется за ПХП навсегда.
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                    +1
                                                    он не уродский, а живой :)
                                                  +5
                                                  Ну два разделителя тут не случайно, это особенность реализации пространств имен в PHP, попробую привести пример:
                                                  namespace SomeSpace;
                                                  WP\Function();
                                                  

                                                  Вызовет SomeSpace\WP\Function, так как учитывается объявленное в файле пространство имен, чтобы вызвать функцию из другого пространства имен и нужен первый слеш, иже
                                                  namespace SomeSpace;
                                                  \WP\Function();
                                                  

                                                  вызовет уже функцию WP\Function относительно корневого пространства имен. Или-же (я считаю это более правильным) — сделать так:
                                                  namespace SomeSpace;
                                                  use WP;
                                                  WP\Function();
                                                  

                                                  Use импортирует заданное пространство имен в текущее, и соот-но члены этого пространства можно использовать без слеша.
                                                  Ну и вы забываете главное, представим себе более серьезную вложенность, в случае действительно большого проекта — вон из того-же Zend'а, например Zend_Navigation_Page_Mvc, без пространств имен так и придется его выписывать заново каждый раз, с пространствами:
                                                  use Zend\Navigation\Page\Mvc;
                                                  $a = new Mvc($options);
                                                  

                                                  Собственно ради этого пространства имен и существуют :) так что экономия одного символа — имхо не аргумент :)
                                                    –1
                                                    Ваш коммент привлекает желающих выразить свою антипатию к \(слэшу) как мух — г***о. Это обсуждалось тысячи раз на иностранных форумах, и на том-же хабре в каждом топике про пхп 5.3, зачем ещё раз поднимать снова набрасывать на вентилятор?
                                                      0
                                                      Значит, видимо, проблема людей волнует. Я на иностранных форумах не был, и неймспейсами как-то не интересовался (да и сейчас то не очень, нигде на наших серверах нету 5.3). ПОэтому увиденный backslash как разделитель действительно вызывает легкое подташнивание.
                                                        0
                                                        Ничего, привыкните, либо просто не будете использовать. Разработчики не собираются ничего менять.
                                                          0
                                                          Ну можно выпустить свой PHP с блекджеком и все такое :)
                                                            –2
                                                            Ага. И компилить PHP из своих исходников на каждом серваке, на котором будет работать ваша система. Правда сервак должен быть выделенным, так как ничего другого кроме ваших систем на этом серваке работать не будет.
                                                        0
                                                        Я соглашусь с каментом выше — я так же не читаю иностранные форумы. Я лишь сравнил сушествующие префиксы с неймспейсами и сделал вывод о лучшем (имхо) по красоте и читаемости кода использованию последних. Почему у вас мой камент вызвал ассоциации с вентилятором я не знаю. Ну это не имеет значения — так как вы совершенно правы — никто ничего менять не будет. так что придется использовать в том варианте, который есть. Или не использовать.
                                                      +2
                                                      Сколько раз тема поднималась, всё никак не привыкну к "\" разделителю. Не нравится, почему-то :(
                                                      Но нововведение полезное. Осталось дождаться теперь поддержки множественного наследования и ООП в php куда удобнее станет :)
                                                        0
                                                        Мне в голову не приходит где бы вы его применили это множественное наследование в веб-приложении на php, можно примерно описать?
                                                          +2
                                                          Например, изобретал я свой мини-фреймворк велосипед, была система кэширования своя. Потом понадобилось использовать другую систему кэша с другим интерфейсом. Я сделаю адаптер. Вариант с множественным наследованием удобнее, чем вариант с методом-ссылкой, т.к. не нужно инстанцировать лишние объекты.
                                                            +1
                                                            не будет множественного наследования, скорее всего будут traits.
                                                              0
                                                              Любопытная система. Местами напоминает import'ы из питона.
                                                                0
                                                                Ой, это же модули из Рубина!
                                                                  –1
                                                                  скорее mixins
                                                                    0
                                                                    А примеси, по вашему, это что? Это когда модули включают в классы. Вы Рубин давно используете? :-)
                                                                  0
                                                                  А мне больше Grafts понравились, на мой взгляд они как-то более логичны.
                                                            +1
                                                            Тем не менее, Вы можете определить различные пространства имен в одном и том же файле, например:
                                                            Так всё-таки namespace должен быть самой первой командой в файле или можно их указывать в произвольном месте?
                                                              +4
                                                              Вы не зря обратили внимание, но и я не ошибся в переводе. Тут есть такая заковырка. Полагаю, высшей инстанцией в данном вопросе можно считать официальный PHP Manual. К нашей проблеме имеют отношение две страницы: Defining namespaces и Defining multiple namespaces in the same file.

                                                              Если, позволите, обойдемся без цитат — объясню своими словами.

                                                              1. Мы имеем четкое правило, что если мы вообще используем, хотя бы раз, в документе namespace, то мы обязаны поместить его в самое начало кода, даже перед комментариями и пробелом. Единственным позволенным исключением, является только declare.
                                                              1. <?php
                                                              2. namespace MyProject;
                                                              3.  
                                                              4. const CONNECT_OK = 1;
                                                              5. class Connection { /* ... */ }
                                                              6. function connect() { /* ... */ }
                                                              7.  
                                                              8. ?>
                                                              * This source code was highlighted with Source Code Highlighter.

                                                              !!! Даже это уже является ошибкой —
                                                              1. <html>
                                                              2. <?php
                                                              3. namespace MyProject; // fatal error - namespace must be the first statement in the script
                                                              4. ?>
                                                              * This source code was highlighted with Source Code Highlighter.

                                                              2. Если мы хотим применить к одной части нашего кода namespace, а другую оставить не помеченной, мы обязаны разместить помеченную часть в начале нашего кода (таки руководствуясь вышеизложенным пунктом 1.), а вторую специально отметить, пример:
                                                              1. <?php
                                                              2. namespace MyProject {
                                                              3.  
                                                              4. const CONNECT_OK = 1;
                                                              5. class Connection { /* ... */ }
                                                              6. function connect() { /* ... */ }
                                                              7. }
                                                              8.  
                                                              9. namespace { // global code
                                                              10. session_start();
                                                              11. $a = MyProject\connect();
                                                              12. echo MyProject\Connection::start();
                                                              13. }
                                                              14. ?>
                                                              * This source code was highlighted with Source Code Highlighter.

                                                              3. Ну и если мы, хоть это и не рекомендуется, желаем пометить две части нашего кода (или более) разными namespaces, это можно сделать двумя вариантами. Нежелательным и боле-менее приемлимым.

                                                              а) нежелательно, но валидно:
                                                              1. <?php
                                                              2. namespace MyProject;
                                                              3.  
                                                              4. const CONNECT_OK = 1;
                                                              5. class Connection { /* ... */ }
                                                              6. function connect() { /* ... */ }
                                                              7.  
                                                              8. namespace AnotherProject;
                                                              9.  
                                                              10. const CONNECT_OK = 1;
                                                              11. class Connection { /* ... */ }
                                                              12. function connect() { /* ... */ }
                                                              13. ?>
                                                              * This source code was highlighted with Source Code Highlighter.

                                                              а) желательно и валидно:
                                                              1. <?php
                                                              2. namespace MyProject {
                                                              3.  
                                                              4. const CONNECT_OK = 1;
                                                              5. class Connection { /* ... */ }
                                                              6. function connect() { /* ... */ }
                                                              7. }
                                                              8.  
                                                              9. namespace AnotherProject {
                                                              10.  
                                                              11. const CONNECT_OK = 1;
                                                              12. class Connection { /* ... */ }
                                                              13. function connect() { /* ... */ }
                                                              14. }
                                                              15. ?>
                                                              * This source code was highlighted with Source Code Highlighter.

                                                              Итак, namespace обязан быть самой первой командой в файле, но после него можно остальные указывать и в произвольных местах.
                                                                0
                                                                Спасибо за разжёвывание :) Фигурные скобки хорошие.
                                                                Но тогда в ваших примерах стоит убрать комментарии перед объявлениями пространств =)
                                                                  0
                                                                  Поверьте, в Ваших знаниях не сомневался. Решил детально расписать, поскольку уверен, что статью будут читать в том числе и начинающие, и именно разжевывание может им пригодиться.

                                                                  Комментарии не обрабатываются компилятором и никак не влияют на программу, а только служат подсказкой и памяткой… Они имеют другой «вес», чем даже тот же пробел. Это даже не исключение.
                                                                    +1
                                                                    Да нет, правда спасибо! Всё до пространств руки не доходили.

                                                                    Насчёт комментария сказал из-за этого:
                                                                    … то мы обязаны поместить его в самое начало кода, даже перед комментариями и пробелом.
                                                                      0
                                                                      Здорово! Это я ошибся… Ты прав!
                                                              +1
                                                              Может, не стоит переводить «full qualified name»? Или хотя бы не превращать его в «полностью квалифицированное имя»? ;)
                                                              Вполне понятный термин. Как вам «полное имя» в качестве аналога?
                                                                +1
                                                                Согласен с Вами, уже изменил. Да пребудет с Вами положительная Карма!
                                                                  0
                                                                  Оказывается, правильно будет:

                                                                  Fully-qualified name — полное квалифицированное имя

                                                                  Qualified name — квалифицированное имя

                                                                  Unqualified name — неквалифицированное имя
                                                                  +2
                                                                  Насколько следует из документации, ключевое слово const работает только внутри классов.

                                                                  ЗЫ: Я прям уже вижу толпу кодеров, которые вместо singleton'ов, к примеру, везде понаставят пространств имен и глобальных переменных…
                                                                    0
                                                                    Спасибо за статью, но для себя я пока не вижу применения пространства имен.
                                                                      0
                                                                      Значит, если займетесь серверным программированием, у Вас еще все впереди :-)
                                                                      +4
                                                                      вот если бы ещё стандартные функции языка по пространствам разложили.
                                                                        +1
                                                                        Интересно, вот статья 2003 года, где говорится про пространства имён в php5
                                                                        phpclub.ru/detail/article/intro_php5
                                                                        в чем подвох?
                                                                          +2
                                                                          >Примечение: разработчики отказались от поддрежки этой возможности.
                                                                          блин не дочитал
                                                                          +1
                                                                          Не хочу обижать разработчиков на Php, но namespaces в чистом виде — не вполне удачное решение из мира C++. Да, гибко; да, позволяет решить проблему; но утяжеляет и без того несимпатичный синтаксис.

                                                                          приемлемо работает Ява с ее пакетами

                                                                          Хорошо решен вопрос в Питоне, Руби, Хаскеле и других — модулями, по которым распихана стандартная библиотека и вообще весь код.

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

                                                                          Самое читаемое