суббота, 28 августа 2010 г.

Поддержка PHP 5.3 Namespaces для Zend Framework 1.10

Введение

Этот пост посвящен продолжению разработки CMF на базе Zend Framework 1.10.x. 

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


Немного теории
Я уже упоминал в одной из статей, что Zend Framework 1.10 поддерживает автоматическую загрузку классов на нэймспейсах 5.3. Да это действительно так, но проблема возникла когда я собрался подключить классы не из библиотеки, а из каталога приложения. Чтобы было понятней приведу структуру каталогов на которой я остановился.
    <project name>/
        application/
            configs/
                application.ini
            modules/
                <module name>
                    configs/
                    controllers/
                        helpers/
                    lang/
                    models/
                        Domain/
                        forms/
                        services/
                    Bootstrap.php
            view/
                default/
                    <module name>
                        forms/
                        filters/
                        helpers/
                        scripts/
                    layouts/
                        main.phtml
            Bootstrap.php
        data/
        docs/
        library/
        public/
        tools/
        tests/
Отличие от стандартной зендовской структуры в том, что я вынес все представление в отдельный каталог, это сделано для того, чтобы легче было изменять дизайн, создавать различные темы и т.д. Так же решил отказаться от каталога с глобальными моделями, вместо этого будет модуль "Core".

Ну так вот, для поддержания своих структур каталогов в ZF есть специальный загрузчик Zend_Loader_Autoloader_Resource и его наследник Zend_Application_Module_Autoloader, который по-умолчанию регистрирует пространства имен для приложения при загрузке. Проблема в том, что этот загрузчик не поддерживает нэймспесы PHP 5.3.

Development

Прежде чем приступить к разработке, сформулируем задачу.
Наш загрузчик должен:
  • поддерживать PHP 5.3 namespaces
  • поддерживать префиксы "_"
  • поддерживать стандартные Zend и ZendX библиотеки
  • поддерживать свои библиотеки
  • поддерживать нестандартное отображение имени класса на структуру  каталогов
Собственно, я решил заменить одним своим загрузчиком стандартные Zend_Loader_Autoloader и Zend_Loader_Autoloader_Resource.

Разработку естественно я начал с тестов, для этого я скопипастил тесты из Zend, убрал лишнее и добавил тесты нэймспесов. Код  тестового класса можно посмотреть на гитхабе.

Соответственно, параллельно писал и загрузчик окончательный вариант можно так же посмотреть на гихабе

Загрузчик похож на вариант Matthew Weier O'Phinney описанный в его заметке про сравнение производительности загрузчиков. Единственно что, я не стал делить загрузку на нэймспейсы и префиксы, у меня все в одной куче, ну и плюс добавлена поддержка суффиксов и пути.

Как использовать

Регистрация загрузчика в spl_autoload происходит автоматически при его инстанцировании. Вызывать лучше после создания Zend_Application, но перед запуском бутстрапа, что бы оказаться выше стандартного зендовского загрузчика:
    $application = new Zend_Application(
        APPLICATION_ENV,
        APPLICATION_PATH . '/configs/application.ini'
    );

    require_once 'Xboom/Loader/Autoloader.php';
    $autoloader = Xboom\Loader\Autoloader::getInstance();

    $application->bootstrap()
                ->run();
При этом  так же регистрируются библиотеки Zend и ZendX из include_path.

Настройку своих библиотек можно уже делать либо в ресурс-методах, либо в ресурс-плагинах.
Привожу пример моей настройки в ресурс-методе.
    protected function _initAutoload()
    {
        $autoloader = Xboom\Loader\Autoloader::getInstance();
        $autoloader->registerNamespace($this->getOption('autoloaderNamespaces'));
        $nsSuffix = $this->getOption('appnamespace');
        if (!empty($nsSuffix))
        {
            $nsSuffix .= '\\';
        }
        $autoloader->registerNamespace(array(
            $nsSuffix . 'Core' => APPLICATION_PATH . '/modules/core',
            $nsSuffix . 'Core\\Model' => APPLICATION_PATH . '/modules/core/models',
            $nsSuffix . 'Core\\Service' => APPLICATION_PATH . '/modules/core/models/services',
            )
        );
    }
Здесь используются стандартные настройки из конфигурационного файла, а так же регистрируются пространства имен для модуля "core". Т.к. загрузчик сделан как Singleton, то аналогично можно регистрировать дополнительные пространства имен для других модулей в загрузчике этих модулей.

Метод registerNamespace(array|string $namespace, string $directory = '') - регистрирует простаранство имен и начальный каталог, если каталог не указан, то считается что классы начинающиеся с этого пространства имен находятся в include_path. Если в качестве первого параметра передан массив, то можно зарегистрировать несколько пространств имен. Используется следующий формат namespace => directory, т.е. ключ это пространстов имен, значение каталог.

Вместо заключения

До выхода Zend Framework второй версии еще прилично времени, а использовать фишки PHP 5.3 хочется уже сейчас. К тому же, нет смысла городить огород с использованием стандартных Зендовских загрузчиков, тем более они морально устарели. К моменту написания статьи я уже перевел свой код на нэймспесы, загрузчик не подвел.
Следить за проектом вы можете на гитхабе.
В ближайших планах реализовать валидацию данных.

3 комментария:

  1. Пока это задумано как CMF, а не CMS. Создается для облегчения разработки новых проектов, так сказать взять этот каркас, обвесить доп. функционалом и получить готовый продукт.

    По большому счету Zend Framework хоть и предлагает множество готовых компонентов, связать их вместе не такая уж тривиальная задача. Тут я стараюсь связать все компоненты, при этом пытаюсь использовать лучшие практики для достижения надежного, гибкого, удобного, простого и т.д. каркаса для быстрой разработки на его основе веб-приложений различного уровня.

    Ну и плюс конечно, это обучение. По большему счету многие вещи для меня новы.

    ОтветитьУдалить
  2. Спасибо за интересную статью! Все очень понятно описано. Конечно хочется чтобы поддержка namespace была включена в очередной резлиз ZF. Видимо ждать еще долго)). В своих проектах создавать такого плана автозагрузчики не хотелось бы, по скольку полностью воспроизвести подобие стандартного автозагрузчика очень сложно. В свзяи с чем вопрос - каким образом можно применить неймспейсы исключительно на модели, формы например? Т.е. на все исключая самой зендовской библиотеки.

    ОтветитьУдалить