Быстрое создание phar файлов используя Box
Phar
— это аналог jar
из мира Java, но только применительно к PHP. Phar
упаковывает файлы проекта в специальный архив и позволяет легко переносить и устанавливать приложение без манипуляций с самим проектом в виде исполняемой программы.
Phar archives are best characterized as a convenient way to group several files into a single file. As such, a phar archive provides a way to distribute a complete PHP application in a single file and run it from that file without the need to extract it to disk. Additionally, phar archives can be executed by PHP as easily as any other file, both on the commandline and from a web server. Phar is kind of like a thumb drive for PHP applications.
Для создания phar файлов в PHP существует довольно развесистый API, но есть способ проще и удобнее — использовать проект Box.
Проект Box позволяет описать процесс создания phar файла в удобном JSON формате.
Самый простой файл выглядит так:
{
"files": ["src/Put.php"],
"main": "bin/main",
"output": "example.phar",
"stub": true
}
где
files
— файлы, которые должны быть включены в phar
main
— файл, который будет выполнен при вызове phar файла
output
— название итогового phar
файла
stub
— для консольных приложений устанавливается в true
Здесь можно посмотреть процесс создания phar-файла на примере простого проекта.
Здесь содержится список всех возможных параметров box.json
с комментариями.
Попробуем собрать phar-файл на примере реального проекта. В качестве примера возьмем систему миграций баз данных Nasgrate. «Традиционно» данная система устанавливается либо через клонирование репозитория из GitHub, либо через Composer. Мы попробуем сделать отдельную утилиту, которую можно просто скачать и начать использовать вообще ничего не зная про PHP.
Создадим box.json
в корне проекта
{
"chmod": "0755",
"directories": ["src","app"],
"files": ["README.md"],
"main": "bin/nasgrate",
"output": "nasgrate.phar",
"stub": true
}
directories
— в данном параметре содержится список директорий, которые целиком будут включены в проект (в данном случае директория src
и app
).
files
— если нужно включить какие-то конкретные файлы, перечисляем их здесь.
Мы бы могли например переписать box.json следующим образом
{
"chmod": "0755",
"directories": ["src"],
"files": ["app/console.php", "app/index.php", "README.md"],
"main": "bin/nasgrate",
"output": "nasgrate.phar",
"stub": true
}
Помимо этих двух варинтов записи есть еще возможность выборочно включить файлы в проект на основе фильтров. Например если бы в проекте активно использовались внешние библиотеки и они располагались бы в папке vendors
, мы бы хотели включить файлы этих библиотек в проект, но исключить например все тесты. Тогда файл выглядел бы так
{
"chmod": "0755",
"directories": ["src"],
"files": ["app/console.php", "app/index.php", "README.md"],
"finder": [
{
"name": "*.php",
"exclude": [
"tests",
"test"
],
"in": "vendor"
}
],
"main": "bin/nasgrate",
"output": "nasgrate.phar",
"stub": true
}
секция finder
в данном случае говорит: «включи все файлы с расширением *.php
из директрии vendor
кроме папок tests
и test
».
chmod
— позволяет установить права на phar
файл. В данном случае мы ставим 0755
чтобы сделать файл исполняемым.
Остальные парметры были описаны выше.
Наиболее простой (и рекомендуемый) способ установки
$ curl -LSs https://box-project.github.io/box2/installer.php | php
после этого в текущей директории появится box.phar
.
Можно его запускать как php box.phar
, либо назначить права на выполнение chmod 755 box.phar
, переименовать в box mv box.phar box
и перенести в /usr/local/bin
. Тогда его можно будет запускать из любого места просто как box
.
Альтернативная установка через Composer
$ composer global require kherge/box --prefer-source
или добавить его в существующий проект
{
"require-dev": {
"kherge/box": "~2.5"
}
}
Проверим правильность установки
$ box -v
должен вывести описание программы и список возможных опций.
Далее необходимо проверить что в вашем php.ini
файле параметр phar.readonly
стоит в 0
, Off
или false
.
Узнаем где находится наш файл, который актуален именно для консоли (если вы запустите phpinfo (); через Apache, он вам покажет другой php.ini):
$ php -i | grep php.ini
>> Configuration File (php.ini) Path => /usr/local/php5/lib
>> Loaded Configuration File => /usr/local/php5/lib/php.ini
Далее находим параметр phar.readonly
и ставим его в Off
.
[Phar]
; http://php.net/phar.readonly
phar.readonly = Off
Заходим в папку с проектом (на уровень где лежит наш box.json
) и запускаем компиляцию
$ box build -v
Флаг -v
позволяет нам увидеть что происходит в процессе компиляции
Removing previously built Phar...
* Building...
? Output path: /Users/dlevsha/Sites/nasgrate/nasgrate.phar
? Adding directories...
+ /Users/dlevsha/Sites/nasgrate/src/config.php
+ /Users/dlevsha/Sites/nasgrate/src/Driver/Base/Dump.php
+ /Users/dlevsha/Sites/nasgrate/src/Driver/Base/Generator.php
+ /Users/dlevsha/Sites/nasgrate/src/Driver/Base/Helper.php
+ /Users/dlevsha/Sites/nasgrate/src/Driver/Base/Migration.php
+ /Users/dlevsha/Sites/nasgrate/src/Driver/Mysql/Dump.php
+ /Users/dlevsha/Sites/nasgrate/src/Driver/Mysql/Generator.php
+ /Users/dlevsha/Sites/nasgrate/src/Driver/Mysql/Helper.php
+ /Users/dlevsha/Sites/nasgrate/src/Process/Base.php
+ /Users/dlevsha/Sites/nasgrate/src/Process/Console.php
+ /Users/dlevsha/Sites/nasgrate/src/Process/Server.php
+ /Users/dlevsha/Sites/nasgrate/src/template.sql
+ /Users/dlevsha/Sites/nasgrate/src/Util/Console.php
+ /Users/dlevsha/Sites/nasgrate/src/Util/Db.php
+ /Users/dlevsha/Sites/nasgrate/app/console.php
+ /Users/dlevsha/Sites/nasgrate/app/index.php
? Adding files...
+ /Users/dlevsha/Sites/nasgrate/README.md
? Adding main file: /Users/dlevsha/Sites/nasgrate/bin/nasgrate
? Generating new stub...
? Setting file permissions...
* Done.
после этого в директории проекта появится файл nasgrate.phar
.
Проверим что все скомпилировалось нормально
$ ./nasgrate.phar
выведет описание утилиты
Nasgrate is a console utility that let you organise database schema migration process at a consistent and easy way.
It supports mysql, mssql, postgresql, oracle and other databases
Usage:
php nasgrate [command] [options]
Command:
status - displays migration status
generate - creates new migration (migration file)
up:show - displays (but not executes) SQL-query, executed by migration update
...
Особенностью работы phar
является то, что все ресурсы он ищет внутри своего архива. В большинстве случаев это не является проблемой, поскольку именно для этого мы phar
и делали, чтобы он был независимым от окружения. Но есть ряд случаев когда ресурсам внутри phar
архива необходимо работать с внешними файлами.
На примере Nasgrate — необходимо считывать конфигурацию из файла .environment
, писать файлы миграций и файлы состояния во внешнюю директорию.
Если мы попытаемся указать относительный путь внутри проекта, например как ../.environment
, сгенерируется ошибка, поскольку такого файла внутри phar
нет.
Существует два варианта решения данной проблемы:
Первый вариант смапить внешний файл во внутреннее пространство phar
файла.
Phar::mount('phar://.environment', '/etc/nasgrate/.environment');
Проблема в том, что вам нужно точно знать абсолютный путь до файла.
Второй вариант — указывать путь от текущего местоположения phar
файла. Примерно так:
define(DIR_RUN, dirname(Phar::running(false)));
Далее вы либо с помощью Phar: mount смапите его во внутреннее пространство phar
либо просто укажите абсолютный путь до файла конфигурации. Предположим что конфигурационный файл находится в той же папке, что и сам phar
define(DIR_RUN, dirname(Phar::running(false)));
Phar::mount('phar://.environment', DIR_RUN.'/.environment');
и далее обращаетесь к нему как к локальному файлу, либо всегда обращаетесь по абсолюному пути.
Есть одно замечание, не знаю баг это или фича, но все директории подключенные через Phar::mount
находятся в режиме readonly и как это поменять не очень ясно. При обращении по абсолютному пути таких проблем не возникает.
Еще один вроде как очевидный момент, но на который стоит обратить внимание. Phar файл содержит ресурсы вашего проекта и никак не регулирует наличие подключенных библиотек к самому php, его версию и т.д. То есть если вы используете какое-то расширение php, например PDO или вообще что-то установили из PECL, или используете какие-то особенности конкретной версии php, ваш phar не будет содержать информацию о среде выполнения. Если вы используете например traits из PHP 5.4, а у пользователя стоит 5.3 — то будет сгенерирована ошибка. Вы должны внутри своего приложения проверять все требуемые зависимости.
Ссылки по теме: