Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】Phar::buildFromIterator()メソッドの使い方

buildFromIteratorメソッドの使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

buildFromIteratorメソッドは、PHPアプリケーションの配布形式であるPharアーカイブを生成する際に、ファイルやディレクトリのリストをイテレータ(反復子)から効率的に読み込んでアーカイブに追加するメソッドです。このメソッドは、Pharクラスの一部として提供されており、大量のファイルを含むプロジェクトを単一の実行可能な.pharファイルにまとめるために利用されます。

このメソッドを使用することで、開発者はIteratorインターフェースを実装した任意のオブジェクトを使い、柔軟な条件で選択されたファイル群をアーカイブに含めることが可能になります。例えば、ファイルシステム上の特定のディレクトリ構造に縛られず、カスタムロジックで選択されたファイル(特定の拡張子を持つファイルのみ、あるいは特定の条件を満たすファイルなど)を効率的にPharアーカイブに追加する場合に非常に有効です。

第一引数には、アーカイブに追加するファイルパスのリストを返すIteratorオブジェクトを指定します。このイテレータは、アーカイブに含めるファイルパスを、内部パスとして適切に解決できるように提供する必要があります。オプションの第二引数には、アーカイブ内のファイルパスに適用するプレフィックスを指定でき、これによりアーカイブ内のディレクトリ構造を柔軟に調整できます。このメソッドを利用することで、PHPプロジェクトの配布とデプロイを簡素化し、効率的に管理することが可能になります。

構文(syntax)

1<?php
2
3// Pharオブジェクトのインスタンス化
4// 'my_archive.phar' は作成するPharアーカイブのファイル名です。
5$phar = new Phar('my_archive.phar');
6
7// アーカイブに追加するファイルの内容を保持するイテレータを準備します。
8// キーはアーカイブ内でのファイルのパス、値はファイルの内容(文字列)です。
9// ArrayIteratorはTraversableインターフェースを実装しており、Phar::buildFromIteratorに渡すことができます。
10$fileIterator = new ArrayIterator([
11    'path/in/archive/document.txt' => 'This is the content of document.txt.',
12    'another_directory/image.jpg' => 'Binary content for an image file...', // バイナリデータも可
13    'log.txt' => 'Log entry 1: Error occurred. Log entry 2: Success.',
14]);
15
16// Phar::buildFromIterator メソッドを呼び出して、イテレータからPharアーカイブを構築します。
17//
18// 引数:
19// 1. $iterator (Traversable): ファイルのキーと値を含むイテレータ。必須引数です。
20// 2. $baseDirectory (?string): オプション。アーカイブにファイルを追加する際の基準ディレクトリ。
21//    nullまたは省略された場合、イテレータ内のパスは現在の作業ディレクトリからの相対パスとして扱われます。
22// 3. $options (?array): オプション。その他の設定(例: Phar::compressAllFiles)。
23//
24// 戻り値:
25// 追加されたすべてのファイルのリストを連想配列として返します。
26// キーはアーカイブ内のファイル名、値は実際のファイルパスです。
27$addedFiles = $phar->buildFromIterator($fileIterator);
28
29// $addedFiles には、アーカイブに追加されたファイルの情報が格納されます。
30// 例: [
31//   'path/in/archive/document.txt' => 'phar://my_archive.phar/path/in/archive/document.txt',
32//   'another_directory/image.jpg' => 'phar://my_archive.phar/another_directory/image.jpg',
33//   'log.txt' => 'phar://my_archive.phar/log.txt'
34// ]
35

引数(parameters)

Traversable $iterator, ?string $baseDirectory = null

  • Traversable $iterator:pharアーカイブに含めるファイルやディレクトリのパスを順に取得できるイテレーターオブジェクト
  • ?string $baseDirectory = null:pharアーカイブのルートディレクトリからの相対パスを指定する文字列。指定しない場合はpharアーカイブのルートが基準となる

戻り値(return)

array

Phar::buildFromIteratorメソッドは、Iteratorから生成されたPharアーカイブ内に含まれるファイルパスの配列を返します。

サンプルコード

PHP Phar::buildFromIteratorでディレクトリからPharを作成する

1<?php
2
3/**
4 * Phar::buildFromIterator メソッドを使用して、指定されたディレクトリ構造からPharアーカイブを作成するサンプルコード。
5 *
6 * システムエンジニアを目指す初心者の方にも理解しやすいように、各ステップをコメントで説明しています。
7 *
8 * 注意: このスクリプトはPharアーカイブを作成するため、PHP設定ファイル (php.ini) で 'phar.readonly = Off' が
9 * 設定されている必要があります。セキュリティ上の理由から、本番環境では 'phar.readonly = On' が推奨されます。
10 * 開発目的で一時的に設定を変更したい場合は、`ini_set('phar.readonly', '0');` をスクリプトの冒頭に追加できますが、
11 * 実行後は元に戻すか、テスト環境でのみ使用してください。
12 */
13function createPharArchiveFromDirectory(): void
14{
15    // 1. 一時ディレクトリとPharアーカイブのファイル名を準備します。
16    //    uniqid() を使うことで、毎回異なる一時ディレクトリ名が生成され、他の実行との衝突を防ぎます。
17    $tempDir = sys_get_temp_dir() . '/phar_example_' . uniqid();
18    $pharFileName = $tempDir . '/my_app.phar';
19    $pharAlias = 'my_app.phar'; // Pharアーカイブに割り当てるエイリアス(オプション)
20
21    // 2. 作業用の一時ディレクトリを作成します。
22    if (!mkdir($tempDir, 0777, true)) {
23        echo "エラー: 一時ディレクトリの作成に失敗しました: " . $tempDir . "\n";
24        return;
25    }
26    echo "一時ディレクトリを作成しました: " . $tempDir . "\n";
27
28    // 3. Pharアーカイブに含めるダミーファイルとサブディレクトリを作成します。
29    //    これにより、Pharアーカイブに含めるファイル構造をシミュレートします。
30    file_put_contents($tempDir . '/index.php', '<?php echo "Hello from index.php in Phar!\n";');
31    mkdir($tempDir . '/src');
32    file_put_contents($tempDir . '/src/helper.php', '<?php function greet() { return "Hello from helper.php inside Phar!"; }');
33    file_put_contents($tempDir . '/README.md', 'This is a test Phar archive created by buildFromIterator.');
34    echo "Pharに含めるダミーファイルとディレクトリを作成しました。\n";
35
36    // 4. php.iniの 'phar.readonly' 設定を確認し、必要であれば一時的に変更します。
37    //    'phar.readonly' が '1' (On) の場合、Pharアーカイブの作成や変更はできません。
38    if (ini_get('phar.readonly') === '1') {
39        echo "警告: php.iniで 'phar.readonly = Off' が推奨されます。このスクリプトは一時的に設定を変更します。\n";
40        ini_set('phar.readonly', '0');
41    }
42
43    try {
44        // 5. 新しいPharオブジェクトを作成します。
45        //    第一引数: 作成するPharアーカイブのファイルパス。
46        //    第二引数: Pharアーカイブ内の要素の扱い方を定義するフラグ。
47        //              - FilesystemIterator::CURRENT_AS_FILEINFO: イテレータの現在の要素をSplFileInfoオブジェクトとして扱います。
48        //              - FilesystemIterator::KEY_AS_FILENAME: イテレータのキーをファイル名として取得しますが、
49        //                                                      buildFromIteratorでは主に第二引数(baseDirectory)でパスを調整します。
50        //    第三引数: Pharアーカイブのエイリアス。
51        $phar = new Phar($pharFileName, FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME, $pharAlias);
52
53        // Pharアーカイブへの書き込みを開始するためにバッファリングを有効にします。
54        $phar->startBuffering();
55
56        // 6. 指定されたディレクトリを再帰的に走査するためのイテレータを作成します。
57        //    - RecursiveDirectoryIterator: 指定されたディレクトリとそのサブディレクトリを探索します。
58        //                                  FilesystemIterator::SKIP_DOTS は '.' と '..' ディレクトリをスキップします。
59        //    - RecursiveIteratorIterator: RecursiveDirectoryIteratorのような再帰的なイテレータを、
60        //                                 通常のループで処理できるフラットなイテレータに変換します。
61        $directoryIterator = new RecursiveDirectoryIterator($tempDir, FilesystemIterator::SKIP_DOTS);
62        $recursiveIterator = new RecursiveIteratorIterator($directoryIterator);
63
64        // 7. buildFromIterator メソッドを使用してPharアーカイブを作成します。
65        //    - 第一引数: ファイルシステムを走査するイテレータ (Traversableオブジェクト)。
66        //    - 第二引数: $baseDirectory。これを指定することで、Pharアーカイブ内のファイルパスが
67        //                $tempDir からの相対パスになります。例えば、$tempDir/src/helper.php は
68        //                Phar内では src/helper.php として格納されます。
69        echo "Phar::buildFromIterator を使用してアーカイブを作成中...\n";
70        $filesAdded = $phar->buildFromIterator($recursiveIterator, $tempDir);
71
72        echo "Pharアーカイブに以下のファイルが追加されました:\n";
73        foreach ($filesAdded as $pharPath => $originalPath) {
74            echo "  - Phar内のパス: " . $pharPath . ", 元のファイルパス: " . $originalPath . "\n";
75        }
76
77        // Pharアーカイブへの書き込みを終了し、変更を保存します。
78        $phar->stopBuffering();
79
80        echo "\nPharアーカイブが正常に作成されました: " . $pharFileName . "\n";
81
82        // 8. 作成されたPharアーカイブの内容を確認します (オプション)。
83        //    Pharアーカイブ内のファイルは 'phar://' スキームを使用してアクセスできます。
84        echo "\n--- Pharアーカイブの内容を検証 ---\n";
85        // Phar内の index.php ファイルを実行
86        include 'phar://' . $pharFileName . '/index.php';
87        // Phar内の src/helper.php ファイルをインクルードし、関数を呼び出す
88        include 'phar://' . $pharFileName . '/src/helper.php';
89        if (function_exists('greet')) {
90            echo "helper.php からのメッセージ: " . greet() . "\n";
91        }
92        echo "README.md の内容:\n";
93        echo file_get_contents('phar://' . $pharFileName . '/README.md') . "\n";
94
95    } catch (Exception $e) {
96        // Phar作成中にエラーが発生した場合、例外をキャッチして表示します。
97        echo "エラー: Pharアーカイブの作成中に問題が発生しました: " . $e->getMessage() . "\n";
98    } finally {
99        // 9. 後片付けを行います。作成した一時ファイルとディレクトリを削除します。
100        echo "\n--- 後片付け中 ---\n";
101
102        // Pharファイルを削除します
103        if (file_exists($pharFileName)) {
104            unlink($pharFileName);
105            echo "Pharアーカイブを削除しました: " . $pharFileName . "\n";
106        }
107        // Pharが署名されている場合、署名ファイル (.sig) も削除します
108        if (file_exists($pharFileName . '.sig')) {
109            unlink($pharFileName . '.sig');
110            echo "Phar署名ファイルを削除しました: " . $pharFileName . ".sig\n";
111        }
112
113        // 一時ディレクトリを再帰的に削除するためのヘルパー関数
114        $rmdirRecursive = function ($dir) use (&$rmdirRecursive) {
115            if (!is_dir($dir)) {
116                return false;
117            }
118            $files = array_diff(scandir($dir), ['.', '..']);
119            foreach ($files as $file) {
120                (is_dir("$dir/$file")) ? $rmdirRecursive("$dir/$file") : unlink("$dir/$file");
121            }
122            return rmdir($dir);
123        };
124
125        // 一時ディレクトリが存在すれば削除
126        if (file_exists($tempDir)) {
127            $rmdirRecursive($tempDir);
128            echo "一時ディレクトリを削除しました: " . $tempDir . "\n";
129        }
130    }
131}
132
133// 関数を実行してPharアーカイブを作成します。
134createPharArchiveFromDirectory();

Phar::buildFromIteratorメソッドは、指定されたファイルやディレクトリの構造を元に、単一のPharアーカイブを効率的に作成するための機能です。このメソッドは、ファイルシステムを走査するイテレータ(Traversable)を受け取り、そのイテレータが提供するファイル群をまとめてPharアーカイブに格納します。

第一引数$iteratorには、アーカイブに含めるファイルやディレクトリのリストを順次提供するTraversableインターフェースを実装したオブジェクトを指定します。通常、RecursiveDirectoryIteratorRecursiveIteratorIteratorを組み合わせて、特定のディレクトリ配下の全ファイルを対象とします。

第二引数$baseDirectoryはオプションで、指定するとPharアーカイブ内部でのファイルのパスを調整できます。このパスを基準に、アーカイブ内のファイルパスが相対的に表現されるため、Phar内の構造を分かりやすく保てます。例えば、/var/www/my_app/src/file.php$baseDirectory/var/www/my_appの場合、Phar内ではsrc/file.phpとして格納されます。

このメソッドは、正常にPharアーカイブへ追加されたファイルの一覧を連想配列で返します。配列のキーはPharアーカイブ内部でのファイルのパス、値は元のファイルシステム上でのフルパスとなります。

PharアーカイブはPHPアプリケーションを単一ファイルとして配布するのに役立ちますが、作成にはPHP設定ファイルでphar.readonly = Offを設定する必要があります。セキュリティ上、本番環境ではOnが推奨されるため、開発時のみ一時的に変更するなどの注意が必要です。

