Недавно инкапсулировал свой проект в namespace и столкнулся с проблемой отсутствия нормальной документации. Все, что удалось найти датируется примерно 2009 годом, а на дворе почти 2012… В найденном материале куча нерабочих мест, использующих то, что в нынешней версии php нет. В связи с этим хочу немного осветить этот вопрос.
Итак, что же такое Namespace или пространство имен? Великая wikipedia определяет их так:
Все ясно? На самом деле все просто. До версии 5.3 в php существовало всего два пространства — глобальное(в котором выполнялся ваш основной код) и локальное(в котором определялись переменные функций).
С версии 5.3 все изменилось. Теперь можно определить свое пространство имен, в котором будут существовать ваши классы методы и т.д.
Надеюсь стало немного понятнее.
Я специально обозвал классы одинаково. Так они определены в разных пространствах, то это два разных класса, несмотря на одинаковые имена. Основной скрипт,-по прежнему, функционирует в глобальном пространстве, здесь ничего не изменилось и в нем, по-прежнему, можно определять классы и функции. Так для чего же тогда нужны пространства? Прежде всего, для уверенности в том, что когда вы подключаете файл, с каким-нибудь фреймворком или библиотекой, ваши классы не переопределят классы фреймворка или наоборот.
Для того, чтобы использовать классы определенные в своем пространстве имен, необходимо в нужном месте(я как правило предпочитаю делать это в начале файла) импортировать определенное вами пространство в глобальное для этого используется ключевое слово
возьмем пример с картинок и воплотим его в коде:
файл A.php
файл B.php
Возможен альтернативный синтаксис:
Рекомендуется объявлять каждое пространство имен в отдельном файле. Хотя можно и в одном, но это строго не рекомендуется!
Теперь переместимся в третий файл, в котором будет функционировать наш основной скрипт
index.php
казалось бы в чем преимущество, только кода прибавилось, однако это не совсем так, чуть дальше я приведу пример класса автозагрузки, с которым строки подключающие файлы с классами будут ненужны.
А теперь обратимся к нашим классам
Для пространств имен необходимо использовать символ обратного слеша "\"
Пространства имен можно вкладывать друг в друга, дополним наш файл 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__ которая содержит имя текущего пространства.
Приведенный ниже класс не мой, я только сделал его рабочим и немного усовершенствовал взят отсюдова.
Если посмотреть на имена классов, которые приходят для загрузки, то будет видно, что каждый класс предваряется префиксом из пространства имен, которое указано в use. Именно поэтому рекомендую использовать расположение файлов в каталогах аналогично пространству имен, это ускоряет поиск до одной-двух итераций.
Теперь наш индекс можно написать так:
теперь все классы и интерфейсы, которые вы будет использовать будут загружены автоматически.
Для демонстрации некоторых динамических возможностей языка с пространствами объявим еще один класс:
test.php
index.php
Надеюсь, что моя статья будет полезна кому-нибудь.
Итак, что же такое Namespace или пространство имен? Великая wikipedia определяет их так:
Пространство имён (англ. namespace) — некоторое множество, под которым подразумевается модель, абстрактное хранилище или окружение, созданное для логической группировки уникальных идентификаторов (то есть имён). Идентификатор, определенный в пространстве имён, ассоциируется с этим пространством. Один и тот же идентификатор может быть независимо определён в нескольких пространствах. Таким образом, значение, связанное с идентификатором, определённым в одном пространстве имён, может иметь (или не иметь) такое же (а скорее, другое) значение, как и такой же идентификатор, определённый в другом пространстве. Языки с поддержкой пространств имён определяют правила, указывающие, к какому пространству имён принадлежит идентификатор (то есть его определение).wiki
Все ясно? На самом деле все просто. До версии 5.3 в php существовало всего два пространства — глобальное(в котором выполнялся ваш основной код) и локальное(в котором определялись переменные функций).
С версии 5.3 все изменилось. Теперь можно определить свое пространство имен, в котором будут существовать ваши классы методы и т.д.
Надеюсь стало немного понятнее.
Я специально обозвал классы одинаково. Так они определены в разных пространствах, то это два разных класса, несмотря на одинаковые имена. Основной скрипт,-по прежнему, функционирует в глобальном пространстве, здесь ничего не изменилось и в нем, по-прежнему, можно определять классы и функции. Так для чего же тогда нужны пространства? Прежде всего, для уверенности в том, что когда вы подключаете файл, с каким-нибудь фреймворком или библиотекой, ваши классы не переопределят классы фреймворка или наоборот.
Для того, чтобы использовать классы определенные в своем пространстве имен, необходимо в нужном месте(я как правило предпочитаю делать это в начале файла) импортировать определенное вами пространство в глобальное для этого используется ключевое слово
use
Внимание: по каким-то своим основаниям php не допускает использование ключевого слова use в блоках условий и циклах
возьмем пример с картинок и воплотим его в коде:
Внимание: ключевое слово namespase должно быть расположено в самом начале файла сразу после <? php
файл A.php
<? php
namespace A
{
class A
{
public static function say()
{
echo 'Я пространство имен А';
}
}
}
файл B.php
<? php
namespace B
{
class A
{
public static function say()
{
echo 'Я пространство имен B';
}
}
}
Возможен альтернативный синтаксис:
<? php
namespace A;
class A
{
public static function say()
{
echo 'Я пространство имен А';
}
}
Рекомендуется объявлять каждое пространство имен в отдельном файле. Хотя можно и в одном, но это строго не рекомендуется!
Теперь переместимся в третий файл, в котором будет функционировать наш основной скрипт
index.php
<? php
require_once 'A.php';
require_once 'B.php';
use A\A;
use B\A;
казалось бы в чем преимущество, только кода прибавилось, однако это не совсем так, чуть дальше я приведу пример класса автозагрузки, с которым строки подключающие файлы с классами будут ненужны.
А теперь обратимся к нашим классам
<? php
require_once 'A.php';
require_once 'B.php';
use A\A;
use B\A;
A\A::say();
B\A::say();
Внимание: использование оператора разрешения области видимости (::) в пространствах имен php не допускается! Единственное для чего он годится — это для обращения к статичным методам класса и константам. Вначале хотели использовать для пространства имен именно его, но затем из-за возникших проблем отказались. Поэтому конструкция вида A::A::say(); недопустима и приведет к ошибке.
Для пространств имен необходимо использовать символ обратного слеша "\"
Внимание: во избежание недоразумений необходимо экранировать данный символ при его использовании в строках: '\\'
Пространства имен можно вкладывать друг в друга, дополним наш файл A.php:
<? php
namespace A
{
class A
{
public static function say()
{
echo 'Я пространство имен А';
}
}
}
namespace A\subA
{
class A
{
public static function say()
{
echo 'Я подпространство имен А';
}
}
}
а в индексе напишем следующее:
<? php
require_once 'A.php';
require_once 'B.php';
use A\A as A;
use B\A as B;
use A\subA as sub
A::say();
A::say();
sub::say();
Важным моментом является использование алиасов для импортированных пространств. Можно было написать 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__ которая содержит имя текущего пространства.
А теперь об автозагрузке.
Приведенный ниже класс не мой, я только сделал его рабочим и немного усовершенствовал взят отсюдова.
Внимание:Для того, чтобы ваши классы загружались имя класса должно совпадать с именем файла!
<?php
namespace yourNameSpace
{
class Autoloader
{
const debug = 1;
public function __construct(){}
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(('ищем файл <b>' .$file .'</b> 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') .'<br/>║<br/>' .PHP_EOL));
flock($file, LOCK_UN);
fclose ($file);
}
}
\spl_autoload_register('yourNameSpace\Autoloader::autoload');
}
Если посмотреть на имена классов, которые приходят для загрузки, то будет видно, что каждый класс предваряется префиксом из пространства имен, которое указано в use. Именно поэтому рекомендую использовать расположение файлов в каталогах аналогично пространству имен, это ускоряет поиск до одной-двух итераций.
Теперь наш индекс можно написать так:
<? php
require_once 'Autoloader.php';
use Autoloader as Autoloader;
use A\A as A;
use B\A as B;
use A\subA as sub
A::say();
A::say();
sub::say();
теперь все классы и интерфейсы, которые вы будет использовать будут загружены автоматически.
Для демонстрации некоторых динамических возможностей языка с пространствами объявим еще один класс:
test.php
<? php
namespace mySpace
{
class test
{
__construct()
{
//конструктор;
}
function sayName($name)
{
echo 'Привет ' . $name;
}
static function sayOther()
{
echo 'статичный вызов';
}
}
}
index.php
<? php
require_once 'Autoloader.php';
use Autoloader as Autoloader;
use mySpace\test as test
//можно, например сделать так
$class = 'test';
//приведет к вызову конструктора
$obj = new $class;
$obj->sayName('test');
//а можно так
test\sayName('test2');
//или так
$obj::sayName('test');
//а можно так
test::sayName('test2');
Надеюсь, что моя статья будет полезна кому-нибудь.