PHAR – упаковка PHP-приложений

В ближайшее время должна быть выпущена версия PHP V5.3. Cерия статей Что нового в PHP V5.3” посвящена новым замечательным функциям, появившимся в этой версии. В части 1 рассматриваются изменения в версии PHP V5.3, относящиеся к объектно-ориентированному программированию и управлению объектами, в части 2 – лямбда-функции и замыкания, а в части 3 мы говорили о пространстве имен, одной из самых долгожданных и обсуждаемых особенностей этой версии РНР. Здесь, в части 4, мы рассмотрим формат архивов Phar, который можно использовать внутри РНР. Он применяется не только для архивирования файлов, но и для распространения и исполнения целого приложения РНР, упакованного в один файл. Его можно использовать с РНР как расширение репозитория PECL, но в будущей версии V5.3 этот формат становится официальным расширением РНР.

Идея архивов Phar заимствована из технологии Java™ с ее архивами JAR, которые позволяют упаковать приложение в один файл, где есть все необходимое для работы этого приложения. Она отличается от концепции единственного исполняемого файла, который обычно создается такими языками программирования, как С, так как это не скомпилированное приложение, а просто архив. Так что файл JAR на самом деле содержит файлы, составляющие приложение, хотя по причинам безопасности они могут быть скрыты. Расширение Phar основано на аналогичной концепции, но больше ориентировано на веб-среду РНР. Кроме того, в отличие от JAR, архивы Phar могут обрабатываться самим РНР и не требуют внешнего инструмента для своего создания или использования.

Расширение Phar не является чем-то новым для РНР. Оно изначально было написано в РНР и известно как PHP_Archive, а в 2005 году было добавлено в репозиторий PEAR. Однако “чистое” РНР-решение этой задачи в реальной жизни работает довольно медленно, поэтому в 2007 году его переписали на С. В то же время была добавлена поддержка использования объекта SPL ArrayAccess для итерационной обработки архивов Phar. С тех пор была проведена определенная работа по повышению быстродействия архивов Phar.

Создание Phar

Для создания файла Phar нужно выполнить несколько шагов. Все они требуют исполнения команд РНР в той или иной форме, так как самостоятельных инструментов для создания таких архивов не существует. Кроме того, чтобы создать и модифицировать файлы Phar, нужно установить в 0 параметр php.ini phar.readonly. Для открытия или вызова файлов внутри архива Phar в РНР этого не требуется.

Рассмотрим шаги, необходимые для создания архива Phar, который можно использовать для исполнения приложения. Это приложение должно загружаться прямо из веб-браузера или из командной строки. Первым шагом будет создание файла Phar. Сделаем это, создав объект Phar, с которым мы будем работать в данном примере (листинг 1). Обращение к этому объекту позволит нам управлять всеми аспектами архива Phar.
Листинг 1. Создание объекта Phar

Первый параметр конструктора – это путь, по которому должен быть сохранен файл Phar. Второй параметр передает любые параметры в родительский класс RecursiveDirectoryIterator. Третий параметр — это псевдоним, по которому происходит обращение к этому архиву Phar в контексте потоков (streams). Так, возвращаясь к листингу 1, к файлу из этого архива Phar можно обращаться также как к phar://my.phar. Еще можно сделать вызов метода Phar::startBuffering(), чтобы сохранить в буфере изменения, произошедшие в архиве до подачи команды Phar::stopBuffering(). Это не обязательно, но таким способом можно ускорить процесс создания или модификации архива, так как не нужно будет сохранять изменения всякий раз при редактировании сценария.

По умолчанию созданный архив Phar будет использовать собственный формат архива Phar. Но можно использовать и формат ZIP или TAR, преобразовав файл Phar в этот формат. В листинге 2 показано изменение формата на ZIP.
Листинг 2. Изменение формата хранения на ZIP

