読者です 読者をやめる 読者になる 読者になる

Session, Flowの中でファイル・アップロードを扱う

PHP

 PHPオブジェクトの特殊メソッドに__wakeupと__sleepというものがありますが、あまり有効に利用されていないような気がします。
 __sleepを利用した例として、次のような事例を考えてみました。


 PHPのファイルアップロードで作成されるテンポラリファイルは、スクリプト実行終了後に消去されてしまう一時ファイルなので、開発者は、ファイルアップロードを受け付ける場合には、move_uploaded_fileを使ってファイルを別の場所に移動しなくてはなりません。この処理を(本当に上手く行くかどうかは分かりませんが)、Session配列と__sleep特殊メソッドを使って、自動化してみます。

<?php
class Piece_Unity_File
{
    function Piece_Unity_File($file)
    {
        foreach($file as $key => $value) {
            $this->$key = $value;
        }
    }
    
    function __sleep()
    {
        $base = basename($this->tmp_name);
        if (strncmp($base, 'piece', 5) != 0) {
            $dir = dirname($this->tmp_name);
            $newName = uniqid('piece'). '_'. time();
            $newFullpath = "{$dir}/{$newName}";
            move_uploaded_file($this->tmp_name, $newFullpath);
            $this->tmp_name = $newFullpath;
        }
        
        return array_keys(get_object_vars($this));
    }
}
?>

 このクラスは、コンストラクタ引数として、PHPのファイルアップロード配列を受け取ります。そして、このクラスのオブジェクトがセッションに格納される時には、__sleepが呼ばれ、ファイルアップロード・ディレクトリ(php.ini, upload_tmp_dirで指定されているディレクトリ)から、任意のディレクトリにファイルを移動します。(ただし、上のコードでは、upload_tmp_dirと同じディレクトリに移動しています。)


 こうすることによって、本来ならば揮発的なアップロードファイルと$_FILESを、セッションの中で半永続的に扱うことができます。
 例えば、次のようにSessionに格納することで、スクリプト実行が終了するタイミングで、自動的にmove_uploaded_fileを呼ぶことができます。

$_SESSION['myfile'] = &new Piece_Unity_File($_FILE('myfile'));

 以降、$_SESSION['myfile']を通して、アップロードされたファイルにアクセスすることができるはずです。


 上で述べたことを確認するため、Piece_Unityのコードにアドホックなパッチを当てた上で、動作を確認してみます。
 最初にアクセスすると、次のようにファイルアップロードを要求する画面が表示されます。



 何かファイルをアップロードして、次の画面に進みます。



 詳細は省きますが、Piece Frameworkのフロー属性(実装上はセッションとみなしてよい)にアップロードされたファイルオブジェクトを保存したおかげで、フレームワーク使用者は特に意識することなく、次の画面で、最初にアップロードしたファイルにアクセスできます。


 Page 2に表示されている/var/tmp/phpXXXXXというファイルは、スクリプト終了後に消去されていしまいますが、Page 3に表示されている/var/tmp/pieceXXXXXというファイルは、__sleepを利用してmove_uploaded_fileで移動したフィアルなので、維持されています。フレームワーク使用者はこのファイルを利用して、アップロードされたファイルにアクセス可能です。

class MyAction extends Piece_Flow_Action
{
  function some_event() {
    $file = &$this->_flow->getAttribute('file');
  }
}


 逆に言うならば、セッション・フローの終了状態に達したならならば、フレームワーク使用者はアップロードファイルを、任意のディレクトリに移動し、テンポラリファイルを削除しなくてはなりません。
 おそらく、最初に掲載したコードは不十分で、実際にはセッション・フロー実行が途中で中断した場合を想定して、明示的に削除されなかったファイルを消去するような処理が必要になると思います。