PHP

индекс
206,63

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

image
В части 1, мы обсуждали для чего нужны пространства имён (namespaces) в PHP, и что обозначает зарезервированное слово namespace. В этой статье мы исследуем оператор use и способ, которым PHP разрешает имена пространств имён.

В целях данной статьи мы будем использовать два почти идентичных кода, единственное различие которых — в их пространствах имён:

lib1.php:

  1. <?php
  2. // application library 1
  3. namespace App\Lib1;
  4.  
  5. const MYCONST = 'App\Lib1\MYCONST';
  6.  
  7. function MyFunction() {
  8.   return __FUNCTION__;
  9. }
  10.  
  11. class MyClass {
  12.   static function WhoAmI() {
  13.     return __METHOD__;
  14.   }
  15. }
  16. ?>

lib2.php:

  1. <?php
  2. // application library 2
  3. namespace App\Lib2;
  4.  
  5. const MYCONST = 'App\Lib2\MYCONST';
  6.  
  7. function MyFunction() {
  8.   return __FUNCTION__;
  9. }
  10.  
  11. class MyClass {
  12.   static function WhoAmI() {
  13.     return __METHOD__;
  14.   }
  15. }
  16. ?>

Прежде чем мы начнём, давайте вспомним несколько определений из PHP терминологии:

Полное квалифицированное имя (Fully-qualified name)

Любой PHP код может ссылаться на полное квалифицированное имя — идентификатор, начинающийся с разделителя пространства имён (т. е. обратного слэша — backslash), например: \App\Lib1\MYCONST, \App\Lib2\MyFunction() и т.д.

В полных квалифицированных именах нет никакой двусмысленности. Начальный обратный слэш действует аналогичным образом как и путь к файлу, обозначая «корень (root)» глобального пространства. Если бы мы выполняли различные MyFunction() в нашем глобальном пространстве, они могли бы быть вызваны из lib1.php или lib2.php с помощью \MyFunction().

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

Квалифицированное имя (Qualified name)

Идентификатор, имеющий как минимум хотя бы один разделитель пространства имён (namespace separator, фактически — обратный слэш), например Lib1\MyFunction().

Неквалифицированное имя (Unqualified name)

Идентификатор без разделителя пространства имён, например MyFunction().

Работа с одинаковыми пространствами имён


Обсудим следующий код:

myapp1.php:

  1. <?php
  2. namespace App\Lib1;
  3.  
  4. require_once('lib1.php');
  5. require_once('lib2.php');
  6.  
  7. header('Content-type: text/plain');
  8. echo MYCONST . "\n";
  9. echo MyFunction() . "\n";
  10. echo MyClass::WhoAmI() . "\n";
  11. ?>

Хотя мы присоединили и lib1.php и lib2.php, идентификаторы MYCONST, MyFunction и MyClass будут относиться только к lib1.php. Это произойдет потому что код myapp1.php расположен в едином с App\Lib1 пространстве имён:

результат:

  1. App\Lib1\MYCONST
  2. App\Lib1\MyFunction
  3. App\Lib1\MyClass::WhoAmI

Импортирование пространств имён (Namespace Importing)


Пространства имён могут быть импортированы с помощью оператора use, например:

myapp2.php:

  1. <?php
  2. use App\Lib2;
  3.  
  4. require_once('lib1.php');
  5. require_once('lib2.php');
  6.  
  7. header('Content-type: text/plain');
  8. echo Lib2\MYCONST . "\n";
  9. echo Lib2\MyFunction() . "\n";
  10. echo Lib2\MyClass::WhoAmI() . "\n";
  11. ?>

Вы можете импортировать с помощью use одно или несколько пространств имён, разделяя их запятой. В данном примере, мы импортировали пространство имён App\Lib2. Мы все еще не можем ссылаться прямо на MYCONST, MyFunction или MyClass потому что наш код находится в глобальном пространстве и PHP будет искать их именно там. Но если мы добавим префикс «Lib2\», они станут квалифицированными именами, а PHP станет искать их в импортированных пространствах имён, пока не найдет полного совпадения.

результат:

  1. App\Lib2\MYCONST
  2. App\Lib2\MyFunction
  3. App\Lib2\MyClass::WhoAmI

Псевдонимы пространства имён (Namespace Aliases)


Псевдонимы пространства имён, возможно, самая полезная конструкция. Псевдонимы позволяют ссылаться на длинные пространства имён с помощью короткого имени.

