Упаковка Zend Framework 2 в PHAR архив

В этой статье я хочу поближе познакомить вас с замечательной фичей – Phar-архивами. В предыдущем посте я упоминал о ней, а много раньше даже писал обзорную статью по Phar. Целью этого поста будет полная упаковка Zend Framework 2 в один архив, чтобы раз и навсегда исключить проблему инклюдов в ZF веб-приложениях.В последнее время я слышу слишком много флуда, что “PHP уже не торт, вот в Ruby, там да…”. Или “на PHP кодят только школьники”, “Enterprise приложение на PHP не сделаешь”, “ZF – тормозной фреймворк” ну и т.д.  А ведь проблемы то растут не из технологий. Хотя PHP имеет груз обратной совместимости, и ZF1 объективно из коробки показывает не высокую производительность. Однако, при использовании драйвера “прямые руки” можно спокойно и без нервов переписать тормозящий участок кода, работать через ORM, оптимизировать код и т.д. Но многим людям проще обвинить PHP, чем заняться делом. Это печально 🙂

В этом посте я хочу рассказать об устранении одной из проблем ZF1 -огромного количество инклюдов (и как следствие файловых операций), а именно компиляция приложения в PHAR архив. Не скажу, что технология компиляции php кода в один файл новая. Раньше были попытки сделать это, но получалось прямо скажем не всегда. Приходилось в полуавтоматическом режиме вырезать require_once() из кода,  а в некоторых местах ZF1 он был нужен, писались исключения. В общем этот подход лично я решил не применять, а ограничился установкой APC байткод-кэшера.

Однако с выходом php 5.3.0 появилась замечательная возможность использовать обертку потока phar://. Теперь можно повторить эксперимент на новом уровне.

Сразу оговорюсь, что можно точно также сжать и Zend Framework 1, есть всего-лишь одно небольшое отличие и я расскажу о нём чуть ниже. Для знакомства с теорией рекомендую почитать вот эту статью на Хабре. Для упаковки ZF2 в Phar нам понадобится несколько компонентов:

  1. Собственно сам ZF2. Скачать последнюю версию можно с офсайта или из Git репозитария.
  2. Файл-загрузчик для Phar-архива (stub.php) – заглушка. Получает управления сразу после инклюда файла с архивом. Будет ниже.
  3. Упаковщик package.php. Будет ниже.

Stub.php для Zend Framework 1.x

Stub.php для Zend Framework 2.x

Package.php

Теперь надо правильно расположить файлы. В каталоге вашего проекта создайте папку ./vendor/ZendFramework/library Поместите туда эти файлы (stub.php, package.php) и папку Zend с самим фреймворком. Такое расположение папок характерно для ZF2. Если у вас в проекте другое, то придётся подправить пути. Дальше запускайте следующую команду для компиляции ZF в один файл.

Чтобы убрать эту ошибку надо разрешить запись в phar-архивы в php.ini:

Теперь весь ZF упакован в один файл:

Отлично, теперь в каталоге ./vendor/ZendFramework/library/ у вас должен появиться файл zf.phar. А значит можно подключать скомпиленный ZF к проекту.

test_phar.php. Подключение для ZF2:

test_phar.php Подключение для ZF1:

Однако при запуске test_phar.php отображается пустой экран. При этом в логи PHP ничего не пишет. Прогуглив как следует этот вопрос, я выяснил, что это происходит из-за  Suhosin patch. Для того, чтобы Phar архивы читались нормально, если в системе стоит suhosin patch, вам надо прописать в /etc/php5/conf.d/suhosin.ini (или в php.ini) следующее:

После этого скрипт должен вывести версию ZF, подключённого через phar архив.

Да, теперь весь ZF можно подключить с помощью одного include! Вы кстати можете модифицировать package.php для своего проекта и вообще залить всё приложение (со всеми библиотеками в один файл). А для хостингов вообще можно красиво сделать. Компилируем php-фреймворки или CMS’ки в phar, включаем APC, и один экземпляр фреймворка в памяти шарится для всех клиентов.

Кэширование APC и PHAR

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

Без использования PHAR

Теперь для чистоты экспериментов очистим кэш.

Сделаем include PHAR архива и посмотрим кэш.

А потом ещё раз include.

О чём это говорит? При include конкретного файла класса он сначала берется из кэша, и если его там нет, то из Phar архива, который тоже висит в памяти, т.е. также кэшируется. В итоге дисковых операций не происходит.

Бонусы

