PHP Namespace
Недавно инкапсулировал свой проект в namespace и столкнулся с проблемой отсутствия нормальной документации. Все, что удалось найти датируется примерно 2009 годом, а на дворе почти 2012… В найденном материале куча нерабочих мест, использующих то, что в нынешней версии php нет. В связи с этим хочу немного осветить этот вопрос.
Итак, что же такое Namespace или пространство имен? Великая wikipedia определяет их так:
Пространство имён (англ. namespace) — некоторое множество, под которым подразумевается модель, абстрактное хранилище или окружение, созданное для логической группировки уникальных идентификаторов (то есть имён). Идентификатор, определенный в пространстве имён, ассоциируется с этим пространством. Один и тот же идентификатор может быть независимо определён в нескольких пространствах. Таким образом, значение, связанное с идентификатором, определённым в одном пространстве имён, может иметь (или не иметь) такое же (а скорее, другое) значение, как и такой же идентификатор, определённый в другом пространстве. Языки с поддержкой пространств имён определяют правила, указывающие, к какому пространству имён принадлежит идентификатор (то есть его определение).wiki
Все ясно? На самом деле все просто. До версии 5.3 в php существовало всего два пространства — глобальное(в котором выполнялся ваш основной код) и локальное(в котором определялись переменные функций).
С версии 5.3 все изменилось. Теперь можно определить свое пространство имен, в котором будут существовать ваши классы методы и т.д.
Надеюсь стало немного понятнее.
Я специально обозвал классы одинаково. Так они определены в разных пространствах, то это два разных класса, несмотря на одинаковые имена. Основной скрипт,-по прежнему, функционирует в глобальном пространстве, здесь ничего не изменилось и в нем, по-прежнему, можно определять классы и функции. Так для чего же тогда нужны пространства? Прежде всего, для уверенности в том, что когда вы подключаете файл, с каким-нибудь фреймворком или библиотекой, ваши классы не переопределят классы фреймворка или наоборот.
Для того, чтобы использовать классы определенные в своем пространстве имен, необходимо в нужном месте(я как правило предпочитаю делать это в начале файла) импортировать определенное вами пространство в глобальное для этого используется ключевое слово
Внимание: по каким-то своим основаниям php не допускает использование ключевого слова use в блоках условий и циклах
возьмем пример с картинок и воплотим его в коде:
Рекомендуется объявлять каждое пространство имен в отдельном файле. Хотя можно и в одном, но это строго не рекомендуется!
Теперь переместимся в третий файл, в котором будет функционировать наш основной скрипт
index.php
казалось бы в чем преимущество, только кода прибавилось, однако это не совсем так, чуть дальше я приведу пример класса автозагрузки, с которым строки подключающие файлы с классами будут ненужны.
А теперь обратимся к нашим классам
Внимание: использование оператора разрешения области видимости (::) в пространствах имен php не допускается! Единственное для чего он годится — это для обращения к статичным методам класса и константам. Вначале хотели использовать для пространства имен именно его, но затем из-за возникших проблем отказались. Поэтому конструкция вида A::A::say(); недопустима и приведет к ошибке.
Внимание: во избежание недоразумений необходимо экранировать данный символ при его использовании в строках: ‘\\’
Пространства имен можно вкладывать друг в друга, дополним наш файл A.php:
Важным моментом является использование алиасов для импортированных пространств. Можно было написать A\subA::say(); согласитесь, каждый раз писать полные пути к пространствам затруднительно для того, чтобы этого избежать были введены алиасы. При компилировании произойдет следующее вместо алиаса sub будет подставлено A\subA, таким образом мы получим вызов A\subA::say();
А что же тогда происходит при вызове функций определенных в глобальном пространстве? PHP сначала ищет функцию внутри того пространства, где вы сейчас работаете, и в случае если не находит, то обращается к глобальной области видимости. Для того, чтобы сразу указать, что вы используете глобальную функцию необходимо перед ней поставить обратный слеш.
Для того чтобы не было проблем с автозагрузкой классов из пространств файловую систему нужно организовать аналогично организации пространств. Например, есть у нас корневая папка classes, где и будут храниться наши классы, тогда наши пространства могут быть организованы следующим образом
classes\A\A.php
classes\A\sub\A.php(подпространство sub вынесем в отдельный файл)
classes\B\B.php
В php есть магическая константа __NAMESPACE__ которая содержит имя текущего пространства.
А теперь об автозагрузке.
public static function autoload($file) < $file = str_replace('\\', '/', $file); $path = $_SERVER['DOCUMENT_ROOT'] . '/classes'; $filepath = $_SERVER['DOCUMENT_ROOT'] . '/classes/' . $file . '.php'; if (file_exists($filepath)) < if(Autoloader::debug) Autoloader::StPutFile(('подключили ' .$filepath)); require_once($filepath); >else < $flag = true; if(Autoloader::debug) Autoloader::StPutFile(('начинаем рекурсивный поиск')); Autoloader::recursive_autoload($file, $path, &$flag); >> public static function recursive_autoload($file, $path, $flag) < if (FALSE !== ($handle = opendir($path)) && $flag) < while (FAlSE !== ($dir = readdir($handle)) && $flag) < if (strpos($dir, '.') === FALSE) < $path2 = $path .'/' . $dir; $filepath = $path2 . '/' . $file . '.php'; if(Autoloader::debug) Autoloader::StPutFile(('ищем файл ' .$file .' in ' .$filepath)); if (file_exists($filepath)) < if(Autoloader::debug) Autoloader::StPutFile(('подключили ' .$filepath )); $flag = FALSE; require_once($filepath); break; >Autoloader::recursive_autoload($file, $path2, &$flag); > > closedir($handle); > > private static function StPutFile($data) < $dir = $_SERVER['DOCUMENT_ROOT'] .'/Log/Log.html'; $file = fopen($dir, 'a'); flock($file, LOCK_EX); fwrite($file, ('║' .$data .'=>' .date('d.m.Y H:i:s') .'
║
' .PHP_EOL)); flock($file, LOCK_UN); fclose ($file); > > \spl_autoload_register('yourNameSpace\Autoloader::autoload'); >
Если посмотреть на имена классов, которые приходят для загрузки, то будет видно, что каждый класс предваряется префиксом из пространства имен, которое указано в use. Именно поэтому рекомендую использовать расположение файлов в каталогах аналогично пространству имен, это ускоряет поиск до одной-двух итераций.
Теперь наш индекс можно написать так:
теперь все классы и интерфейсы, которые вы будет использовать будут загружены автоматически.
Для демонстрации некоторых динамических возможностей языка с пространствами объявим еще один класс:
test.php
function sayName($name) < echo 'Привет ' . $name; >static function sayOther() < echo 'статичный вызов'; >> >
sayName('test'); //а можно так test\sayName('test2'); //или так $obj::sayName('test'); //а можно так test::sayName('test2');
Надеюсь, что моя статья будет полезна кому-нибудь.