myapp3.php:

  1. <?php
  2. use App\Lib1 as L;
  3. use App\Lib2\MyClass as Obj;
  4.  
  5. header('Content-type: text/plain');
  6. require_once('lib1.php');
  7. require_once('lib2.php');
  8.  
  9. echo L\MYCONST . "\n";
  10. echo L\MyFunction() . "\n";
  11. echo L\MyClass::WhoAmI() . "\n";
  12. echo Obj::WhoAmI() . "\n";
  13. ?>

Первый оператор use определяет App\Lib1 как «L». Любое квалифицированное имя, использующее «L», будет преобразовано во время компиляции в «App\Lib1». Поэтому, мы скорее сошлемся на L\MYCONST или L\MyFunction, чем на полное квалифицированное имя.

Второй оператор use более интересен. Он определяет «Obj» как псевдоним для класса «MyClass» в пределах пространства имён App\Lib2\. Эта операция применительна только для классов — не для констант или функций. Теперь мы можем использовать new Obj(), или вызывать статические методы, как показано выше.

результат:

  1. App\Lib1\MYCONST
  2. App\Lib1\MyFunction
  3. App\Lib1\MyClass::WhoAmI
  4. App\Lib2\MyClass::WhoAmI

Правила разбора имён


Имена PHP идентификаторов разрешаются следующими правилами пространств имён. для более полной информации, обратитесь к руководству по PHP (на английском / на русском)
  1. Вызов квалифицированных функций разрешается во время компилирования.
  2. Все квалифицированные имена транслируются во время компиляции в соответствии с текущими импортированными пространствами имён. К примеру, если импортировано пространство имён A::B::C, вызов C::D::e() будет транслирован как A::B::C::D::e().
  3. Внутри пространства имён все квалифицированные имена транслируются согласно правилам импортирования, например, если пространство имён A\B\C импортируется как C, вызов C\D\e() транслируется в A\B\C\D\e().
  4. Неквалифицированные имена классов транслируются во время компиляции в соответствии с текущими импортированными пространствами имён и полные имена заменяют короткие импортированные имена, например, если класс C в пространстве имён A\B импортирован как X, new X() будет транслирован в new A\B\C().
  5. Внутри пространства имён вызов неквалифицированных функций интерпретируется во время компиляции. Например, если MyFunction() вызвана в пределах пространства имён A\B, PHP сперва ищет функцию \A\B\MyFunction(). Если она не будет найдена, PHP будет искать \MyFunction() в глобальном пространстве.
  6. Вызовы неквалифицированных или квалифицированных имён классов интерпретируется во время компиляции. Например, если мы вызываем new C() в пределах пространства имён A\B, PHP будет искать класс A\B\C. Если он не будет найден, это приведет к попытке автозагрузки A\B\C.

В следующей части: ключевые слова и автозагрузка.

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


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

Примечания:



A/ Замечания, поправки, указания на неточности и проч. — приветствуются!

B/ Код подсвечен с помощью Source Code Highlighter.
+29
11 октября 2009, 10:16
85

комментарии (55)

0
pento #
App\Lib2\MyClass::WhoAmI()
ужасный по внешнему виду код
0
nerezus #
У них появились сложности с разработкой синтаксического парсера, но вместо решения проблемы они решили обойтись лексическим костылем.

Кстати в русской документации(которая абсолютно неверна) код выглядит более привлекательно(::), только вот он не соответствует синтаксису пхп )
+2
serkys #
Возможно, я ошибаюсь, но вы являетесь последователем массовой истерии по поводу ввода в синтаксис этого символа.
Напротив, плох следующий код:
App::Lib2::MyClass::WhoAmI()
Причина — неоднозначность для программиста. Вызывается ли статичный метод MyClass::WhoAmI(), или же функция WhoAmI() неймспейса MyClass — знает только парсер. Ну и автор, разумеется — в течении месяца после завершения работ над проектом, в лучшем случае.
+1
NetGhost #
Я тоже не могу на него пока что спокойно смотреть (на \), но чувствую, что использование этого символа оправданно, а то что нету неоднозначности — огромнейший плюс
0
pento #
> Возможно, я ошибаюсь, но вы являетесь последователем массовой истерии по поводу ввода в синтаксис этого символа.
Да, поэтому в повседневной жизни и перешёл на Python
+2
chetzof #
No one cares.
+2
serkys #
Не вижу связи. Вы перешли на питон из-за мнения толпы о PHP?
0
pento #
Да нет :) Я сам бывший проф. разработчик на PHP.
Перешёл из-за по сути красот и удобсвтва использования (продуманная архитектура языка, минимальный и удобный синтаксис, фишки вроде документирования и т.п.)
0
DeMx #
Сомнительные доводы для перехода, прямо скажем. =)
+2
pento #
Ну вы попробуйте оба языка, а потом обсудим :)
+1
DeMx #
Пробовал, еще до третьей версии. Смысла мигрировать не увидел.
Но соглашусь, что я, возможно, не достаточно глубоко рыл этот самый Питончик. :)
НЛО прилетело и опубликовало эту надпись здесь
0
dicos #
У ПХП только кажущаяся простота, да, делать простые сайты-визитки легко, но если делать что-то более сложное, значительно труднее, чем на C#, Ruby, Python.
+1
chetzof #
«У ПХП только кажущаяся простота»
А мужики-то не знают О_о