В этом посте будет пара бонусов, а именно скомпиленные в PHAR фреймворки Zend Framework 1 и Zend Framework 2.
zf_1_11_0_dev.phar.tar.gz (зеркало)
zf_2_0_0beta1.phar.tar.gz (зеркало)
Скачать всё с GitHub

Подключаются они обычным инклюдом:

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

Ссылки

UPD

Обязательно нужно убрать саму папку library/Zend из проекта, иначе идет передекларирование классов, т.е. ты все классы подключил через phar, а Loader делает include_once например, и он инклудит физический файл, но класс уже был объявлен в phar и выходит ошибка. Или же проверять загружен ли класс или нет перед include_once.

23 Comments

  1. как же я раньше не додумался сделать этого 🙂
    отличная идея и реализация!

  2. Очень интересно.
    А ты не делал какие-нибудь тесты чтобы сравнить производительность?
    Хотелось бы увидеть графики “до и после”.

  3. Ну а где же сравнение производительности? Может оно и не стоит того совсем.

      1. Можешь посоветовать какие-нибудь утилиты для просмотра нагрузки на сервер. У нас сайт падает…перестают инклудится файлы, а нагрузку не могу определить

        1. Во-первых посмотри логи mysql slow query log, найдёшь там тормзные запросы. Во вторых сделай банальные top и ps -aux и посмотри, кто у тебя там самый прожорливый. Обычно это либо mysql, либо php. В случае с mysql надо будет оптимизировать запросы, расставить индексы, смотреть на планы запросов и т.д. В случае с php – надо смотреть какие именно странички долго грузятся / потребляют память или cpu. Тут можно оптимизировать алгоритм обработки, заняться кэшированием, и кэшировать результаты обработки запросов / блоки на сайте / страницы целиком в зависимости от обстоятельств. Можешь мне на почту написать – пообщаемся на эту тему.

  4. Я так понимаю это все же продакшин версия сайта? Как IDE относятся к phar?

    1. Конечно только для продакшена. IDE ничего подсказывать не будет, если юзается только phar. Так это же не проблема по-моему, встроить в деплоилку компиляцию phar архива.

      1. Eclipse и Zend Studio позволяют подключать phar архив к проекту как библиотеку, так что подсказки будут

  5. Мне непонятен один момент:
    в stub.php вы просто устанавливаете autoload для классов Zend, но, по умолчанию, Zend содержит в себе директивы include_once, т. е. в любом случае зависимости будут пытаться грузится через include_path.

    Мне кажется, что стоит в stub.php добавить что-то вроде

    Phar::mapPhar(‘zf.phar’);

    set_include_path(‘phar://zf.phar’ . PATH_SEPARATOR . get_include_path());

    Zend_Loader_Autoloader::setZfPath() здесь не сработает, так как в нём есть проверка на то, что путь – это директория 🙂

  6. В общем попробовал использовать ZF через phar. Результат разочаровал: APC – 50 ms, APC + phar – 175 ms. Мерял xdebug, приложение сферическое – статья + комменты.
    Кстати, до кучи проверил и Yaf – 5 ms!

    1. Неплохой сервис. Я раньше юзал похожий, но потом отказался, т.к. при использовании нового класса надо было перезакачивать архив.

  7. К сожалению, как показала практика, APC не кэширует файлы полученные по протоколу phar://… Соответствующий тикет открыт на их сайте: https://bugs.php.net/bug.php?id=59398

    А тот файл, что виден в табличке APC – это кэширование stub части phar архива. На это намекает его размер в памяти.

    Так что ждём исправление работы APC с обёртками потоков. Другие opcode кэшеры имеют ту же проблему.

  8. Всем привет.
    Не знаю что делать с ZFTool для ZF2
    Помогите кто может. Я его установил как модуль в папку \vendor\zendframework\zftool и в config/application.config.php дописал этот модуль. Потом поставил phpunit и он мне сказал что нельзя проинициализировать zftool. В path windows 7 я вписал …zftool/bin. Открываю cmd windows и пишу zftool.phar и вижу алерт с текстом не могу открыть файл. если пишу php zftool.phar на экране вижу все доступные команды(те все ок). Потом решил увидеть тесты на екране проекта и пошел в модуль zftool и расскоментировал все в файле zftool/config/global.php и по идее если набрать site/diagnostics должен быть отчет но у меня 404 ошибка. И с командной строки пытаюсь вызвать все команды( php public/index.php diag -v …) и ничего кроме версии php.
    Буду очень благодарен за любую инфу. спасибо

Leave a Comment