Упаковка 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.