Использование 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::TARPhar::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 (добавлено в Phar 2.0.0). Для того чтобы использовать подписывание 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.

Начиная с версии 2.0.0 класс 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();
?>