«но если делать что-то более сложное, значительно труднее, чем на C#, Ruby, Python»
Возможно вы просто не имеете грамотно планировать архитектуру сложного ПХП-приложения? Не знаете язык на должном уровне? Тогда это всё объясняет.

Это всего-лишь инструмент, и если владеть инструментом хорошо, то вам будут под силу не только сайты-визитки.
0
funca #
класс определяет лексическое пространство имен. т.е. класс фактически является неймспейсом. поэтому синтаксис App::Lib2::MyClass::WhoAmI() был бы логичен (равно как и вложенные классы).
0
serkys #
Вы правы, раньше действительно применяли статичные классы вместо пространств имён.
Но различия всё же есть, не стоит лишний раз путать программиста.
+1
AlexFrost #
Разработчики сами себя загнали в тупик изначально заложеной архетиктурой языка.

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

Очевидно что выбран второй путь.
0
KeJSaR #
Как Вы полагаете, имели Zeev Suraski и Andi Gutmans возможность предвидеть подобное развитие языка?
+1
AlexFrost #
Думаю правилный вопрос будет звучать как — Следили ли они за тенденцией развития топовых языков программирования?

Скорее всего да, но не очень внимательно. На момент зарождения PHP уже была довольно успешная Java-а, которая повлияла на многие сторонние языки.
О том что PHP стал ОО языком начиная только с пятой версии даже говорить не стоит.

Если бы разработчики PHP спохвотились вовремя и обратили внимания на другие языки типа C#/ASP.NET, Java, Python в эпоху PHP 4 рефакторинг языка прошел бы менее болезненно и возмушений по поводу нововведенний в коментариях к этому посту было бы гораздо менше.

Но время упущенно.
НЛО прилетело и опубликовало эту надпись здесь
0
serkys #
Что есть «очистка занимаемой памяти»? Сборщик мусора в РНР есть уже давно (если не ошибаюсь, с 5 версии). Сейчас он умеет разбирать циклические зависимости.
НЛО прилетело и опубликовало эту надпись здесь
+1
serkys #
У PHP вся суть другая. Вам просто не нужно об этом думать — это язык более высокого уровня. Оптимизация производительности заключается в другом — в первую очередь, в работе с БД.
unset() вообще не предназначен для освобождения памяти, он удаляет переменную. Сборщик мусора находит неиспользуемые объекты и удаляет их из памяти, самое главное — делает это незаметно.
Если вам нравится управление памятью — пишите на C/C++, PHP просто не для вас. В новых версиях РНР появились функции управления сборщиком мусора, но их возможности, как и сам язык, вас никогда не устроят — вы с другой стороны смотрите на программирование.
0
taliban #
иногда управлять сборщиком мусора полезно, в данное время в пхп, например, есть один баг, он не освобождает циклические ссылки, в этом слуае было быполезно ручками управлять сборщиком мусора… всегда можно найти примеры, просто я считаю что возможность не плохая, и хоть иногда, но бывают моменты когда было бы неплохо ею воспользоваться
+1
serkys #
Поясните, пожалуйста, насчёт бага. Насколько я знаю, это было исправлено в PHP5.3.0
+1
taliban #
я проверял, к сожалению баг остался, смысл в том что 2 обьекта имеют ссылку друг на друга, при удалении обоих память не очищается, если это действие сделать в цикле, то память будет постоянно расти
0
serkys #
Очень жаль. Ранее это было анонсировано во многих презентациях.
НЛО прилетело и опубликовало эту надпись здесь
+1
serkys #
Ручное управление памятью в PHP не нужно — он развивается в другом направлении.
В данном случае надо корректно работать с областями видимости объектов, чтобы они не висели до окончания выполнения скрипта. Если ваш bootstrap-объект хранит в себе сотню объектов, разумеется, они не будут уничтожены. Если эти же объекты будут выполнять свою задачу в локальных областях видимости, никакого управления памятью не понадобится — сборщик мусора сам всё соберёт.
Хорошая архитектура спасёт ситуацию, а не ручное управление памятью.
НЛО прилетело и опубликовало эту надпись здесь
+2
serkys #
Лучшая оптимизация — улучшение архитектуры проекта. Остальное, за редкими исключениями — костыли.
Приведу пример скрипта:
<?php

