tmpfile()関数が適切でない場合にPHPで一時ファイルを作成する方法

PHPプログラマーが一時ファイルを作成する必要がある場合、マニュアルでtmpfile()



関数を見つけ、例を検討した後、その最適な使用方法について考え始めます。 したがって、データをすぐに一時ファイルにアップロードする必要があり、変数を使用してデータを処理する必要がなかったのは、私と一緒でした。 ただし、 tmpfile()



がローカルファイルへのリンクではなく記述子を返すため、この方法で作成されたファイルを操作するのは不便です。 一時ファイルの構造をもう少し詳しく見て、私が遭遇した落とし穴を見てみましょう。







tmpfile()



関数は、 fopen()



と同様にリソースを作成し、 STDIO I / O ストリームで動作します 。 これは、 php://temp



一時ファイルを使用して後で作業するためのphp://temp



ストリームを開くことと同じです。 どちらの場合でも、ファイルはphp.ini



に登録されphp.ini



いる一時フォルダーに表示され、スクリプトの最後またはfclose()



を使用して事前に自動的に削除されます。







php:// tempを使用する場合、データサイズが2 MBを超えると、ファイルは一時フォルダーに作成されます。 これに先立ち、記録されたデータはすべてphp://メモリに保存されます。 この制限は、php:// temp / maxmemory:0ストリームをすぐに入力することで回避できます。 -PHP

一時ファイルの作成時にtmpfile()



およびfopen()



がストリームで機能するため、 stream_get_meta_data()



を使用してメタデータを抽出し、さらに操作するためにファイルへの実際のパスを見つけることができます。







 <?php //    $tmpfile = tmpfile(); //     $data = stream_get_meta_data($tmpfile); /* ... */
      
      





stream_get_meta_data()



が返す値についてはドキュメントに詳しく説明されていますが、ストリームに関連付けられているファイル名に関心があります。 配列のuri



キーで取得できます。







 Array ( [timed_out] => false [blocked] => true [eof] => false [wrapper_type] => plainfile [stream_type] => STDIO [mode] => r+b [unread_bytes] => 0 [seekable] => true [uri] => home\user\temp\phpDC08.tmp )
      
      





php://temp



の場合、メタデータからURIを取得することはできませんが、実際には、ファイルの重みが2 MBを超えると一時フォルダーに作成されます。 一時ファイルが物理的に保存されている場所と、ストリームを操作するときにどの名前で保存されているかを確認する他の方法はありません。







ファイルへの実際のパスは、一時ファイルをディスク上の別の場所に移動する必要が生じたときに必要になる場合があります。つまり、この方法で記録されたデータを保存します。 tmpfile()



がストリームにブロッキングモードを課し、 stream_set_blocking()



を介してキャンセルするため、実際には動作しないため、 rename()



でこれを行うことはできません。 もちろん、コピーすることで問題を解決できますが、スクリプトが終了する前に、一時ファイルを途中で削除しない場合、データのコピーが2つあります。







あるオブジェクトから別のオブジェクトにリソースをスローすることも、この実装にはインターフェースが必要になるため、あまり便利ではありません。 私の場合、 Fileクラスの一時ファイルの名前をSymfony HttpFoundationパッケージからコンストラクターのFileクラスに厳密に依存するオブジェクトに転送する必要がありました。 アプリケーションのビジネスロジックは、異なるレベルでファイルを検証することでした。ここで、検証が失敗した場合、パスの最初にあるファイルを削除するよう注意することが重要でした。 この段階で、 tmpfile()



関数は一時ファイルの作成に適していないことが明らかになりました。







別の解決策として、一時フォルダーにファイルを作成する任意のファイル操作自動削除のスキームに従って動作する独自のメカニズムを作成しましたtempnam()



関数を使用すると、PHPの一時フォルダーに一意の名前のファイルを作成できます。







 <?php //      $tmpfile = tempnam(sys_get_temp_dir(), 'php'); /* ... */
      
      





最初の引数はsys_get_temp_dir()



を介して一時フォルダーの場所を示し、2番目はファイル名のプレフィックスです。 このようなファイルは、0600(rw-)の権利で作成されているため、所有者のみが読み書きできます。 自動ファイル削除を実装するために、 __destruct()



を使用してファイルを削除しようとするクラスにさらにロジックを転送することを提案します。







 <?php class tmpfile { public $filename; public function __construct() { $this->filename = tempnam(sys_get_temp_dir(), 'php'); } public function __destruct() { @unlink($this->filename); } public function __toString() { return $this->filename; } } //    $tmpfile = new tmpfile; //      file_put_contents($tmpfile, 'Hello, world!'); /* ... */
      
      





__toString()



クラスで記述されているため、オブジェクトはtempnam()



関数が作成したファイルへのリンクを返します。 したがって、リソースを操作する必要がなくなりました。 オブジェクトへのすべてのリンクが解放されるか、スクリプトの完了時に、 致命的なエラーがスローされるか例外がスローされるまで、ファイル自体は削除されます。







デストラクタは、オブジェクトが破棄されるときに呼び出されます。 重大なエラーの場合、__ destruct()はPHP7以前では呼び出されない場合があります。 デストラクタは、オブジェクトを不安定な状態のままにしないでください。 したがって、PHPでは、オブジェクトを破棄および解放するためのハンドラーは互いに分離されています。 リリースハンドラは、オブジェクトが他のどこでも使用されていないことをエンジンが完全に確認したときに呼び出されます。 -PHP7のオブジェクト

デストラクタを使用してファイルを削除することはベストプラクティスではありません。これは、多くの利用可能なソリューションで使用されています。 確実にファイルを削除するために、関数を登録できます。この関数は、スクリプトが完了した後、どんな場合でも実行されます。 これは、クラスのコンストラクターでregister_shutdown_function()



を使用して行われます。







 <?php class tmpfile { public $filename; public function __construct() { $this->filename = tempnam(sys_get_temp_dir(), 'php'); register_shutdown_function(function () { @unlink($this->filename); }); } public function __toString() { return $this->filename; } } /* ... */
      
      





このアプローチにより、 tmpfile()



またはphp://temp



を使用せずに一時ファイルを作成できます。これは、OOPで非常に便利です。 すべてのロジックが単一のメソッドまたはクラスにカプセル化されているローカルの問題を解決するには、標準メソッドが推奨されます。







その結果、一時ファイルを操作するためのクラスができました。 Githubリポジトリにソースコードを投稿しました 画像 denisyukphp / tmpfileを追加し、クラスにCRUD操作のサポートを追加しました。 書き込みおよび読み取りのメソッドは、 file_put_contents()



およびfile_get_contents()



ラッパーです。 Composerを使用してプロジェクトに接続できます。







例を見る
 <?php require __DIR__ . '/vendor/autoload.php'; //    $tmpfile = new tmpfile; //    $tmpfile->write('Hello, world!'); //    $tmpfile->read(7, 5); //      new SplFileInfo($tmpfile); //     rename($tmpfile, __DIR__ . '/data.txt'); //     $tmpfile->delete(); /* ... */
      
      





Githubのリポジトリ

Packagistでのプロジェクト








All Articles