【PHP8.x】fpassthru()関数の使い方
fpassthru関数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
fpassthru関数は、開かれたファイルポインタから、残りのデータをすべて出力バッファへ直接出力する関数です。この関数は、fopen()などで作成されたファイルリソース(ファイルポインタ)を引数として受け取ります。ファイルポインタが指す現在の位置からファイルの終端(EOF)まで、残りの内容を順次読み込み、PHPの出力機構を通じて直接クライアント(例えばウェブブラウザ)へ送信します。
この関数の大きな利点は、特に大きなサイズのファイルを扱う際に、メモリ効率が非常に優れている点です。ファイルの内容をすべて一度にPHPのメモリに読み込むのではなく、データを読み出し次第すぐに出力するため、システムメモリの消費を最小限に抑えながら、効率的にファイルの内容を提供できます。
また、fpassthru関数はバイナリセーフであるため、テキストファイルはもちろんのこと、画像、音声、PDFといったあらゆる種類のバイナリファイルも、破損させることなく安全に出力することが可能です。関数は、出力したバイト数を整数で返します。もしエラーが発生した場合はfalseを返します。この関数を実行すると、ファイルポインタはファイルの終端に移動します。
構文(syntax)
1<?php 2$filePointer = fopen("php://temp", "r+"); 3fwrite($filePointer, "This text will be output directly by fpassthru."); 4fseek($filePointer, 0); 5fpassthru($filePointer); 6fclose($filePointer); 7?>
引数(parameters)
resource $stream
- stream: resource: 読み書き対象のストリームリソース
戻り値(return)
int|false
fpassthru関数は、ファイルポインタが指す位置からファイルストリームの終端までを標準出力にコピーします。成功した場合は、コピーされたバイト数が返されます。エラーが発生した場合はfalseが返されます。
サンプルコード
PHP fpassthru関数でファイルダウンロードする
1<?php 2 3// fpassthru() 関数を使用してファイルをブラウザに直接出力するサンプルコードです。 4// これは、Webアプリケーションでファイルダウンロード機能を提供する際によく使用されます。 5 6/** 7 * fpassthru() を使用して仮想ファイルをブラウザにダウンロードさせる関数。 8 * システムエンジニアを目指す初心者向けに、ファイルダウンロードの基本的な流れを示します。 9 */ 10function serveFileDownload(): void 11{ 12 // 1. ダウンロードするファイルの仮想内容とファイル名を定義 13 $fileContent = "これは fpassthru 関数によってダウンロードされるテストファイルです。\n"; 14 $fileContent .= "PHP 8 の環境で動作します。\n"; 15 $fileContent .= "このファイルはブラウザに直接ストリームされます。\n"; 16 $fileName = "sample_download.txt"; 17 18 // 2. 一時ファイルを作成し、その中に上記の内容を書き込む 19 // tempnam() は一意なファイル名を持つ一時ファイルを作成し、そのパスを返します。 20 // これにより、実際のファイルパスがなくても動作する、単体で実行可能なサンプルとなります。 21 $tempFilePath = tempnam(sys_get_temp_dir(), 'php_fpassthru_'); 22 if ($tempFilePath === false) { 23 // 一時ファイルの作成に失敗した場合の処理 24 http_response_code(500); 25 echo "エラー: 一時ファイルを作成できませんでした。"; 26 exit; 27 } 28 29 // file_put_contents() で一時ファイルに内容を書き込む 30 if (file_put_contents($tempFilePath, $fileContent) === false) { 31 // 書き込みに失敗した場合の処理 32 http_response_code(500); 33 echo "エラー: 一時ファイルへの書き込みに失敗しました。"; 34 unlink($tempFilePath); // 作成した一時ファイルを削除 35 exit; 36 } 37 38 // 3. 一時ファイルを読み込みモードで開く 39 // 'rb' はバイナリ読み込みモードを意味します。 40 $fileHandle = fopen($tempFilePath, 'rb'); 41 if ($fileHandle === false) { 42 // ファイルオープンに失敗した場合の処理 43 http_response_code(500); 44 echo "エラー: ファイルを開けませんでした。"; 45 unlink($tempFilePath); // 作成した一時ファイルを削除 46 exit; 47 } 48 49 // 4. HTTPヘッダを設定し、ブラウザにファイルをダウンロードさせる 50 // Content-Type: ダウンロードするファイルの種類を指定 (例: テキストファイル) 51 header('Content-Type: text/plain'); 52 // Content-Disposition: ブラウザにダウンロードを促し、ファイル名を指定 53 header('Content-Disposition: attachment; filename="' . $fileName . '"'); 54 // Content-Length: ファイルサイズを指定 (ダウンロードの進捗バーなどに利用される) 55 header('Content-Length: ' . filesize($tempFilePath)); 56 // その他のキャッシュ制御ヘッダ (ブラウザがファイルをキャッシュしないように推奨) 57 header('Cache-Control: public, must-revalidate, max-age=0'); 58 header('Pragma: public'); 59 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // 過去の日付を設定 60 61 // 5. fpassthru() を使って、ファイルポインタ ($fileHandle) から現在出力バッファへ 62 // 残りの全データを読み込み、出力します。これにより、ファイルの内容がHTTPレスポンスボディとして 63 // 直接クライアントに送信され、ダウンロードが開始されます。 64 fpassthru($fileHandle); 65 66 // 6. 使用済みファイルハンドルを閉じ、作成した一時ファイルを削除 67 fclose($fileHandle); 68 unlink($tempFilePath); 69 70 // 7. スクリプトの実行を終了 71 // これ以降に余計な出力がないようにするため、重要です。 72 exit; 73} 74 75// 関数を実行し、ファイルダウンロードを開始します 76serveFileDownload();
PHPのfpassthru関数は、ファイルポインタが指す現在位置からファイルの最後までを読み込み、その内容を直接出力バッファへ書き出すための関数です。この関数は、Webアプリケーションでユーザーにファイルをダウンロードさせる際など、サーバー上のファイルをクライアントに効率的にストリーム配信する目的で利用されます。
引数にはresource $streamを受け取ります。これはfopen()関数などで読み込み用に開かれたファイルポインタ(ファイルハンドル)を指し、fpassthru関数はこのファイルポインタを使ってファイルを読み込みます。戻り値は、出力されたバイト数を示す整数値、または処理が失敗した場合にはfalseを返します。
サンプルコードでは、一時ファイルに任意のコンテンツを書き込み、その一時ファイルをfopen()で開いてファイルポインタを取得した後、fpassthru()関数を用いてブラウザに直接出力する具体的な方法を示しています。特に重要なのは、ダウンロードを促すためのContent-TypeやContent-DispositionといったHTTPヘッダを事前に設定することです。これにより、ブラウザは受け取ったデータをファイルとして認識し、指定されたファイル名でダウンロードを開始します。fpassthruが実行されると、ファイルの残りの全内容がクライアントに送信され、その後ファイルハンドルを閉じ、一時ファイルを削除して処理を終了します。この一連の処理により、サーバー側でファイルをメモリにすべて読み込むことなく、効率的にファイルを配信できます。
fpassthru()関数を利用したファイルダウンロード機能では、まずheader()関数で適切なHTTPヘッダを設定することが最も重要です。特にContent-TypeやContent-Dispositionは、ブラウザがファイルを正しく認識し、指定した名前でダウンロードするために不可欠です。ヘッダ送信は出力より前に行う必要があるため、fpassthru()やheader()の前に余計な空白やechoなどがないよう、スクリプト全体に注意してください。一時ファイルや開いたファイルハンドルは、使用後に必ずunlink()やfclose()で適切に解放します。エラー発生時には適切なHTTPステータスコードを返し、最後にexit;でスクリプトを確実に終了させることが、安全な運用とダウンロードの安定性を保つための重要なポイントです。
PHP fpassthruで大容量ファイルを効率的にダウンロードする
1<?php 2 3/** 4 * 指定された大容量ファイルをブラウザに直接ストリーミングしてダウンロードさせる関数。 5 * fpassthru() を使用することで、ファイル全体をメモリにロードすることなく効率的に転送します。 6 * 7 * @param string $filePath ダウンロードするファイルの絶対パスまたは相対パス。 8 * @param string|null $downloadFileName ブラウザに表示されるダウンロードファイル名。省略した場合は元のファイル名を使用します。 9 * @return void 10 */ 11function streamLargeFileForDownload(string $filePath, ?string $downloadFileName = null): void 12{ 13 // 1. ファイルの存在チェック 14 if (!file_exists($filePath)) { 15 http_response_code(404); 16 echo "Error: File not found."; 17 exit; 18 } 19 20 // 2. ダウンロード時のファイル名を決定(指定がなければ元のファイル名を使用) 21 $downloadFileName = $downloadFileName ?? basename($filePath); 22 23 // 3. 出力バッファリングをクリアし、不要な出力がファイルダウンロードを妨げないようにする 24 // これにより、HTTPヘッダーやファイルデータ以外のコンテンツが送信されるのを防ぎます。 25 if (ob_get_level()) { 26 ob_end_clean(); 27 } 28 29 // 4. HTTPヘッダーを設定し、ブラウザにファイルをダウンロードさせる指示を出す 30 header('Content-Description: File Transfer'); 31 header('Content-Type: application/octet-stream'); // バイナリファイルの一般的なMIMEタイプ 32 header('Content-Disposition: attachment; filename="' . $downloadFileName . '"'); // ダウンロード時のファイル名を指定 33 header('Content-Transfer-Encoding: binary'); 34 header('Expires: 0'); // キャッシュを無効化 35 header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); // キャッシュを無効化 36 header('Pragma: public'); // キャッシュを無効化 37 header('Content-Length: ' . filesize($filePath)); // ファイルサイズをブラウザに正確に伝える 38 39 // 5. ファイルを読み込みモード(バイナリモード)でオープン 40 $fileHandle = fopen($filePath, 'rb'); 41 42 if ($fileHandle === false) { 43 http_response_code(500); 44 echo "Error: Could not open file."; 45 exit; 46 } 47 48 // 6. fpassthru() を使って、ファイルの内容を直接出力ストリームに書き込む 49 // この関数は、ファイルポインタからファイルの終端まですべてのデータを読み込み、 50 // 現在の出力バッファへ書き込みます。readfile() とは異なり、ファイルを一度にメモリに 51 // ロードしないため、特に大容量ファイルのストリーミングに適しています。 52 fpassthru($fileHandle); 53 54 // 7. ファイルポインタを閉じる 55 fclose($fileHandle); 56 57 // 8. スクリプトの実行を終了する(これ以降のコードは実行されません) 58 exit; 59} 60 61// -------------------------------------------------------------------------- 62// サンプルコードの使用例 63// 以下のコードは、このスクリプトと同じディレクトリにダミーの大きなファイルを作成し、 64// それをダウンロードさせるデモンストレーションです。 65// -------------------------------------------------------------------------- 66 67// ダウンロード対象のファイルパスを指定 68$sourceFilePath = __DIR__ . '/example_large_file.txt'; 69 70// テスト用のダミーファイルを作成 (もし存在しなければ) 71// この部分は、実際のシステムでは既存のファイルを指定するため不要です。 72if (!file_exists($sourceFilePath)) { 73 // 約6MBのダミーファイルを作成 74 $dummyContent = str_repeat('This is a line of dummy content for a large file example. ', 100000); 75 file_put_contents($sourceFilePath, $dummyContent); 76 echo "Created a dummy large file for demonstration: " . basename($sourceFilePath) . "<br>"; 77} 78 79// ファイルダウンロードを実行 80// ブラウザでこのPHPファイルにアクセスすると、'downloaded_large_file.txt' という名前で 81// 'example_large_file.txt' の内容がダウンロードされます。 82streamLargeFileForDownload($sourceFilePath, 'downloaded_large_file.txt'); 83 84// streamLargeFileForDownload 関数内で `exit;` が呼び出されるため、 85// この行以降のコードは実行されません。 86// echo "This line will not be executed.";
PHPのfpassthru関数は、指定されたファイルポインタ(resource $stream)から読み込んだデータを、直接出力ストリーム(通常はウェブブラウザ)へ書き出すための機能です。この関数は、特に大容量ファイルをウェブ上でダウンロードさせる際に非常に役立ちます。その最大の利点は、ファイル全体をサーバーのメモリに一度にロードすることなく、データを効率的に転送できる点です。これにより、メモリの消費を抑えながら、スムーズなファイル配信を実現します。処理が成功すると、書き込まれたバイト数を整数(int)で返し、失敗した場合はfalseを返します。提供されたサンプルコードでは、このfpassthru関数を用いて、ダウンロード用のHTTPヘッダーを設定した後に、fopenで開いた大容量ファイルをブラウザに直接ストリーミングし、ユーザーにダウンロードさせる一連の流れを示しています。
このコードは、fpassthruを使って大容量ファイルを効率的にストリーミングダウンロードする際に役立ちます。最も重要な点は、fpassthru実行後に必ずexit;でスクリプトを終了させ、ダウンロードファイルに不要な出力が混ざるのを防ぐことです。また、Content-TypeやContent-Dispositionなど、ブラウザがファイルを正しく処理するためのHTTPヘッダーを正確に設定してください。ダウンロード前にob_end_clean()で既存の出力バッファをクリアし、fopenで開いたファイルハンドルは忘れずにfcloseで閉じることも重要です。もしダウンロード対象のファイルパスがユーザー入力によるものなら、セキュリティ上の脆弱性を避けるために厳密な検証が不可欠です。
PHP fpassthru vs readfile 比較
1<?php 2 3declare(strict_types=1); 4 5/** 6 * fpassthru() と readfile() の違いを比較するためのサンプルコード 7 * 8 * readfile() は、指定したファイルの内容をすべて読み込み、直接出力します。 9 * 一方で fpassthru() は、すでに開かれているファイルポインタを受け取り、 10 * そのポインタの「現在位置」からファイルの終端までを出力します。 11 * 12 * この性質を利用すると、ファイルの一部(例えばヘッダー行)を先に読み込んで処理し、 13 * 残りの部分だけを効率的に出力する、といった柔軟な制御が可能になります。 14 */ 15function demonstrateFpassthruVsReadFile(): void 16{ 17 // デモ用の一時ファイルを作成 18 $filename = tempnam(sys_get_temp_dir(), 'demo_'); 19 if ($filename === false) { 20 echo "一時ファイルの作成に失敗しました。\n"; 21 return; 22 } 23 24 // ファイルに複数行のコンテンツを書き込む 25 $content = "Header: This is the first line to be processed separately.\n"; 26 $content .= "Data: This is the second line.\n"; 27 $content .= "Data: This is the third line.\n"; 28 file_put_contents($filename, $content); 29 30 echo "--- fpassthru() の使用例 ---\n"; 31 32 // ファイルを読み込みモード('r')で開く 33 $stream = fopen($filename, 'r'); 34 if ($stream === false) { 35 echo "ファイルのオープンに失敗しました。\n"; 36 unlink($filename); // 後片付け 37 return; 38 } 39 40 try { 41 // 1. ファイルの最初の行だけを fgets() で読み込む 42 // これにより、ファイルポインタが2行目の先頭に移動する 43 $headerLine = fgets($stream); 44 echo "先に読み込んで処理した行: " . htmlspecialchars(trim($headerLine)) . "\n\n"; 45 46 // 2. fpassthru() を使い、ポインタの現在位置からファイルの終端までを出力する 47 echo "残りの部分を fpassthru() で出力します:\n"; 48 // fpassthru() は出力した文字数を返す 49 $bytes = fpassthru($stream); 50 51 echo "\nfpassthru() によって出力されたバイト数: " . $bytes . "\n"; 52 } finally { 53 // 3. ファイルポインタを閉じる 54 fclose($stream); 55 // 4. 作成した一時ファイルを削除する 56 unlink($filename); 57 } 58 59 echo "\n--- (比較) readfile() の場合 ---\n"; 60 echo "もし readfile('{$filename}') を使った場合、\n"; 61 echo "ファイルの内容が区別なく一度にすべて出力されます。\n"; 62} 63 64// 関数を実行してデモを表示 65demonstrateFpassthruVsReadFile();
PHP 8 の fpassthru 関数は、すでに開かれているファイルポインタ(resource型の $stream 引数で指定)が指す「現在位置」から、ファイルの終端までを直接出力するための関数です。この関数は、Webサーバーからクライアントへファイルを直接送る際や、大きなファイルを部分的に処理して出力する場合に特に役立ちます。
引数 $stream には、fopen() などで開かれた有効なファイルポインタを渡します。このポインタが指す現在位置から出力が開始されるため、例えばファイルのヘッダー部分を事前に fgets() などで読み込んで処理した後、残りのデータ部分だけを fpassthru で効率的に出力する、といった柔軟な制御が可能です。
関数が成功すると、出力されたバイト数が整数値として返されます。ファイルを開けない、または何らかの理由で出力に失敗した場合は false が返されます。
同種の機能を持つ readfile() 関数が、指定されたファイル全体を常に最初から出力するのに対し、fpassthru はファイルポインタの現在位置を利用することで、ファイルの内容の一部を読み飛ばしたり、特定の部分だけを出力したりできる点で異なります。これにより、大量のデータを含むファイルを扱う際に、メモリ消費を抑えつつ効率的なデータ転送を実現できます。
fpassthru関数は、すでに開かれたファイルポインタの「現在の位置」から終端までを直接出力します。readfile関数とは異なり、ファイル全体を常に最初から出力するわけではないため、ファイルの一部だけを先に処理し、残りを効率的に出力するといった柔軟な制御が可能です。
この関数を使用する際は、fopenで開いたファイルポ゚インタを必ずfcloseで閉じる必要があります。また、一時ファイルを使用する場合はunlinkで削除するなど、ファイルリソースの管理を徹底することが重要です。これを怠ると、リソースリークや不要なファイルが残る原因となります。
fpassthruは、正常に処理を終えると出力したバイト数を返しますが、失敗した場合はfalseを返します。そのため、戻り値を確認し、適切にエラーハンドリングを行うことで、より堅牢なコードになります。