CXXIII. Phar アーカイブストリームおよびクラス

導入

phar 拡張モジュールは、 phar ストリームラッパーおよび Phar クラスを提供し、 PHP アーカイブ (phar) ファイルを操作することができます。 Phar クラスを使用すると、phar ファイルの作成や展開、 そしてその中身を順に処理することなどができます。

PHP アーカイブファイル (Phar) は複数のファイルをまとめたもので、 外部からその中のファイルを透過的に実行することができます。 Java の jar アーカイブファイルと似たものです。 phar アーカイブを使用すると、 PHP アプリケーションをひとつのファイルにまとめて配布できるようになります。 このファイルは、変更したり展開したりすることなくそのまま外部から実行できます。 phar アーカイブは、tar や zip のアーカイブと同様に、 ファイルを保存するために使用することもできます。 Phar は圧縮もサポートしています。 zlib 拡張モジュールが存在する場合には gzip を、そして bz2 拡張モジュールが存在する場合には bzip2 を使用します。 さらに、SPL 拡張モジュールが存在する場合には、 順次処理やその他の機能が使用可能となります。 md5 や sha1 による Phar の署名の検証は ネイティブでサポートされており、アーカイブの整合性を確かめることができます。

Phar アーカイブのネイティブな実装は PEAR パッケージ PHP_Archive であり、この拡張モジュールの実装はこれと非常によく似ています。 しかし Phar 拡張モジュールのほうが、より多くの機能をサポートしています。 PHP_Archive はより柔軟に Phar を作成することができます。また PHP_Archive_Manager クラスのような便利なデバッグツールも用意されています。 一方 Phar 拡張モジュールは、Phar の中身の順次処理や 配列形式でのアクセス、そして中身の変更などが シンプルなインターフェイスでできるようになります。 PHP_Archive は、Phar 拡張モジュールあるいは PHP_Archive のいずれかでシームレスに処理できる Phar アーカイブの作成をサポートしています。 一方、Phar 拡張モジュールは、 Phar 拡張モジュールで動作するアーカイブを作成するよう設計されています。 さらに Phar 拡張モジュールは、INI 設定 allow_url_include あるいは allow_url_fopen が無効になっていても動作します。一方 PHP_Archive で (Phar 拡張モジュールを使用せずに) 作成したアーカイブは動作しません。

要件

Phar を使用するには PHP 5.2.0 以降が必要です。また、 Phar ファイルの中身に対して配列形式でアクセスしたり順に処理したりするには SPL 拡張モジュールが PHP に組み込まれている必要があります。 phar ストリームを使用するには、 これは不要です。

オプションとして、zlib 拡張モジュールおよび bzip2 拡張モジュールを有効にしておくと、 圧縮された phar をサポートすることができます。

インストール手順

Windows 用のバイナリは http://snaps.php.net/ にあります。インストールするには、php_phar.dll をダウンロードして、php.ini の extension_dir ディレクティブで指定されたフォルダに配置します。 その後、php.ini に extension=php_phar.dll を追加してこれを有効にし、ウェブサーバを再起動します。

extension_dir=c:/php5/exts/
extension=php_phar.dll

Linux、BSD その他 *nix 系 の場合は、次の手順でコンパイルします。

  • 次の手順か、

    • pecl install phar で、PECL/phar のインストーラを実行します。

    • phar.so を、ビルド時に表示されたディレクトリから php.ini の extension_dir ディレクトリにコピーします。

    • extension=phar.so を php.ini に追加します。

    あるいはこちらの手順を実行します。

    • 次のようにして、php.ini へのパスを設定します。

      pecl config-set php_ini /path/to/php.ini

    • pecl install phar で、PECL/phar のインストーラを実行します。

  • ウェブサーバを再起動し、php.ini の設定を読み込ませます。

開発バージョン: 現在は、PECL/phar の 安定版 はまだありません。 アルファ版 の PECL/phar をインストールするには pecl install phar-alpha を実行します。

PEAR コマンドを使用しない PECL/phar のコンパイル: pecl install phar を使用すると、 PECL/phar を自動的にダウンロードし、インストールします。しかし、 tar ボールを PECL からダウンロードすることもできます。tar ボールを展開したルートディレクトリで phpize && ./configure --enable-phar && make を実行すると phar.so が出来上がります。 ビルドしたものを、上のようにしてインストールします。

この PECL 拡張モジュールをインストールする方法は、 マニュアルの PECL 拡張モジュールのインストール という章にあります。 新規リリース・ダウンロード・ソースファイル・管理者情報・CHANGELOG といった関連する情報については、次の場所にあります。 http://pecl.php.net/package/phar.

