PHP 8.4.1 Released!

Использование Phar-архивов: Введение

Концептуально Phar-архивы аналогичны JAR-архивам Java, но учитывают нужды и гибкость PHP-приложений. Phar-архив используется для распространения законченного PHP-приложения или библиотеки в виде одного файла. Приложение, имеющее вид Phar-архива, используется в точности так же, как и любое другое PHP-приложение:

php coolapplication.phar

Использование библиотеки, имеющей вид Phar-архива, идентично использованию любой другой PHP-библиотеки:

<?php
include 'coollibrary.phar';
?>

Обёртка потока phar представляет собой основу модуля phar, про её использование подробно написано здесь. Обёртка потока phar предоставляет доступ к файлам внутри phar-архива с использованием стандартных файловых функции PHP: fopen(), opendir() и других, которые работают с обычными файлами. Обёртка потока phar поддерживает все операции чтения/записи как над файлами, так и над каталогами.

<?php
include 'phar://coollibrary.phar/internal/file.php';
header('Content-type: image/jpeg');
// доступ к phar-архивам может осуществляться по полному пути или с помощью псевдонима
echo file_get_contents('phar:///полный/путь/к/coollibrary.phar/images/wow.jpg');
?>

Класс Phar реализует расширенные возможности по доступу к файлам и по созданию phar-архивов. Использование класса Phar подробно описано здесь.

<?php
try {
// открыть существующий phar-архив
$p = new Phar('coollibrary.phar', 0);
// Phar наследует SPL-класс DirectoryIterator
foreach (new RecursiveIteratorIterator($p) as $file) {
// $file является объектом класса PharFileInfo, который наследует SplFileInfo
echo $file->getFileName() . "\n";
echo
file_get_contents($file->getPathName()) . "\n"; // отображает содержимое;
}
if (isset(
$p['internal/file.php'])) {
var_dump($p['internal/file.php']->getMetadata());
}

// создать новый phar-архив - параметр phar.readonly в php.ini должен быть 0
// phar.readonly включён по умолчанию из соображений безопасности.
// На работающих серверах phar-архивы никогда не должны создаваться,
// а только выполняться.
if (Phar::canWrite()) {
$p = new Phar('newphar.tar.phar', 0, 'newphar.tar.phar');
// создать phar-архив, основанный на tar, сжатый gzip-сжатием (.tar.gz)
$p = $p->convertToExecutable(Phar::TAR, Phar::GZ);

// создать транзакцию - в newphar.phar ничего не будет записано
// до тех пор, пока не будет вызван stopBuffering(), однако для этого требуется временное хранилище
$p->startBuffering();
// добавить все файлы в каталоге /путь/к/проекту/project, сохранение в phar-архив с префиксом "project"
$p->buildFromIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('/путь/к/проекту/project')), '/путь/к/проекту/');

// добавить новый файл используя ArrayAccess
$p['file1.txt'] = 'Информация';
$fp = fopen('hugefile.dat', 'rb');
// скопировать все данные из потока
$p['data/hugefile.dat'] = $fp;

if (
Phar::canCompress(Phar::GZ)) {
$p['data/hugefile.dat']->compress(Phar::GZ);
}

$p['images/wow.jpg'] = file_get_contents('images/wow.jpg');
// любое значение может быть сохранено в качестве метаданных файла
$p['images/wow.jpg']->setMetadata(array('mime-type' => 'image/jpeg'));
$p['index.php'] = file_get_contents('index.php');
$p->setMetadata(array('bootstrap' => 'index.php'));

// сохранить phar-архив на диск
$p->stopBuffering();
}
} catch (
Exception $e) {
echo
'Невозможно открыть Phar: ', $e;
}
?>

Кроме того, проверка содержимого phar-файла может быть осуществлена с помощью любого из поддерживаемых симметричных алгоритмов хеширования (MD5, SHA1, SHA256 и SHA512, если ext/hash включён), а также с помощью подписывания асимметричными открытым/закрытым ключами, используя OpenSSL. Для того чтобы использовать подписывание OpenSSL, вам необходимо сгенерировать пару из открытого и закрытого ключей и установить закрытый ключ для подписывания, используя Phar::setSignatureAlgorithm(). Кроме того, открытый ключ, извлечённый при помощи этого кода:

<?php
$public
= openssl_get_publickey(file_get_contents('private.pem'));
$pkey = '';
openssl_pkey_export($public, $pkey);
?>
должен быть сохранён рядом с phar-архивом, для проверки которого он используется. Если phar-архив сохранён как /путь/к/моему/архиву/my.phar, то открытый ключ должен быть сохранён как /путь/к/моему/архиву/my.phar.pubkey, иначе phar не сможет проверить подлинность подписи OpenSSL.

Класс Phar также предоставляет 3 статических метода: Phar::webPhar(), Phar::mungServer() и Phar::interceptFileFuncs(), которые имеют решающее значение для упаковки PHP-приложений, предназначенных для использования на обычных файловых системах и для веб-приложений. Phar::webPhar() реализует фронтальный контроллер, который направляет HTTP-вызовы в правильное место внутри phar-архива. Phar::mungServer() используется для изменения значений массива $_SERVER, что позволяет обмануть приложения, обрабатывающие эти значения. Phar::interceptFileFuncs() инструктирует Phar о необходимости перехвата вызовов fopen(), file_get_contents(), opendir() и прочих функций, основанных на stat (file_exists(), is_readable() и так далее) и перенаправления всех относительных путей внутрь phar-архива.

Например, для упаковки выпуска популярного приложения phpMyAdmin для его использования в качестве phar-архива, требуется только этот простой скрипт, а phpMyAdmin.phar.tar.php будет доступен как обычный файл на вашем веб-сервере после изменения значений user/password:

<?php
@unlink('phpMyAdmin.phar.tar.php');
copy('phpMyAdmin-2.11.3-english.tar.gz', 'phpMyAdmin.phar.tar.php');
$a = new Phar('phpMyAdmin.phar.tar.php');
$a->startBuffering();
$a["phpMyAdmin-2.11.3-english/config.inc.php"] = '<?php
/* Конфигурация сервера */
$i = 0;

/* Сервер localhost (config:root) [1] */
$i++;
$cfg[\'Servers\'][$i][\'host\'] = \'localhost\';
$cfg[\'Servers\'][$i][\'extension\'] = \'mysqli\';
$cfg[\'Servers\'][$i][\'connect_type\'] = \'tcp\';
$cfg[\'Servers\'][$i][\'compress\'] = false;
$cfg[\'Servers\'][$i][\'auth_type\'] = \'config\';
$cfg[\'Servers\'][$i][\'user\'] = \'root\';
$cfg[\'Servers\'][$i][\'password\'] = \'\';


/* Конец конфигурации сервера */
if (strpos(PHP_OS, \'WIN\') !== false) {
$cfg[\'UploadDir\'] = getcwd();
} else {
$cfg[\'UploadDir\'] = \'/tmp/pharphpmyadmin\';
@mkdir(\'/tmp/pharphpmyadmin\');
@chmod(\'/tmp/pharphpmyadmin\', 0777);
}'
;
$a->setStub('<?php
Phar::interceptFileFuncs();
Phar::webPhar("phpMyAdmin.phar", "phpMyAdmin-2.11.3-english/index.php");
echo "phpMyAdmin предназначен для выполнения в веб-браузере\n";
exit -1;
__HALT_COMPILER();
'
);
$a->stopBuffering();
?>

Добавить

Примечания пользователей 3 notes

up
15
shaun at shaunfreeman dot co dot uk
14 years ago
If you are trying to use Phar for a web application and just getting a blank screen, if you have enabled suhosin as well you have to add:

suhosin.executor.include.whitelist="phar"

to "/etc/php5/conf.d/suhosin.ini" file or your "php.ini" file.

once done everything works fine and dandy.
up
9
ch1902
10 years ago
If you are going to be running a webPhar from the browser, for example http://localhost/myphar.phar then you will probably have to associate the .phar extension with PHP in your webserver to interpret the PHP code. In Apache modify httpd.conf to include

AddType application/x-httpd-php .php .phar
up
2
frame86 at live dot com
11 years ago
The openssl example is completely wrong. The public key must be extracted from certificate and openssl_pkey_export() is for private key only.

Working example:
<?php
$publicKey
= openssl_get_publickey(file_get_contents('certificate.pem'));
$details = openssl_pkey_get_details($publicKey);
file_put_contents('my.phar.pubkey', $details['key']);
?>

No need to say that the best and strongest encryption of my.phar/.phar/signature.bin is useless if the consumer does not check against a valid fingerprint or certificate of public key as anybody can open, read, recreate and sign a new archive with new key. Do you do? Think about it.
To Top