Изменение формата архива имеет свои преимущества и недостатки. Главное преимущество заключается в том, что содержимое архива можно просматривать при помощи любого инструмента для работы с файлами ZIP или TAR. Однако при использовании архива Phar в форматах ZIP или TAR требуется расширение Phar, а при использовании архива Phar в собственном формате Phar можно обойтись без его загрузки.

Теперь нужно определить загрузчик файла, то есть первый код, вызываемый при загрузке файла Phar.

Загрузчик файла Phar

Загрузчик (stub) Phar-файла — это просто небольшой сегмент кода, который исполняется сразу после загрузки файла Phar. Он всегда оканчивается признаком __HALT_COMPILER(). Пример типичного загрузчика файла приведен в листинге 3.
Листинг 3. Загрузчик файла Phar

Обращение к методу Phar::mapPhar() инициализирует архив Phar путем считывания manifest-файла. Это нужно сделать до ссылки на внутренние файлы архива посредством оболочки (stream wrapper) phar://. Первым будет загружаться тот файл, который данное приложение обычно загружает первым; в данном случае: index.php.

Способ добавления загрузчика в архив Phar зависит от используемого формата архива. Для архивов формата Phar применяется метод Phar::setStub(), который принимает один параметр кода РНР для помещения в загрузчик в виде строки. Этот подход иллюстрируется в листинге 4.
Листинг 4. Использование Phar::setStub() для создания загрузчика файла

Если вы не планируете ничего делать в загрузчике, кроме переадресации на страницу index.php, для его создания можно использовать метод helper Phar::createDefaultStub(). Для этого достаточно просто передать имя файла, которое нужно включить в загрузчик файла. В листинге 5 вызов метода Phar::setStub() переписан с применением этого метода.
Листинг 5. Использование Phar::createDefaultStub() для создания загрузчика файла

Второй необязательный аргумент метода Phar::createDefaultStub() позволяет включить другой файл, если Phar загружается из веб-сервера. Это удобно, когда приложение должно работать и в командной строке, и в контексте веб-браузера.

В реализациях на базе ZIP и TAR содержимое загрузчика хранится в файле.phar/stub.php, и команда setStub() не используется.

Добавление файлов в архив

Объект Phar использует объект SPL ArrayAccess, позволяющий обращаться к содержимому архива как к массиву. Это открывает массу возможностей для добавления файлов в архив. Простейший способ — прямое использование интерфейса ArrayAccess.
Листинг 6. Добавление файлов в архив

Из листинга 6 видно, что имя файла определяется как ключ массива, а содержание — как его значение. Чтобы извлечь содержимое существующего файла для использования в качестве значения, можно прибегнуть к функции file_get_contents(). Это обеспечивает некоторую гибкость при добавлении файлов в архив, позволяя ссылаться на существующий файл или создавать файл динамически. Последнее удобно в сценариях сборки приложения.

Если в архиве Phar нужно сохранить крупный файл, его можно сжать при помощи gzip или bzip2 соответственно с применением методов PharFileInfo::setCompressedGZ() или PharFileInfo::setCompressedBZIP2(). В листинге 7 производится сжатие файла при помощи bzip2.
Листинг 7. Сжатие файла в архиве Phar при помощи bzip2

Чтобы сжать файл или динамически использовать архив со сжатым файлом, нужно, чтобы при установке PHP было разрешено расширение bzip2 или zlib (для файлов, сжимаемых посредством gz).

Предположим, что в архив нужно добавить много файлов. Добавление их один за другим при помощи интерфейса ArrayAccess может оказаться утомительным делом, но есть несколько способов сократить этот путь. Один из них — применение метода Phar::buildFromDirectory(), который просматривает определенный каталог и добавляет все находящиеся в нем файлы. Он поддерживает также фильтрацию файлов путем добавления второго параметра с шаблоном в виде регулярного выражения для отбора файлов и добавления их в архив (листинг 8).
Листинг 8. Добавление файлов в архив при помощи Phar::buildFromDirectory()

В листинге 8 в архив Phar добавляются все файлы РНР из заданного каталога. Затем, если надо внести в добавленные файлы какие-то изменения, например, сжать их, можно еще раз пройти по архиву при помощи интерфейса ArrayAccess.