$parser = new ConfigParser('/path/to/config.xml');
$configuration = $parser->toArray();

//other code

?>


* This source code was highlighted with Source Code Highlighter.

Разумеется, что объект парсера будет занимать память всё время выполнения скрипта. Отсюда — производительность хуже, чем могла бы быть.
Другой пример:
<?php

class Application
{
  private $_configuration = array();

  private function _parseConfig()
  {
    $parser = new ConfigParser('/path/to/config.xml');
    return $parser->toArray();
  }

  public function dispatch()
  {
    $this->_configuration = $this->_parseConfig();
    //dispatching
  }
}

$application = new Application();
$application->dispatching();

?>


* This source code was highlighted with Source Code Highlighter.

Объект парсера существует лишь пока он нужен. После выполнения метода _parseConfig() он удаляется из памяти сборщиком мусора.
Налицо пример оптимизации производительности за счёт улучшения архитектуры.
НЛО прилетело и опубликовало эту надпись здесь
0
serkys #
Значения на PHP5.3.0:
325392
325392
325392
325584 -IN
325832 -OOT
325584
325392

На PHP5.2.9:
66184
66200
66200
66608 -IN
66976 -OOT
67000
67000

PHP5.3.0 гораздо более прожорлив :)
Но речь не об этом. Как видим, сборщик мусора в PHP5.3.0 отработал как следует и вернул память к начальному значению. PHP5.2.9, к сожалению, не справился с этой задачей.
НЛО прилетело и опубликовало эту надпись здесь
0
serkys #
Итог обсуждения:
Все млин добавили, а вот очистки занимаемой памяти так и не сделали… а ведь млин если бы она была многие вещи можно было бы не переписывать на Си…

Таки сделали.
НЛО прилетело и опубликовало эту надпись здесь
0
serkys #
Насчёт бага согласен, он пока исправлен не до конца — судя по дампам, при цикличных ссылках очищается не всё, что должно. Это баг, его исправлением занимаются.
Предмет обсуждения — необходимость в ручном управлении памятью — считаю исчерпанным.
НЛО прилетело и опубликовало эту надпись здесь
0
serkys #
Единственный оставшийся момент — цикличные ссылки между объектами. Этот вопрос скоро будет решён.
Больше вариантов ручного управления памятью не вижу.
0
alitvinenko #
это что же у вас за супер-проект то такой? может быть стоит подойти со стороны оптимизации кода, а не тупо переписывать части проекта на другом языке?
НЛО прилетело и опубликовало эту надпись здесь
0
alitvinenko #
А зачем вы такие вещи вообще делали в PHP? Я думаю, что для этого язык PHP не предназначен.
Задача пхп — обработать запрос пользователя, если надо — сохранить данные, и выдать результат на экран. По сути все. Ну конечно там еще какие-то более-менее ненагруженные операции вычисления произвести при помощи скриптов, запущенных в кроне.
Или я не прав?
НЛО прилетело и опубликовало эту надпись здесь
0
alitvinenko #
Именно это я и хотел сказать :) Наверно, как-то неверно описал свою мысль.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
+3
taliban #
мне кается было бы удобно сделать неймспейсы по пути файла, и заминить ими инклуды. В том же питоне очен удобная работа с модулями, было бы удобно иметь аналог в пхп
НЛО прилетело и опубликовало эту надпись здесь
0
romy4 #
а если архитектура такая, что неймспейсы храянтся как topspace.subspace.myspace.php или topspace\subspace.myspace.php? У каждого свой подход к именованию и всё стандартизировать, значит убирать гибкость.
0
taliban #
а я собственно не понял в чем проблемма? к примеру все «модули» начинаются от корнясайта, что вам мешает подключить неймспейс выше стоящий или из другой ветки? каким образом теряется гибкость?
0
taliban #
вообще надо стандартизировать все, тогда проблем будет меньше
–5
egoserg #
как мне кажется, за такой код нужно руки отбивать.
0
serkys #
В чём, собственно, проблема?
0
Goodkat #
A они догадались упрятать старинные функции с именами кто в лес кто по дрова в какой-нить нэймспэйс типа "\deprecated"? (который можно было бы подключать нэймспэйсом по-умолчанию через php.ini для старых скриптов.
Или хотя бы к шестой версии догадаются преименовать все функции в едином стиле?
А то ведь то глагол впереди, то существительно, то «неймспейс» (str-функции), слова пишутся иногда вместе, иногда через подчёркивание.
0
LoneCat #
Да никак не догадались, все классы, функции и константы что были в глобальном пространстве имен — там и остались :)

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