実行時設定

php.ini の設定により動作が変化します。

表 1. ファイルシステムおよびストリームの設定オプション

名前デフォルト変更の可否変更履歴
phar.readonly"1"PHP_INI_SYSTEM では有効化/無効化、PHP_INI_ALL では有効化のみバージョン 1.0.0 以降で使用可能
phar.require_hash"0"PHP_INI_SYSTEM では有効化/無効化、PHP_INI_ALL では有効化のみバージョン 1.0.0 以降で使用可能

以下に設定ディレクティブに関する 簡単な説明を示します。

phar.readonly boolean

このオプションを使用すると、phar ストリームや Phar オブジェクトによる Phar アーカイブの作成や変更ができなくなります。 この設定は、実運用環境では常に有効にしておくべきです。 phar 拡張モジュールのこの機能は、 サーバ上に他のセキュリティ上の脆弱性があった場合に php ベースのウィルスを直接作成されてしまうことにつながります。

注意: この設定は、セキュリティ上の理由により php.ini でしか解除できません。 phar.readonly を php.ini で無効にした場合は、 ユーザがスクリプト内で phar.readonly の有効/無効を切りかえることができます。 phar.readonly を php.ini で有効にした場合は、 スクリプト内でこれを "再度有効にする" ことはできますが、無効にすることはできません。

phar.require_hash boolean

このオプションを使用すると、署名つき (現在サポートしているのは MD5 および SHA1) の Phar アーカイブのみをオープンするようになります。 署名を含まない Phar アーカイブの処理はできません。

注意: この設定は、セキュリティ上の理由により php.ini でしか解除できません。 phar.require_hash を php.ini で無効にした場合は、 ユーザがスクリプト内で phar.require_hash の有効/無効を切りかえることができます。 phar.require_hash を php.ini で有効にした場合は、 スクリプト内でこれを "再度有効にする" ことはできますが、無効にすることはできません。

リソース型

Phar 拡張モジュールで使用するリソースは phar ストリームで、これにより、phar 内のファイルへの透過的なアクセスが可能となります。 Phar ファイルのフォーマットについては ここ で説明されています。

リソース型は定義されていません。

定義済みクラス

Phar
PharFileInfo
PharException

Phar アーカイブの使用法: 導入

Phar アーカイブは Java の JAR アーカイブと似た概念のものですが、 PHP アプリケーションで使用する際に必要な機能をより柔軟に使用できるよう改良しています。 Phar アーカイブを使用すると、PHP アプリケーションやライブラリを ひとつのファイルにまとめて配布できるようになります。 Java における JAR アーカイブの実装とは異なり、 PHP の Phar アーカイブを処理したり実行したりするには外部のツールは不要です。 Phar アーカイブ形式のアプリケーションは、その他の PHP アプリケーションとまったく同様に扱えます。

php coolapplication.phar

Phar アーカイブ形式のライブラリを使用する方法も、 その他の通常の PHP ライブラリとまったく同じです。

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

Phar アーカイブを非常に便利なものとしている機能が phar ストリームラッパーです。 詳細については こちら で説明します。このストリームラッパーを使用すると、 phar 内の個々のファイルに対して、 まるで通常のファイルシステム上にあるのと同じような感覚でアクセスできます。 phar ストリームラッパーは、 ファイルに対する読み書きや、ディレクトリに対する opendir() をすべてサポートしています。

<?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 アーカイブ内のファイルに対して まるでそれが連想配列であるかのようにアクセスできるようになります。 またその他の機能も用意されています。Phar クラスについての説明は こちら をご覧ください。

<?php
try
{
    
// 既存の phar をオープンします
    
$p = new Phar('coollibrary.phar');
    foreach (
$p as $file) {
        
// $file は PharFileInfo クラスで、これは SplFileInfo を継承しています
        
echo $file->getFileName() . "\n";
        echo
$file . "\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(dirname(__FILE__) . '/newphar.phar', 0, 'newphar.phar');
        
// トランザクションを作成します - commit() がコールされるまで、
        // newphar.phar には何も書き込まれません。しかし一時ストレージが必要となります
        
$p->begin();
        
// 新しいファイルを追加し、その内容を設定します
        
$p['file1.txt'] = 'Information';
        
$fp = fopen('hugefile.dat', 'rb');
        
// ストリームからコピーします
        
$p['data/hugefile.dat'] = $fp;
        if (
Phar::canCompress()) {
            
$p['data/hugefile.dat']->setCompressedGZ();
        }
        
$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'));
        
// ローダースタブを設定します
        
$p->setStub('<?php
$p = new Phar(__FILE__);
$m = $p->getMetaData();
require "phar://" . __FILE__ . "/" . $m["bootstrap"];
__HALT_COMPILER();'
);
        
// phar アーカイブをディスクに保存します
        
$p->commit();
    }
}
catch (Exception $e) {
    echo
'Phar をオープンできません: ', $e;
}
?>