Phar::buildFromIteratorを利用する際は、PHP設定のphar.readonlyOffになっていることを確認してください。デフォルトではOnのため、アーカイブ作成時に一時的な設定変更が必要な場合がありますが、本番環境ではセキュリティのためにOnに戻すことが強く推奨されます。

このメソッドの第二引数$baseDirectoryは、Pharアーカイブ内にファイルを格納する際のパス階層を決定する重要な役割を持ちます。これを指定することで、元のファイルシステムのパス構造から指定ディレクトリ以下の相対パスでファイルがアーカイブに格納されます。

ディレクトリ構造をPharに含めるためには、RecursiveDirectoryIteratorRecursiveIteratorIteratorを組み合わせて使用し、目的のファイルを効率的に走査するパターンを理解することが重要です。また、Phar作成時のエラーを適切に処理するtry-catch文の利用や、作成した一時ファイルやディレクトリを確実に削除する後片付けの習慣も身につけましょう。

Phar::buildFromIteratorでファイルをパッケージ化する

1<?php
2
3/**
4 * Phar::buildFromIterator メソッドのサンプルコード。
5 * イテレータを使って複数のファイルをPharアーカイブにパッケージ化する方法を示します。
6 *
7 * キーワード「php iterator to array」に対する関連性:
8 * - buildFromIterator メソッドは Traversable (イテレータ) を引数に取ります。
9 * - メソッドの戻り値は、アーカイブに追加されたファイルのパスの配列です。
10 */
11
12function createPharArchiveFromIterator(): void
13{
14    // 一時ディレクトリとPharファイルの設定
15    $tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phar_test_' . uniqid();
16    $pharFile = $tempDir . '.phar';
17
18    // 既存のPharファイルを削除(テストの繰り返し実行のため)
19    if (file_exists($pharFile)) {
20        unlink($pharFile);
21    }
22    // 関連ファイル(圧縮されたPharファイルや署名ファイル)もクリーンアップ対象とする
23    if (file_exists($pharFile . '.gz')) {
24        unlink($pharFile . '.gz');
25    }
26    if (file_exists($pharFile . '.sig')) {
27        unlink($pharFile . '.sig');
28    }
29
30    // 一時ディレクトリを作成し、サンプルファイルを作成
31    if (!mkdir($tempDir, 0777, true) && !is_dir($tempDir)) {
32        throw new RuntimeException(sprintf('Directory "%s" was not created', $tempDir));
33    }
34
35    file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'file1.txt', 'Content of file 1.');
36    // サブディレクトリを作成し、その中にファイルを追加
37    mkdir($tempDir . DIRECTORY_SEPARATOR . 'subdir');
38    file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'subdir' . DIRECTORY_SEPARATOR . 'file2.txt', 'Content of file 2.');
39
40    echo "一時ディレクトリ '{$tempDir}' とサンプルファイルを作成しました。\n";
41
42    try {
43        // Pharオブジェクトを作成
44        // 新しいPharファイルを作成するため、書き込みモードで開きます。
45        $phar = new Phar($pharFile);
46        // デフォルトのスタブを設定することで、Pharファイルを直接PHPスクリプトとして実行可能にします。
47        $phar->setStub($phar->createDefaultStub());
48
49        // イテレータの準備 (ジェネレータ関数を使用)
50        // buildFromIterator メソッドは、通常「アーカイブ内でのパス => ファイルシステム上の絶対パス」
51        // の形式のペアを返すイテレータを期待します。
52        $filesToArchiveGenerator = function (string $baseDir): Traversable {
53            // RecursiveDirectoryIterator で指定されたディレクトリを再帰的に走査します。
54            // SKIP_DOTS を指定することで、"." や ".." ディレクトリをスキップします。
55            $directory = new RecursiveDirectoryIterator($baseDir, RecursiveDirectoryIterator::SKIP_DOTS);
56            $iterator = new RecursiveIteratorIterator($directory);
57            // ファイルシステム上の絶対パスから、Pharアーカイブに含める相対パスを計算するための基準パス長
58            $basePathLen = strlen(rtrim($baseDir, DIRECTORY_SEPARATOR)) + 1;
59
60            foreach ($iterator as $file) {
61                /** @var SplFileInfo $file */
62                // ディレクトリではなくファイルのみを対象とします。
63                if ($file->isFile()) {
64                    $absolutePath = $file->getPathname();
65                    // ファイルシステム上の絶対パスから、Pharアーカイブ内で使用する相対パスを計算します。
66                    $relativePath = substr($absolutePath, $basePathLen);
67                    // yield を使用して、(相対パス => 絶対パス) のペアを返すイテレータ要素を生成します。
68                    // これが buildFromIterator の入力となります。
69                    yield $relativePath => $absolutePath;
70                }
71            }
72        };
73
74        echo "Phar::buildFromIterator を呼び出します...\n";
75        // buildFromIterator メソッドを呼び出してPharアーカイブを構築します。
76        // 第一引数には、ファイルのペアを生成するイテレータ(ここではジェネレータ関数の呼び出し結果)を渡します。
77        // 第二引数には、イテレータが返すファイルの絶対パスの基準となるディレクトリを指定します。
78        // 戻り値は、Pharアーカイブに実際に追加されたファイルのパスの配列です。
79        $addedFiles = $phar->buildFromIterator($filesToArchiveGenerator($tempDir), $tempDir);
80
81        echo "Pharアーカイブ '{$pharFile}' を作成しました。\n";
82        echo "Phar::buildFromIterator が返した、アーカイブに追加されたファイルのパス:\n";
83        foreach ($addedFiles as $file) {
84            echo " - {$file}\n";
85        }
86
87        // Pharアーカイブを検証(オプション)
88        // 作成されたPharファイルの中身を実際に読み込んで確認します。
89        echo "\nPharアーカイブの中身を検証します:\n";
90        $pharContent = new Phar($pharFile);
91        foreach ($pharContent as $file) {
92            /** @var PharFileInfo $file */
93            echo " - {$file->getPathname()} (サイズ: {$file->getSize()} バイト)\n";
94        }
95
96    } catch (Exception $e) {
97        echo "エラーが発生しました: " . $e->getMessage() . "\n";
98    } finally {
99        // クリーンアップ
100        // 作成したPharファイル、署名ファイル、一時ディレクトリを削除します。
101        if (file_exists($pharFile)) {
102            unlink($pharFile);
103        }
104        if (file_exists($pharFile . '.gz')) {
105            unlink($pharFile . '.gz');
106        }
107        if (file_exists($pharFile . '.sig')) {
108            unlink($pharFile . '.sig');
109        }
110        if (is_dir($tempDir)) {
111            // ディレクトリとその内容を再帰的に削除します。
112            $files = new RecursiveIteratorIterator(
113                new RecursiveDirectoryIterator($tempDir, RecursiveDirectoryIterator::SKIP_DOTS),
114                RecursiveIteratorIterator::CHILD_FIRST
115            );
116            foreach ($files as $fileinfo) {
117                $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
118                $todo($fileinfo->getRealPath());
119            }
120            rmdir($tempDir);
121        }
122        echo "\nクリーンアップが完了しました。\n";
123    }
124}
125
126// スクリプトを実行
127createPharArchiveFromIterator();

