Phar アーカイブの使用法: 導入
Phar アーカイブは Java の JAR アーカイブと似た概念のものですが、 PHP アプリケーションで使用する際に必要な機能をより柔軟に使用できるよう改良しています。 Phar アーカイブを使用すると、PHP アプリケーションやライブラリを ひとつのファイルにまとめて配布できるようになります。 Phar アーカイブ形式のアプリケーションは、その他の PHP アプリケーションとまったく同様に扱えます。
php coolapplication.phar
Phar アーカイブ形式のライブラリを使用する方法も、 その他の通常の PHP ライブラリとまったく同じです。
<?php
include 'coollibrary.phar';
?>
phar
ストリームラッパーは phar 拡張モジュールの要となるものです。
詳細については こちら
で説明します。このストリームラッパーを使用すると、
phar 内の個々のファイルに対して
fopen() や opendir()
といった標準のファイル操作関数を使用でき、
まるで通常のファイルシステム上にあるのと同じような感覚でアクセスできます。
phar
ストリームラッパーは、
ファイルやディレクトリに対する読み書き操作をすべてサポートしています。
<?php
include 'phar://coollibrary.phar/internal/file.php';
header('Content-type: image/jpeg');
// phar にアクセスするには、フルパスあるいはエイリアスを使用します
echo file_get_contents('phar:///fullpath/to/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 の作成 - php.ini で phar.readonly を 0 にしておく必要があります。
// phar.readonly は、セキュリティ上の理由によってデフォルトで有効になっています。
// 実際の運用サーバーでは、決して Phar を作成する必要はないはずで、
// 単に実行できるだけでよいはずです。
if (Phar::canWrite()) {
$p = new Phar('newphar.tar.phar', 0, 'newphar.tar.phar');
// tar 形式の phar アーカイブを作成し、gzip で圧縮します (.tar.gz)
$p = $p->convertToExecutable(Phar::TAR, Phar::GZ);
// トランザクションの作成 - newphar.phar には、
// stopBuffering() がコールされるまで (一時的なストレージが必要となっても) なにも書き込まれません
$p->startBuffering();
// すべてのファイルを /path/to/project に追加し、プレフィックス "project" で phar に保存します
$p->buildFromIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('/path/to/project')), '/path/to/');
// 配列形式の API で新しいファイルを追加します
$p['file1.txt'] = 'Information';
$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、そしてもし ext/hash が有効ならさらに SHA512) のいずれか、 あるいは OpenSSL を使用した非対称公開鍵/秘密鍵ペアによる署名のいずれかを使用します。 OpenSSL による署名を使用するには、 まず公開鍵/秘密鍵のペアを作成し、秘密鍵を使用して Phar::setSignatureAlgorithm() で署名を行います。 公開キーによる展開はこのようなコードで行います。
<?php
$public = openssl_get_publickey(file_get_contents('private.pem'));
$pkey = '';
openssl_pkey_export($public, $pkey);
?>
/path/to/my.phar
の場合は公開鍵を
/path/to/my.phar.pubkey
としなければなりません。
そうしないと、OpenSSL 署名の検証ができなくなります。
Phar には 3 つのstaticメソッド Phar::webPhar()、 Phar::mungServer() そして Phar::interceptFileFuncs() が追加されました。これは、通常のファイルシステム用あるいはウェブアプリケーション用の PHP アプリケーションのパッケージを作成する際に重要となります。 Phar::webPhar() は、HTTP のコールを phar アーカイブ内の特定の場所に転送するフロントコントローラを実装します。 Phar::mungServer() は $_SERVER 配列の中身をうまく変更して アプリケーションでその値を使えるようにします。 Phar::interceptFileFuncs() は、 fopen() や file_get_contents()、opendir() そしてすべての stat 系関数 (file_exists()、is_readable() など) のコールを Phar が横取りして、相対パスを phar アーカイブ内の適切な位置に変換するようにします。
たとえば、あの有名な phpMyAdmin を phar アーカイブにすることを考えてみましょう。
必要なのは、こんな単純なスクリプトだけです。あとは、
ユーザー名とパスワードを変更してウェブサーバー上から
phpMyAdmin.phar.tar.php
にアクセスするだけで利用できます。
<?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;
/* Server 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 is intended to be executed from a web browser\n";
exit -1;
__HALT_COMPILER();
');
$a->stopBuffering();
?>
User Contributed Notes 3 notes
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.
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
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.