Кроме того, можно использовать метод Phar::buildFromIterator(), позволяющий добавить файлы при помощи итератора. Поддерживаются итераторы двух типов: те что отображают имя файла, находящегося внутри Phar, на имя файла на диске и те, что возвращают объекты SplFileInfo. Одним из таких совместимых итераторов является RecursiveDirectoryIterator, который используется в листинге 9 для добавления в архив файлов из каталога.
Листинг 9. Добавление файлов в архив при помощи Phar::buildFromIterator()

Метод Phar::buildFromIterator() принимает в качестве единственного аргумента сам объект итератора. В приведенном выше примере объект RecursiveDirectoryIterator упакован в объект RecursiveIteratorIterator, причем последний представляет собой совместимый тип итератора, который требуется методу Phar::buildFromIterator().

Итак, мы создали архив Phar, который можно применять в любом приложении РНР. Теперь посмотрим, как использовать этот архив.

Работа с архивами Phar

Достоинство архивов Phar состоит в том, что их можно легко интегрировать в приложение, особенно при использовании собственного формата архива на базе Phar. В этом случае даже не нужно устанавливать расширение Phar, так как РНР сам умеет загружать такие файлы и извлекать их содержимое. Для загрузки ZIP- и TAR-архивов требуется расширение Phar.

Архивы Phar можно включать в приложение как любой другой файл РНР, что делает их использование предельно простым для разработчиков, которые уже знают, как включить в приложение чужой код. Продемонстрируем, как легко интегрировать Phar в приложениt

Интеграция кода из архива Phar в приложение

Простейший способ интегрировать код из архива Phar – просто включить в него этот архив, а затем ввести в архив файл, который нужно использовать. Для доступа к файлу внутри загруженного архива Phar можно использовать stream-оболочку phar:// (листинг 10).
Листинг 10. Загрузка кода из архива Phar

Первый оператор include загружает архив myphar.phar, включая код, указанный в загрузчике файла. Второй с помощью stream-оболочки открывает архив Phar и включает в архив только указанный файл. Заметим, что, как видно из листинга 10, для включения файла, находящегося в архиве, не обязательно применять include к самому архиву Phar.

Исполнение РНР-приложения из архива Phar

Одна из замечательных возможностей архивов Phar – это упаковка в архив Phar целого приложения и его распространение в таком виде. Преимущество этого подхода заключается в том, что он облегчает развертывание приложений, причем без ущерба для производительности — благодаря нескольким усовершенствованиям, внесенным в PHP V5.3. Однако при проектировании приложения для работы внутри Phar нужно учитывать несколько моментов:

  1. Любые файлы, которые могут понадобиться для отдельного экземпляра приложения, такие как файлы config, не должны входить в состав архива, так что их нужно записать в отдельное, но доступное место. То же относится к любым кэш-файлам, создаваемым приложением в рамках расширения.
  2. Для максимальной переносимости следует придерживаться формата архива на базе Phar без сжатия файлов в архиве. ZIP- и TAR-архивы требуют установки в РНР расширения Phar, тогда как Phar-архивы можно использовать, не задавая расширение Phar.
  3. Любые ссылки на файлы внутри приложения следует заменить с использованием stream-оболочки phar:// с именем архива, как показано в предыдущем разделе.

Одно из популярных РНР-приложений, упакованных в Phar для иллюстрации того, как легко работать с архивами Phar, — PHPMyAdmin (см. Ресурсы). Оно целиком разработано для исполнения из файла архива Phar, однако позволяет хранить свой файл конфигурации вне архива Phar.

Заключение

Архивы Phar — весьма полезное дополнение к PHP V5.3. Они позволяют упаковать код РНР, что удобно для распространения приложений или библиотек в одном файле. Архив Phar можно легко загрузить из файла РНР при помощи функции require или include, либо выполнить прямо из браузера или командной строки, указав имя архива Phar.

Оригинал статьи на сайте IBM

Leave a Comment