Phar アーカイブの使用法: phar ストリームラッパー

Phar ストリームラッパーは、fopen() による読み込みや 書き込みそして追記、unlink()stat()fstat()fseek()rename() そしてディレクトリに対する opendir() といった機能に完全に対応しています。 Phar ストリームラッパーはディレクトリの作成や削除には対応していません。 ファイルは単なるファイルとしてしか格納されず、 ディレクトリを抽象化した概念は存在しないからです。

Phar アーカイブ内の各ファイルの圧縮やファイル単位のメタデータの操作も、 ストリーム上で可能です。

<?php
$context
= stream_context_create(array('phar' =>
                                    array(
'compression' => Phar::GZ)),
                                    array(
'metadata' => array('user' => 'cellog')));
file_put_contents('phar://my.phar/somefile.php', 0, $context);
?>

phar ストリームラッパーは、 リモートファイルを操作することはできません。つまり、INI 設定 allow_url_fopen および allow_url_include が無効になっている場合でも使用できます。

ストリーム操作だけで新しい phar アーカイブをゼロから作成することも可能ですが、 そんな場合は Phar クラスの組み込み機能を使用するほうが便利です。 ストリームラッパーが有用なのは、読み込み操作を行う場合です。

Phar アーカイブの使用法: Phar クラス

Phar クラスは Phar アーカイブの読み込みや操作をサポートしています。 また RecursiveDirectoryIterator クラスを継承しているため、順次処理も可能です。 ArrayAccess インターフェイスをサポートしているので、 Phar アーカイブ内のファイルに対して、 それがまるで連想配列であるかのようにアクセスすることができます。

注意すべき点は、Phar アーカイブを作成する際には Phar のコンストラクタに フルパスを渡さなければならないということです。 相対パスでは初期化に失敗します。

$p が、次のように作成した Phar オブジェクトであるとしましょう。

<?php
$p
= new Phar('/path/to/myphar.phar', 0, 'myphar.phar');
?>

空の Phar アーカイブが /path/to/myphar.phar に作成されます。もし /path/to/myphar.phar が既に存在する場合は、それを再度オープンします。 リテラル myphar.phar は、エイリアスを表します。 これを用いると、URL で /path/to/myphar.phar を参照する際に次のようにできます。

<?php
// これらのふたつの file_get_contents() コールが同等となるのは、
// /path/to/myphar.phar のマニフェストでエイリアス "myphar.phar"
// が明示的に指定されている場合か、先ほどの例のように
// Phar オブジェクトを初期化した場合です。
$f = file_get_contents('phar:///path/to/myphar.phar/whatever.txt');
$f = file_get_contents('phar://myphar.phar/whatever.txt');
?>