PHP 8のPhar::buildFromIteratorメソッドは、複数のファイルをまとめてPharアーカイブ(PHPの実行可能なアーカイブファイル)として作成する際に使用されます。このメソッドは、ファイルパスのリストをイテレータ形式で受け取り、それらを効率的にアーカイブへ追加します。

第一引数Traversable $iteratorには、アーカイブに追加したいファイルの情報(アーカイブ内でのパス => ファイルシステム上の絶対パス)をペアとして返すイテレータを渡します。サンプルコードでは、ジェネレータ関数を用いて、指定されたディレクトリ内のファイルを再帰的に走査し、この形式で提供しています。第二引数?string $baseDirectoryは、イテレータが返すファイルの絶対パスの基準となるディレクトリを指定します。これにより、アーカイブ内のファイルの相対パスを適切に処理できます。メソッドの戻り値は、Pharアーカイブに実際に追加されたファイルのパスの配列です。

このサンプルコードでは、まず一時ディレクトリとそこに格納するサンプルファイルを作成します。次に、Pharオブジェクトを初期化し、buildFromIteratorメソッドにファイルのパスのペアを生成するイテレータ(ここではジェネレータ関数)を渡して、Pharアーカイブを構築します。これにより、指定されたファイルが単一のPharファイルとしてパッケージ化されます。最後に、作成されたアーカイブの内容を検証し、一時ファイルやディレクトリをクリーンアップします。この方法は、「php iterator to array」というキーワードが示すように、イテレータによって提供されるデータを処理し、結果としてファイルパスの配列を得る具体的な例として機能します。

Phar::buildFromIteratorメソッドを利用する際は、イテレータが「Phar内の相対パス => ファイルシステム上の絶対パス」の形式でキーと値のペアを返すように準備する必要があります。Pharファイルの作成や変更操作を実行するには、PHPの設定phar.readonlyOffに設定しておく必要がありますのでご注意ください。サンプルコードのように一時ファイルやディレクトリを生成する場合は、処理が終了した後にfinallyブロックなどで必ずこれらを適切にクリーンアップしましょう。Pharファイルは実行可能な形式であるため、信頼できないソースから取得したものを実行する際にはセキュリティリスクに留意し、十分注意してください。このメソッドの戻り値は、Pharアーカイブに実際に格納されたファイルの相対パスの配列です。

関連コンテンツ

関連IT用語

関連プログラミング言語