Только для читателей Lifeexample возможно открыть интернет-магазин на Moguta.CMS со скидкой в 15%

<<< Интеграция PayPal, простой PHP класс || jQuery UI sortable – как узнать новую позицию элементов >>>

Пространство имен PHP (Внедрение в проект)

16.01.2014
Пространство имен PHP (Внедрение в проект)

Здравствуйте, уважаемые читатели блога LifeExample, сегодня мы поговорим о том, как внедрить пространства имен в PHP проект. Вы наверняка сталкивались с ситуацией: подключаете стороннюю библиотеку в проект, а в ней встречаются названия классов, которые вы уже где-то в своем коде используете (например, распространенные названия такие как ‘Logger’ или ‘Model’).

Не всегда получается поправить эти конфликты с пересекающимися именами из-за совместимости резервных файлов или внешних классовых ссылок в базе данных.

Ситуация может быть еще хуже, если вы не можете изменить стороннюю библиотеку, потому что последняя является, например, закрытым источником (по какой либо причине). В таком случае, у вас не получится использовать подобную библиотеку и приходится от нее отказываться.

Сегодня мы познакомимся с механизмом позволяющим легко внедрить пространства имен в PHP проект используя, которые можно быть спокойным за совместимость проекта со сторонними библиотеками.

Что такое пространство имен PHP

Пространство имён (англ. namespace) — некоторое окружение, созданное для группировки уникальных идентификаторов (то есть имён).

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

Внедрение в проект

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

Основная процедура состоит в создании класса псевдонимов при пустом запрашиваемом классе и наличии необходимых названий в найденном классе.

Рассмотрим на примере:

1
2
3
//файл: myclass.php
namespace Scavix\SomeCoolProject;
class MyClass { /* тело класса */ }

Приведенный файл описывает класс MyClass в пространстве имен Scavix\SomeCoolProject.
Стандартный механизм автозагрузки для названий классов выглядит примерно так:

1
2
3
4
5
6
7
8
// Файл: autoloader.php
function system_spl_autoload($class_name)
{
    // используем некоторую функцию для поиска файла с объявляемым классом
    $file = __search_file_for_class($class_name);
    require_once($file);
}
spl_autoload_register("system_spl_autoload",true,true);

Стандартные PHP функции: __search_file_for_class, spl_autoload_register.

В файле index.php подключим автозагрузчик и попробуем инициализировать экземпляр класса MyClass из файла myclass.php, двумя способами.

1
2
3
4
// file: index.php
require_once("autoloader.php");
$mc = new MyClass(); // Выбросит ошибку ‘Class not found’
$mc = new Scavix\SomeCoolProject\MyClass(); // Успешно создаст экземпляр класса

Конечно ошибка ‘Class not found’ здесь умышленная и сделана для примера. Чтобы код работал придется оставить только строку

1
$mc = new Scavix\SomeCoolProject\MyClass();

При наличии такого автозагрузчика придется всем классам проекта задавать пространства имен и вызывать их аналогичным образом.

Согласитесь, если проект объемный, то сразу сделать это без проблем будет крайне не просто. Хотелось бы, чтобы при внедрении пространств имен в проект можно было подключать классы, как обычным методом, так и используя пространство имен.

Давайте расширим автозагрузчик для упрощения внедрения пространства имен PHP. Будем создавать псевдоним класса для предотвращения ошибки ‘Class not found’.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// file: autoloader_ex.php
function system_spl_autoload($class_name)
{
    if( strpos($class_name, "\\") !== false )
    {
        $orig = $class_name;
        $class_name = array_pop(explode("\\",$class_name));
    }
    // используем некоторую функцию для поиска файла с объявляемым классом
    $file = __search_file_for_class($class_name);
   
    // запомним объявленные классы, и подключим файлы
    $pre = get_declared_classes();
    require_once($file);
    $post = array_unique(array_diff(get_declared_classes(), $pre));
   
    // Создадим псевдонимы для новых классов
    foreach( $post as $cd )
    {
        $d = explode("\\",$cd);
        if( count($d) > 1 )
        {
           create_class_alias($cd,array_pop($d));
        }
    }
   
   
//Получим определяемые классы. Предполагается что в одном файле один класс или интерфейс
    $def = array_pop($post);
    if( !isset($orig) && !$def )
    {
        foreach( array_reverse($pre) as $c )
        {
            if( !ends_with($c,$class_name) )
                continue;
           
            create_class_alias($c,$class_name,true);
            break;
        }
    }
    else
    {
        $class_name = isset($orig)?$orig:$class_name;
        if( strtolower($def) != strtolower($class_name) && ends_iwith($def,$class_name) )
         {
            create_class_alias($def,$class_name,true);
        }
    }
}
spl_autoload_register("system_spl_autoload",true,true);

function create_class_alias($original,$alias,$strong=false)
{
    if( $strong )
        class_alias($original,$alias);
   
    // Сохраняет алиас в глобальные переменны
    $alias = strtolower($alias);
    if( isset($GLOBALS['system_class_alias'][$alias]) )
    {
        if( $GLOBALS['system_class_alias'][$alias] == $original )
            return;
       
        if( !is_array($GLOBALS['system_class_alias'][$alias]) )
            $GLOBALS['system_class_alias'][$alias] = array($GLOBALS['system_class_alias'][$alias]);
        $GLOBALS['system_class_alias'][$alias][] = $original;
    }
    else
        $GLOBALS['system_class_alias'][$alias] = $original;
}

Стандартные PHP функции: __search_file_for_class, spl_autoload_register, get_declared_classes, ends_with, ends_iwith, class_alias.

Немого изменим файл index.php:

1
2
3
4
// file: index.php
require_once("autoloader_ex.php");
$mc = new MyClass(); // OK
$mc = new Scavix\SomeCoolProject\MyClass(); // OK

Теперь, благодаря расширенной функции автозагрузчика классов можно постепенно переносить свой проект для использования пространств имен в PHP, без страха положить проект.

Когда применять пространства имен в PHP

Как вы могли заметить, пространство имен – это полезная штука! Но нужно применять его правильно и использовать только там где необходимо. Поэтому следуйте следующим правилам:

  • Используйте уникальный корень для пространства имен ;
  • Используйте только одно пространство имен в каждом файле;
  • Проверьте наличие в пространстве имен каждого класса/интерфейса;
  • Поставьте каждый класс/интерфейс в собственный файл и ничего больше туда не добавляйте;
  • Не добавляйте функции в пространство имен.

Спасибо за внимание, надеюсь данный материал будет вам полезен если вы захотите внедрить в ваш проект пространство имен PHP.

Чтобы не пропустить публикацию следующей статьи подписывайтесь на рассылку по E-mail или RSS ленту блога.

Нравится

Комментарии

  • Дмитрий

    Неужели трудно было снадбить урок простейшими примерами?
    А то для людей типа меня с конкретным типом мышления, этот урок не принес пользы, но вызвал чувство досады на свою бестолковость.

    • Статья рассчитана на людей имеющих представление о работе пространствами имен. Ключевой смысл статьи не в том чтобы объяснить механизм работы с пространством имен в PHP, а в том как внедрять их в проект. Возможно в будущем я напишу статью именно о том, что такое пространство имен в PHP и как этим пользоваться.

  • Taras

    зачем писать: «Стандартные PHP функции: __search_file_for_class»?
    Я не нашел такой функции.Я так понял,что там должна быть какай то пользовательская функция,которая находит файл с нужным классом.

  • Оставить комментарий

    Подписаться на комментарии к этой статье по RSS

    Яндекс.Метрика