新しく作成した Phar オブジェクト $p に対して、次のような処理が可能となります。

  • $a = $p['file.php'] とすると、 phar://myphar.phar/file.php の中身を参照する PharFileInfo クラスが作成されます。

  • $p['file.php'] = $v とすると、 myphar.phar の中に新しいファイル (phar://myphar.phar/file.php) を作成するか、あるいは同名のファイルを上書きします。 $v には、文字列あるいはファイルポインタのいずれかを指定できます。 ファイルポインタを指定した場合は、その中身全体をもとにして新しいファイルを作成します。

  • isset($p['file.php']) とすると、phar://myphar.phar/file.phpmyphar.phar の中に存在するかどうかがわかります。

  • unset($p['file.php']) とすると、 phar://myphar.phar/file.phpmyphar.phar から削除します。

さらに、Phar 固有のメタデータにアクセスするためには Phar オブジェクトを使用することが唯一の方法となります。そのためには Phar->getMetaData() を使用します。また、Phar アーカイブの PHP ローダスタブを設定したり取得したりするための唯一の方法が Phar->getStub() および Phar->setStub() です。 また、Phar アーカイブ全体の圧縮を行うには Phar クラスが必要となります。

Phar オブジェクトの全機能の一覧については、以下で説明します。

PharFileInfo クラスは SplFileInfo クラスを継承しており、Phar 内のファイルについての Phar 固有の情報 (圧縮情報やメタデータなど) を扱うためのメソッドが追加されています。

Phar ファイルフォーマット

Phar ファイルは、三つあるいは四つの部分から構成されています。

  1. スタブ

  2. 内容を説明するマニフェスト

  3. ファイルの内容

  4. [オプション] Phar の整合性を検証するためのシグネチャ

Phar ファイルのスタブ

Phar のスタブは、単純な PHP ファイルです。必要最小限のスタブは、次のようになります。

<?php __HALT_COMPILER();

スタブには、少なくとも __HALT_COMPILER(); トークンが必要です。 典型的なスタブは、次のように読み込み機能を含んでいます。

<?php
Phar
::mapPhar();
include
'phar://myphar.phar/index.php';
__HALT_COMPILER();

Phar スタブの内容には特に制限はありません。唯一の制約は、最後が __HALT_COMPILER(); でなければならないということです。 PHP の終了タグ ?> は含めても含めなくてもかまいません。 しかし、; と終了タグ ?> の間にはひとつ以上の空白を含めてはいけません。 もしそうしてしまうと、phar 拡張モジュールは その Phar アーカイブのマニフェストを処理できなくなります。

Phar マニフェストの書式

Phar マニフェストは高度に最適化された書式で、 ファイル単位で圧縮やパーミッションの情報を指定することができ、 さらにファイルのユーザやグループなど、独自に定義したメタデータも含めることができます。 1 バイトをこえる大きさの値はリトルエンディアン形式のバイト順で保存されます。 ただし API バージョンだけは例外です。これは 3 ニブルのデータですが、 歴史的な理由によりビッグエンディアン形式のバイト順で保存されます。

未使用のフラグはすべて将来の使用に備えて予約されています。 したがって、独自の情報を保存するためにそれを使用してはいけません。 特定のファイルについて独自の情報を保存するには、 ファイル単位のメタデータ機能を使用します。

Phar アーカイブマニフェストの基本的なファイルフォーマットは、次のようになります。

表 2. グローバル Phar マニフェスト書式

バイト数説明
4 バイトマニフェスト全体のバイト長 (最大 1 MB)。
4 バイトPhar 内のファイル数。
2 バイトPhar マニフェストの API バージョン (現在は 1.0.0)。
4 バイトグローバルな Phar ビットマップフラグ。
4 バイトPhar のエイリアスの長さ。
??Phar のエイリアス (先ほどの長さに基づきます)。
4 バイトPhar のメタデータの長さ (存在しない場合は 0)。
??シリアライズ化された Phar メタデータ。serialize() 形式で格納される。
最低でも 24 * エントリ数ぶんのバイト各ファイルのエントリ

グローバルな Phar ビットマップフラグ

これが、Phar 拡張モジュールが現在 Phar フラットビットマップとして認識するフラグです。

表 3. 認識するビットマップ値

説明
0x00010000設定されている場合、この Phar には検証用シグネチャが含まれます。
0x00001000 設定されている場合、この Phar には zlib 圧縮されたファイルが少なくともひとつ含まれます。
0x00002000 設定されている場合、この Phar には bzip 圧縮されたファイルが少なくともひとつ含まれます。

Phar マニフェストのファイルエントリの定義

マニフェスト内の各ファイルについて、次のような情報が含まれます。

表 4. Phar マニフェストのファイルエントリ

バイト数説明
4 バイトファイル名の長さを表すバイト数。
??ファイル名 (先ほど指定した長さになります)。
4 バイト圧縮前のファイルサイズを表すバイト数。
4 バイトファイルの Unix タイムスタンプ。
4 バイト圧縮後のファイルサイズを表すバイト数。
4 バイト圧縮前のファイルの CRC32 チェックサム。
4 バイトファイル固有のビットマップフラグ。
4 バイトシリアライズされたファイルのメタデータの長さ (存在しない場合は 0)。
??シリアライズされたファイルのメタデータ。serialize() の形式で格納される。

ファイル固有のビットマップ値として認識される値は次のとおりです。

表 5. 認識されるビットマップ値

説明
0x000001FF これらのビットは、ファイルの特定のパーミッションを定義するために予約されています。 このパーミッションは fstat() で用いられ、 ファイルを展開する際に特定のパーミッションを指定することができます。
0x00001000 設定されている場合、このファイルは zlib で圧縮されています。
0x00002000 設定されている場合、このファイルは bzip で圧縮されています。

Phar のシグネチャの書式

シグネチャを含む Phar は、常にシグネチャを保持しています。 シグネチャの場所は、 ローダ、マニフェスト、実際のファイルの内容に続く Phar アーカイブの最後の部分です。 現在サポートしているシグネチャのフォーマットは MD5 および SHA1 の二種類です。

表 6. シグネチャのフォーマット

バイト長説明
16 あるいは 20 バイト 実際のシグネチャ。SHA1 の場合は 20 バイト、 MD5 の場合は 16 バイトとなります。
4 バイト シグネチャのフラグ。0x0001 は MD5 シグネチャ、そして 0x0002 は SHA1 シグネチャを表します。
4 バイト GBMB という固定値で、 シグネチャが存在することを表します。

目次
Phar::apiVersion -- API のバージョンを返す
Phar::canCompress -- phar 拡張モジュールが zlib あるいは bzip2 による圧縮をサポートしているかどうかを返す
Phar::canWrite -- phar 拡張モジュールが phar の書き込みや作成をサポートしているかどうかを返す
Phar->compressAllFilesBZIP2 -- 現在の Phar アーカイブ内のすべてのファイルを Bzip2 で圧縮する
Phar->compressAllFilesGZ -- 現在の Phar アーカイブ内のすべてのファイルを Gzip で圧縮する
Phar::__construct -- Phar アーカイブオブジェクトを作成する
Phar->count -- Phar アーカイブ内のエントリ (ファイル) の数を返す
Phar->getMetaData -- phar アーカイブのメタデータを返す
Phar->getModified -- phar が変更されているかどうかを返す
Phar->getSignature -- Phar アーカイブの MD5/SHA1 シグネチャを返す
Phar->getStub -- Phar アーカイブの PHP ローダーあるいは起動スタブを返す
Phar->getVersion -- Phar アーカイブのバージョン情報を返す
Phar->isBuffering -- Phar の書き込み操作がバッファリングされるか、あるいは直接ディスクに書き込まれるかを調べる
Phar::loadPhar -- 任意の phar アーカイブを、エイリアスを指定して読み込む
Phar::mapPhar -- 現在実行されている (phar 形式の) ファイルを読み込み、その内容を登録する
Phar::offsetExists -- ファイルが phar 内に存在するかどうかを調べる
Phar::offsetGet -- 指定したファイルの PharFileInfo オブジェクトを取得する
Phar::offsetSet -- 内部ファイルに、外部ファイルの内容を設定する
Phar::offsetUnset -- ファイルを phar から削除する
Phar->setMetaData -- phar アーカイブのメタデータを設定する
Phar->setStub -- Phar アーカイブの PHP ローダ (あるいは起動スタブ) を設定する
Phar->startBuffering -- Phar の書き込み操作のバッファリングを開始するが、ディスク上の Phar オブジェクトは変更しない
Phar->stopBuffering -- Phar アーカイブへの書き込みリクエストのバッファリングを終了し、変更内容をディスクに書き込む
Phar->uncompressAllFiles -- 現在の Phar アーカイブ内のすべてのファイルを展開する
PharFileInfo->chmod -- ファイルとともに保存する、ファイル固有のメタデータを設定する
PharFileInfo::__construct -- Phar エントリオブジェクトを作成する
PharFileInfo->getCompressedSize -- Phar アーカイブ内での実際のファイルの大きさ (圧縮された状態) を返す
PharFileInfo->getCRC32 -- CRC32 コードを返すか、CRC がチェックできない場合に例外をスローする
PharFileInfo->getMetaData -- ファイルとともに保存されている、ファイル固有のメタデータを返す
PharFileInfo->getPharFlags -- Phar ファイルエントリのフラグを返す
PharFileInfo->isCompressed -- エントリが圧縮されているかどうかを調べる
PharFileInfo->isCompressedBZIP2 -- エントリが bzip2 で圧縮されているかどうかを調べる
PharFileInfo->isCompressedGZ -- エントリが gz で圧縮されているかどうかを調べる
PharFileInfo->isCRCChecked -- ファイルエントリの CRC が検証されているかどうかを調べる
PharFileInfo->setCompressedBZIP2 -- phar 内の現在の Phar エントリを、Bzip2 で圧縮する
PharFileInfo->setCompressedGZ -- phar 内の現在の Phar エントリを、gz で圧縮する
PharFileInfo->setMetaData -- ファイルとともに保存する、ファイル固有のメタデータを設定する
PharFileInfo->setUncompressed -- phar 内の現在の Phar エントリが圧縮されている場合に、それを展開する