Рубрика:
Программирование /
Веб-программирование
|
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|
Александр Майоров
Пространства имен в PHP
Пространства имен, появившиеся в PHP относительно недавно, предназначены для локализации имен идентификаторов и предотвращения их конфликтов. Сегодня подробно рассмотрим способы применения пространств имен и их особенности в языке PHP.
Что такое namespace?
Пространство имён (namespace) – это область определения переменных, констант и т. п., ограничивающая их область видимости. Оно предназначено для локализации имен идентификаторов и предотвращения конфликтов между ними. Элементы, объявленные в одном пространстве, отделены от элементов, принадлежащих другому пространству.
Немного уйдем от PHP к С++ и посмотрим на примерах, что из себя представляют пространства имен, так как в этом языке они появились относительно давно.
Среда программирования С++ наполнена большим количеством переменных, функций и классов. Раньше все их имена пребывали в одном глобальном пространстве и нередко конфликтовали между собой.
Например, если в программе определена функция atoi(), она может замещать собой стандартную функцию atoi(), поскольку имена обеих функций находятся в одном глобальном пространстве имен.
Чаще всего конфликты имен возникали, когда в одной программе использовались несколько сторонних библиотек одновременно, написанных разными программистами. Особенно это касается имен классов. Например, если в программе определен класс MergeInstance, а в библиотеке, которую использует эта программа, имя уже было задействовано, возникал конфликт.
Было решено разделять идентификаторы по пространству имен, и ввели ключевое слово namespace. Введение namespace позволило решать эти проблемы. Поскольку пространство имен позволяет локализовать область видимости объектов, объявленных внутри него, одно и то же имя, упомянутое в разных контекстах, больше не вызывало конфликтов. Теперь стандартная библиотека определена внутри своего собственного пространства имен std, что намного уменьшает вероятность конфликтов. Программист может самостоятельно создавать свои собственные пространства имен и локализовать имена, которые могут вызывать конфликты. Это особенно важно при разработке классов и библиотек функций.
Ключевое слово namespace позволяет разделить глобальное пространство имен на декларативные области. В сущности, пространство имен – это область видимости. Общий вид объявления пространства имен выглядит так:
namespace name
{
//Здесь ваш код…
}
Все, что объявлено внутри namespace, находится внутри области видимости этого пространства имен. Рассмотрим пример:
namespace MyNameSpace
{
int variable_1;
int variable_2;
class MyClass { ... };
}
Здесь переменные variable_1 и variable_2, а также класс MyClass находятся в области видимости, определенной пространством MyNameSpace. Доступ к ним можно осуществить через имя объявленного пространства:
MyNameSpace::variable1 = 10;
MyNameSpace::MyClass myObject;
Легко представить, во что превратится программа, в которой часто встречаются ссылки на элементы пространства имен. Текст такой программы станет малопонятным и очень объемным, поскольку будет пестреть квалификаторами и операторами области разрешения видимости.
Для того чтобы этого избежать, была введена директива using, имеющая такой вид:
using namespace name;
using name::spacemember
Теперь вы имеете представление о пространствах имен и о том, как они реализованы в С++.
Далее мы разберем, как же реализовали разработчики PHP этот механизм, рассмотрим отличия, достоинства и недостатки.
Что получили?
До сегодняшнего дня PHP, как когда-то Cи, располагал все идентификаторы в одном глобальном пространстве имен. Все подключаемые файлы разделяют одно и то же пространство, что затрудняет коллективную разработку, так как разработчикам приходится следить за именованием функций, констант и переменных, чтобы случайно не назвать их одинаково или не назвать именем уже имеющейся функции в языке. Отсюда и пошел стиль оформления длинных имен, разделенных подчеркиванием. Например, что-то вроде tpl_tplproc_execute_template. Ведь как-то надо различать функции по принадлежности к той или иной библиотеке функций. При разработке сложных приложений могут возникать конфликты идентификаторов, как уже описывалось выше.
С приходом PHP версии 5 эта проблема решалась в какой-то степени благодаря расширенному ООП синтаксису. Но это не устраняет основной проблемы. В результате было принято решение добавить в PHP версии 6 поддержку namespace.
Все с нетерпением ждут выхода PHP 6, так как разработчики обещают порадовать массой нововведений, но вот пространством имен они уже порадовали нас сейчас! Наконец-то в PHP появились namespace. И уже сейчас у вас есть возможность протестировать и попробовать в работе это нововведение. Для того чтобы вы смогли протестировать примеры из статьи, вам достаточно скачать доступную dev-версию PHP 5.3 с сайта http://snaps.php.net.
Директива namespace
Собственно, теперь рассмотрим, что из себя представляют namespace в PHP. Объявление пространства имен осуществляется конструкцией namespace name. Она должна идти всегда в самом начале файла, иначе будет сгенерирована ошибка «Fatal error...».
<?php
namespace myspace;
...
?>
Если в файле объявлен namespace, то этот файл уже рассматривается как некоторый модуль, а не обычный include-файл.
В отличие от C++ началом пространства имен считается его объявление, а окончанием – конец файла. То есть файл является неким контейнером для объявленного пространства и может рассматриваться как некий модуль, в котором все функции, классы, константы и переменные логически связаны друг с другом.
В файле, в который осуществляется включение данного модуля, доступ ко всем функциям, классам и константам осуществляется через созданное пространство.
<?php //ns.php
namespace myspace;
function test_foo ( $argv )
{
printf("\n%s\n", $argv);
}
?>
<?php
include 'ns.php';
$argv = 'test';
myspace:: test_foo( $ragv );
?>
Вложенные namespace в отличие от С++ создавать нельзя, но можно создавать составные пространства имен:
<?php
namespace myspace::space1::subspace;
...
?>
Доступ осуществляется точно так же, но через полное имя составного пространства:
<?php //ns.php
namespace myspace::space1::subspace;
function foo_bar( $argv )
{
printf("\n%s\n", $argv);
}
?>
<?php
include 'ns.php';
$argv = 'test';
myspace::space1::subspace::test( $argv );
?>
Директива USE (IMPORT)
Так же, как и в С++, для удобства работы было добавлено ключевое слово import. Оно чем-то похоже на using из С++.
Пока я писал статью и изучал новые возможности неймспейсов, разработчики вели бурные обсуждения по поводу того, следует ли заменить слово import на слово using, к тому же оно уже зарезервировано в языке PHP. По смыслу слово import больше подходит, чем слово use, так как директива import предназначена в первую очередь для импорта пространства.
И вот, скачав очередное обновление, оказалось, что ключевое слово import все-таки заменили словом use. Мне пришлось заново проводить тесты.
Давайте рассмотрим, что дает нам директива use:
<?php //ns.php
namespace myspace;
function foo_bar( $argv )
{
printf("\n%s\n", $argv);
}
?>
<?php
include 'ns.php';
use myspace as ns;
$argv = 'test';
ns::foo_bar( $argv );
?>
С помощью директивы use мы импортировали область видимости из пространства имен myspace в пространство ns. Точнее, мы просто назначили псевдоним (alias). Таким образом, мы сделали удобнее работу. Но лучше всего это можно прочувствовать, если вы импортируете составной namespace, например такой:
<?php //ns.php
namespace MyNameSpace::MySpace::Subspace;
function foo_bar( $argv )
{
printf("\n%s\n", $argv);
}
?>
<?php
include 'ns.php';
use MyNameSpace::MySpace::Subspace as ns;
ns::foo_bar('test');
Сами понимаете, что при программировании лишний раз писать квалификаторы области разрешения видимости будет не очень удобным.
Также можно делать импорт составного имени без определения псевдонима. Например:
<?php //ns.php
namespace MyNameSpace::MySpace::Subspace;
function foo()
{
...
}
?>
<?php
include 'ns.php';
MyNameSpace::MySpace::Subspace:: foo();
use MyNameSpace::MySpace;
MySpace::Subspace:: foo();
use MyNameSpace::MySpace::Subspace;
Subspace:: foo();
?>
Это аналогично записи:
<?php
include 'ns.php';
MyNameSpace::MySpace::Subspace:: foo();
use MyNameSpace::MySpace as MySpace;
MySpace::Subspace:: foo();
use MyNameSpace::MySpace::Subspace as Subspace;
Subspace:: foo();
?>
Мы уже успели посмотреть, что можно делать, используя директиву use (это далеко не все возможности, но о них мы поговорим подробнее чуть позже).
Нельзя делать импорт несоставного имени без определения псевдонима. Если написать:
<?php //ns.php
namespace MyNameSpace;
function foo()
{
...
}
?>
<?php
include 'ns.php';
use MyNameSpace;
MySpace::Subspace:: foo();
...
?>
то будет сгенерировано предупреждение: «Warning: The use with non-compound name has no effect in...»
В отличие от С++, произвести импорт пространства имен в глобальное или текущее, используя оператор use, нельзя.
Нельзя делать импорт в составной алиас:
<?php
include 'ns.php';
use MyNameSpace::MySpace::Subspace as test::ns;
...
?>
Переопределение и рокировка
Есть еще одна интересная особенность в неймспейсах – это переопределение уже существующего неймспейса и его перекрытие.
Представим, что у нас есть некоторый модуль space1.php.
<?php
namespace space1;
function foo_bar()
{
printf("\n space1 \n");
}
?>
И модуль space2.php.
<?php
namespace space2;
function foo_bar()
{
printf("\n space2 \n");
}
?>
Оба модуля подключаются к некоторому скрипту script.php.
<?php
include 'space1.php';
include 'space2.php';
echo space1:: foo_bar();
// Выведет space1
echo space2:: foo_bar();
// Выведет space2
?>
Пока все работает как надо. А теперь мы берем и переопределяем space1 на space2.
<?php
include 'space1.php';
include 'space2.php';
use space1 as space2;
echo space1:: foo_bar();
// Выведет space1
echo space2:: foo_bar();
// Выведет space1
?>
В результате мы перекроем space2, и он станет ссылаться на space1. Просто мы «перетрем» существующий идентификатор нашим импортированным алиасом.
Вроде бы можно было сказать, что это тоже баг. Но нет! Это как раз-таки фича. Представьте себе, как удобно перекрывать отдельные участки кода, использующие разные модули, но с одинаковыми функциями. Налицо мы имеем полиморфизм, но достигнутый не средствами ООП через классы, а через пространство имен.
Но это не все. Мы можем произвести рокировку пространства имен. То есть мы можем переопределить space1 на space2, а space2 на space1.
<?php
include 'space1.php';
include 'space2.php';
echo space1:: foo_bar();
// Выведет space1
echo space2:: foo_bar();
// Выведет space2
printf(" \n ------------------ \n");
use space1 as space2;
echo space1:: foo_bar();
// Выведет space1
echo space2:: foo_bar();
// Выведет space1
printf(" \n ------------------ \n");
use space2 as space1;
echo space1:: foo_bar();
// Выведет space2
echo space2:: foo_bar();
// Выведет space1
?>
В итоге мы полностью поменяли местами названия пространства имен.
Кстати, надо вас предупредить, что делать переопределение можно только один раз. Если вы попытаетесь еще раз вызвать «use space1 as space2», то будет сгенерирована ошибка «Fatal error: Cannot reuse import name ...». Но при этом вы можете переопределять пространство имен на другие имена сколь угодно много, главное, чтобы имена не повторялись.
<?php
include 'mylib.php';
use mylib as myspace;
use mylib as a;
use mylib as b;
use mylib as c;
...
use mylib as z;
?>
При этом доступ можно осуществлять через любое переопределенное имя неймспейса, включая исходное имя пространства имен данного модуля.
Зарезервированная константа __NAMESPACE__
Для указания на текущее пространство имен появилась константа __NAMESPACE__.
<?php //ns.php
namespace MyNameSpace::MySpace::Subspace;
function foo_bar( $argv )
{
printf("\n %s \n", __NAMESPACE__);
}
?>
Эта константа всегда указывает на текущее пространство имен. Для глобального неймспейса она не определена или имеет пустое значение.
<?php
var_dump( __NAMESPACE__ );
?>
Дамп выведет string(0), то есть пустое значение. Эту константу можно использовать для вызова функций, тем самым сделав настраиваемый квалификатор доступа. Также применение этой константы позволяет делать очень гибкие приложения с взаимозаменяемыми «сменными» модулями. Например, есть 2 библиотеки:
<?php //ns1.php
namespace space1;
$current_space = __NAMESPACE__ ;
function foo()
{
return "space1";
}
function bar()
{
printf("\n %s \n", foo() );
}
?>
и
<?php //ns2.php
namespace space2;
$current_space = __NAMESPACE__ ;
function foo()
{
return "space2";
}
function bar()
{
printf("\n %s \n", foo() );
}
?>
Есть программа:
<?php
include $lib;
call_user_func("$current_space::bar");
?>
Благодаря такой организации мы можем сменить библиотеку, изменив значение переменной $lib. Это найдет свое применение при разработке кросс-платформенных приложений. Например, можно сделать поддержку двух баз данных, MySQL и SQLite или чего-нибудь еще. Достаточно будет менять имя подключаемого модуля – и все. Хотя это не самый лучший пример, так как такой функционал можно реализовать при помощи классов. Но главное, понять суть, а применение уважаемый читатель найдет сам.
От теории к практике
Пространство имен теперь позволяет создавать функции с именами встроенных функций в PHP. Для наглядности напишем простые тесты.
Тесты будут решать некую абстрактную задачу. Не будем сильно усложнять ее, так как моя задача показать принцип работы с пространством имен, а не написать очередной Фреймворк. Например, у нас есть библиотека для работы с массивами и библиотека для работы со строками. Опишем каждую из них.
Библиотека для работы со строками содержит функции sort() и rsort(). Вы знаете, что эти функции являются встроенными функциями для сортировки массивов. Просто так переопределить их невозможно.
Было невозможно. Теперь благодаря нововведению мы создаем нашу библиотеку, strings.sort.php, объявляем в ней пространство имен mylib::strings::sort.
Вы заметили, что имя файла похоже на объявленное пространство имен. Это сделано только для удобства, имя неймспейса никак не зависит от имени файла.
Далее в нашей библиотеке мы объявляем функции.
<?phpphp
namespace mylib::strings::sort;
function sort( $str )
{
/**
* Эта функция сортирует строку в алфавитном порядке
* ...
*/
}
function rsort( $str )
{
/**
* Эта функция сортирует строку в обратном алфавитном порядке
* ...
*/
}
?>
Также мы реализовали библиотеку для работы с массивами arrays.sort.php. Эта библиотека будет содержать точно такие же функции, как и для сортировки строк, но они для работы с массивами.
<?phpphp
namespace mylib::arrays::sort;
function sort( $array )
{
/**
* Функция сортировки ассоциативного массива в алфавитном порядке
* ...
*/
}
function rsort( $array )
{
/**
* Функция сортировки ассоциативного массива в обратном порядке
* ...
*/
}
?>
В PHP уже есть встроенные функции для работы с массивами, причем с точно такими же именами, скажете вы. Но я напоминаю, что мы решаем абстрактную задачу, поэтому сейчас потребовалось написать свои алгоритмы сортировок, а для удобства мы используем имена встроенных функций.
Теперь подключаем в наш скрипт start.php, к примеру, наши библиотеки. Вызов соответствующих функций производится через неймспейсы.
<?php
include 'mylib/string.sort.php';
include 'mylib/arrays.sort.php';
$str = 'string to sort';
$str = mylib::strings::sort::sort( $str );
$arr = array(
'string'=>'value1',
'to'=>' value1',
'sort'=>' value1',
);
$str = mylib::arrays::sort::sort( $arr );
?>
Как видите, мы смогли создать свои функции с зарезервированными именами.
Более того, мы подключили файлы, в которых имена функций совпадают, но при этом не происходит никаких конфликтов. Вызов идет через полные пути неймспейсов, и вы можете сказать, что неудобно использовать такие длинные имена. Для этого была добавлена директива import, позволяющая делать импорт и переопределять пространства имен.
Например, мы можем сделать импорт имен, тогда скрипт будет переписан следующим образом:
<?php
include 'mylib/string.sort.php';
include 'mylib/arrays.sort.php';
use mylib::strings::sort;
$str = 'string to sort';
$str = sort::sort( $str );
$arr = array(
'string'=>'value1',
'to'=>' value2',
'sort'=>' value3',
);
$str = mylib::arrays::sort ( $arr );
?>
После этого мы получаем доступ к нашим функциям через импортированное пространство имен, что позволяет заметно сократить запись.
Вы заметили, что был произведен импорт только библиотеки для работы со строками? Дело в том, что если произвести импорт неймспейса библиотеки для работы с массивами import mylib::arrays::sort, у нас произойдет конфликт имен и будет сгенерирована ошибка: «Fatal error: Cannot reuse import name...».
Но тут к нам на помощь приходит директива import. Пространства имен можно не только импортировать, но так же переопределять и задавать алиасы (alias – псевдоним) для них. Если задан алиас для неймспейса, то доступ можно осуществлять одновременно как по полному пути неймспейса, так и по созданному псевдониму.
<?php
include 'mylib/string.sort.php';
include 'mylib/arrays.sort.php';
use mylib::strings::sort as str;
$str = 'string to sort';
$str = str::sort( $str );
import mylib::arrays::sort as arr;
$arr = array(
'string'=>'value1',
'to'=>' value2',
'sort'=>' value3',
);
$str = arr::sort ( $arr );
?>
Константы
Пусть у нас есть некая общая библиотека для работы со строками. В ней есть какой-то класс, а так же константы, определяющие работу нашего приложения. Константы внутри неймспейса объявляются точно так же, как внутри класса. Если вы используете функцию define(), то такая константа будет работать точно так же, как если бы мы делали обычное включение файла без определенного пространства имен. То есть она будет находиться в глобальном пространстве, хотя и была объявлена внутри нашего namespace.
Но внутри неймспейса можно определить константу, которая будет ограничена этим пространством и не будет пересекаться с такими же из других библиотек. Объявление происходит точно так же, как объявляются константы внутри классов.
В нашей библиотеке strings.php хранятся некоторые классы и константы для настройки. Допустим, у нас есть разные варианты библиотек со строковыми функциями для различных кодировок:
<?phpphp
namespace mylib;
const LOCALE = 'cp1251';
class TString
{
const MIXED = 0;
const UPPERCASE = 1;
const LOWERCASE = 2;
public static $Encode = '';
public $case = 0;
}
?>
Теперь мы выбираем подключаемую библиотеку в зависимости от настроек приложения. Делаем это следующим образом:
<?php
include 'mylib/strings.php';
switch ( mylib::LOCALE )
{
case 'utf8':
$path = 'utf8';
break;
case 'koi8r':
$path = 'koi8r';
break;
default:
$path = 'cp1251';
}
include "mylib/$path/strings.sort.php ";
include 'mylib/arrays.sort.php';
...
?>
При этом у нас в каждом модуле может быть своя константа LOCALE. Можно так же завести общую константу, видимую во всех файлах и не конфликтующую с внутренними константами. Внутри пространства можно определять классы.
Обращение к классам, свойствам и методам класса осуществляется аналогично.
<?php
...
mylib::TString::$Encode = $encode;
$string = new mylib::TString();
$string->case = mylib::TString::LOWERCASE;
...
?>
Классы и интерфейсы
С интерфейсами и классами дела обстоят аналогично. Можно объявить их внутри одного пространства имен, а использовать в пределах другого. Например, есть абстрактный класс и интерфейсы, объявленные внутри пространства mylib:
<?php
namespace mylib;
interface Interface1
{
public function Method_1();
}
interface Interface2
{
public function Method2();
}
abstract class test
{
public function __construct()
{
//...
}
public function __destruct()
{
//...
}
public function foo()
{
//..
}
}
?>
Инициализация класса и наследование производятся следующим образом:
<?php
require_once 'abstract.php';
class test
extends mylib::test
implements
mylib::Interface1,
mylib::Interface2
{
public function __construct()
{
//...
}
public function __destruct()
{
//...
}
public function Method_1()
{
//...
}
public function Method_2()
{
//...
}
public function foo()
{
//...
}
}
?>
Как видите, ничего сложного. Самое интересное, что вы можете в разных библиотеках описывать классы с одинаковыми названиями и так же их подключать и производить от них наследование.
<?php
require_once 'abstract1.php';
require_once 'abstract2.php';
class test
implements
mylib1::Interface,
mylib2::Interface
{
...
}
?>
Единственное ограничение – это невозможность наследования от интерфейсов с одинаковыми методами. Но тут без комментариев, вы сами все прекрасно понимаете.
Глобальное пространство и вызов функций внутри модулей
В самом файле, в который происходит подключение наших модулей, можно создавать функции и классы с именами, аналогичными именам в модулях. Правда, если не определено пространство имен, то имена пользовательских идентификаторов не должны пересекаться с зарезервированными именами встроенных функций. В самой библиотеке функций функции можно вызывать напрямую, без каких-либо обращений через пространство имен. Приведу пример:
<?php
namespace myspace;
function foo()
{
echo __NAMESPACE__ ;
}
foo();
// Будет вызвана функция этого неймспейса, описанная выше
?>
Если вы создали какую-то общую библиотеку и хотите, чтобы все функции из нее были доступны во всех объявленных вами пространствах имен, то для этого вы создаете файл без указания неймспейса. В библиотеку помещаете ваши функции. А вот обращение к ним осуществляется через пустой глобальный неймспейс. Лучше всего это пояснить на примере. Допустим, есть файл func.php.
<?php
function foo()
{
echo __FILE__ ;
}
?>
Есть библиотека mylib.php.
<?php
namespace mylib;
function foo()
{
echo __NAMESPACE__ ;
// Здесь мы хотим вызвать функцию foo() из файла func.php
}
?>
Есть некоторый рабочий скрипт index.php.
<?php
include 'func.php';
include 'mylib.php';
?>
Мы при разработке библиотеки использовали функцию foo(), доступную в файле func.php, но в самой библиотеке есть функция с аналогичным названием. Если вы вызовете функцию foo() в файле mylib.php, то будет вызвана функция mylib::foo(), а вам надо вызвать функцию foo() из файла func.php. Для этого существует глобальное пространство имен, по-другому его называют «пустой неймспейс». Вызов производится следующим образом:
<?php
namespace mylib;
function foo()
{
/**
* Тут происходит вызов функции foo() из файла func.php
*/
echo __NAMESPACE__ . ::foo();
}
?>
Работа с константами и классами происходит аналогичным образом. Также, если вы хотите использовать файл непосредственно в библиотеке, то можно включить его прямо в модуль:
<?php
namespace mylib;
include 'func.php';
function foo()
{
/**
* Тут происходит вызов функции foo() из файла func.php
*/
echo __NAMESPACE__ . ::foo();
}
?>
Но не стоит думать, что включаемый файл станет частью модуля mylib.php. Он так и будет играть роль внешнего подключаемого файла, и вызовы нужно будет делать через глобальный неймспейс. Если вы попытаетесь вызвать функцию без указания глобального неймспейса из другого файла, при этом внутри модуля она не будет объявлена, то будет сгенерирована ошибка «Fatal error: Call to undefined function mylib::foo()...», так как интерпретатор будет искать функцию внутри данного модуля.
Если вы включаете один модуль в другой, то это равносильно, как если бы вы подключали их по отдельности.
<?php //myspace.php
namespace myspace;
include 'myspace.php';
function foo()
{
echo __NAMESPACE__ ;
}
?>
<?php //mylib.php
namespace mylib;
include 'myspace.php';
function foo()
{
echo __NAMESPACE__ ;
}
foo(); // Выведет mylib
myspace:: foo() // Так делать нельзя
mylib::myspace:: foo() // Так делать нельзя
?>
<?php
include 'mylib.php';
mylib::foo(); // Выведет mylib
myspace::foo(); // Выведет myspace
?>
Мы подключили только mylib.php, но при этом нам доступны идентификаторы пространства myspace.
Переменные
Что касается переменных, то они не «воспринимают никаких ограничений», и для них не существует никаких пространств имен. Если вы объявили, например, переменную $var = 1, то она будет видна во всех файлах и модулях в пределах видимости, которую допускает PHP.
Пока неясно, будет ли добавлена возможность перекрывать область видимости переменных пространством имен или нет. В доступном мне снапшоте такой возможности не было. Хотя разработчики могут ограничиться тем, что переменные всегда можно упрятать в класс, сделав их статическими. По идее вызов таких переменных не будет отличаться от вызова их пространства имен, разве что придется делать это через имя класса.
<?php
namespace myspace;
class vars
{
public static $var1 = 1;
public static $var2 = 'Test';
public static $var3 = array('a','b','c');
}
?>
Соответственно вызов происходит так:
<?php
require_once 'mylib.php';
var_dump(
myspace::vars:: $var1,
myspace::vars:: $var2,
myspace::vars:: $var3
);
?>
Получается, что сам класс играет роль пространства имен (хотя, по сути, так оно и есть).
Глядя на код, не видно различий, где идет вызов через пространство имен, а где обращение к классу. Может быть, разработчики на это и рассчитывали. Просто сам факт того, что переменные, объявленные внутри модуля, видны вне модуля, наводит на мысль о недоработанности механизма. Например, в С++ переменные также можно «прятать» внутри пространства имен, как и все остальное. Примеры мы рассмотрели в начале статьи.
Ведь объявив переменные внутри пространства имен, например для каких-то служебных целей, мы рассчитываем, что они не будут мешать переменным из других модулей. Хотя идеология построения модулей подразумевает, что модули – это некий набор функций, логически связанных между собой и используемых в проектируемом нами приложении. То есть в самом модуле ничего не должно вызываться и запускаться. Он служит всего лишь контейнером для функций и классов. Вроде бы все рассмотрели из того, что на сегодня доступно.
Итог
Некоторые могут сказать, что на сегодня все, что было описано выше, можно повторить с помощью классов, ведь классы – это тоже пространства имен. Но это неверное утверждение. Если так рассуждать, то любой логический блок – это уже пространство. И так оно и есть. Когда мы объявляем классы, мы создаем пространство, внутри которого все идентификаторы логически связаны между собой и ограничены рамками класса. Но не стоит заблуждаться и путать эти понятия. Многие почему-то не осознают разницу между классами и пространством имен. Под классом подразумевается некая сущность, которая задает некоторое общее поведение для объектов. Таким образом, любой объект может принадлежать или не принадлежать определенному классу, то есть обладать или не обладать поведением, которое данный класс подразумевает. Класс определяет для объекта контракт, то есть правила, с помощью которых с объектом могут работать другие объекты (обычно это делается с помощью определения методов класса). Кроме того, классы могут находиться друг с другом в различных отношениях. В качестве контейнера для ограничения области видимости можно даже использовать функции. Следующий пример наглядно показывает это:
<?
function space_0( $name )
{
function foo( $argv )
{
var_dump( $argv );
}
function bar( $a1, $a2, $a3 )
{
var_dump($a1, $a2, $a3);
}
$argv = func_get_args();
unset($argv[0]);
return call_user_func_array ( $name, $argv );
}
function space_1( $name )
{
function foo( $argv )
{
var_dump( $argv );
}
function bar( $a1, $a2, $a3 )
{
var_dump($a1, $a2, $a3);
}
$argv = func_get_args();
unset($argv[0]);
return call_user_func_array ( $name, $argv );
}
var_dump( space_0('bar', 1,2,3), space_1('bar', 1,2,3) );
?>
Как видите, мы смогли объявить два раза функции с одинаковыми названиями, но так как они спрятаны внутри функций, в свою очередь, каждая из них находится внутри пространства имен, созданного функциями space_0 и space_1 соответственно. Эти функции играют роль контейнеров, ограничивающих область видимости. При этом всем такой способ является всего лишь «костылем». С помощью классов можно повторять то же самое, но в более удобной форме. И все же, как уже было написано выше, классы не предназначены для использования их в качестве пространств имен, в том понимании, в котором подразумевает директива namespace, как и функции. Ведь библиотека может содержать несколько классов. И описывать классы внутри классов – то же самое, что описывать функции внутри функций. Поэтому введение директивы namespace – это не просто расширение языка некоторой надстройкой над уже чем-то существующим, а реальный инструмент, решающий свои конкретные задачи.
Как вы уже сумели убедиться, у PHP-программистов в скором времени появится новый мощный инструмент. Сам факт появления пространств имен свидетельствует о «взрослении» языка. Для тех, кто работал хотя бы раз с С++, данное нововведение не покажется сложным для освоения. Хотя пока все это находится в стадии тестирования, но уже сейчас можно начинать планировать структуры ваших будущих фреймворков и приложений с учетом текущего синтаксиса пространств имен. Также вы заранее планируйте свои библиотеки таким образом, чтобы в них случайно не были созданы функции с зарезервированными словами namespace, import и __NAMESPACE__, иначе вам потом придется патчить ваш код, заменяя данные слова на альтернативные.
Ждем релиза PHP 5.3!
- http://ru2.php.net/manual/ru/language.namespaces.php.
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|