発展編02

ファイルを公開する

ここではサーバ上にあるドキュメントを公開するスクリプトを見ていきましょう。

public.phpはディレクトリの情報を読み取り、内容を表示するスクリプトです。output.phpはクリックなどで選択された項目のファイルをクライアント側に出力するスクリプトです。同階層に「document」フォルダとを作成し、そのフォルダ内にさらにいくつかのフォルダとファイルを作成します。そしてその各フォルダ内には適当な数のファイルを作成します。

public.php

    <html>
    <html>
    <head>
    <title>ファイル公開リスト</title>
    </head>
    <body>

    <h1>
        ファイル公開リスト
    </h1>

    <table border="1" cellspacing="0" cellpadding="0">
        <tr>
            <td>
                <?php

                    if(isset($_POST['directory'])) {

                        $currentpath=$_POST['directory'];

                        if($currentpath!="./document") {

                ?>

                <div style="text-align:right;">
                    <form name="fa" method="POST" action="public.php">
                        <input type="hidden" name="directory" value="<?php echo(dirname($currentpath)) ;?>" />
                        <a href="javascript:document.fa.submit();">上位階層ディレクトリ</a>
                    </form>
                </div>
                <?php

                    }
                } else {

                    $currentpath ="./document";

                }

                ?>
            </td>
        </tr>
        <tr>
            <td>
                <table border="1" cellspacing="0" cellpadding="0" summary="ファイル公開リスト">
                    <tr>
                        <th>ファイル名</th>
                        <th>ファイル形式</th>
                        <th>ファイルサイズ</th>
                        <th>ファイルへの最終アクセス日</th>
                        <th>ファイルの最終更新日</th>
                    </tr>
                    <?php

                        $d = dir($currentpath);

                        while($regist = $d->read()) {

                            $count++;

                            if($regist!="." && $regist!="..") {

                                $str = $currentpath."/".$regist;

                                $arrayinfo = pathinfo(mb_convert_encoding($str,"EUC-JP","UTF-8"));

                                if(is_dir($str)) {

                    ?>
                    <form name="fa<?php echo($count); ?>" method="POST" action="public.php">
                    <tr>
                        <td>
                            <input type="hidden" name="directory" value="<?php echo($currentpath."/".$regist); ?>" />
                            <a href="javascript:document.fa<?php echo($count); ?>.submit();">
                                <?php echo($arrayinfo["basename"])?>
                            </a>
                        </td>
                        <td>
                            ディレクトリ
                        </td>
                        <td>
                            <br />
                        </td>
                        <?php } else { ?>
                        <form name="fa<?php echo($count); ?>" method="POST" action="output.php">
                    </tr>
                    <tr>
                        <td>
                            <input type="hidden" name="directory" value="<?php echo($currentpath."/".$regist);?>" />
                            <a href="javascript:document.fa<?php echo($count); ?>.submit();">
                                <?php echo(str_replace(".".$arrayinfo["enhance"],"",$arrayinfo["basename"])) ;?>
                            </a>
                        </td>
                        <td>
                            <?php echo(strtoupper($arrayinfo["enhance"])); ?>ファイル
                        </td>
                        <td>
                            <?php echo(round(filesize($str)/1024)); ?>KB
                        </td>
                        <?php
                            }

                            echo("<td>".date("Y/m/d H:i:s",fileatime($str))."</td>");
                            echo("<td>".date("Y/m/d H:i:s",fileatime($str))."</td>");

                            echo("</tr></form>");

                            }

                        }

                        $d->close();

                        ?>

                </table>
            </td>
        </tr>
    </table>

    </body>
    </html>
    

public.php「if(isset($_POST['directory'])) {」から「</form></div>」はまず、現在表示するディレクトリまでのパス(変数「$currentpath」)を特定しています。次に、isset関数によって、「$_POST['directory']」に値がセットされているかを判断します。隠しフィールド(hidden)の指定がない場合は変数「$currentpath」に上位階層の「./document」を格納します。「$_POST['directory']」に値がセットされている場合は、そのセットされている値を変数「$currentpath」」に格納し、上位階層ディレクトリパス(「dirname($$currentpath)」)を自分自身に遷移するリンクを作成します。

「$d = dir($currentpath);」は現在場所のディレクトリのオブジェクトを生成します。

「$arrayinfo = pathinfo(mb_convert_encoding($str,"EUC-JP","UTF-8"));」のpathinfo関数は取得したサブディレクトリ、ファイルの持つ情報(ファイルのパスなど)を配列として取得します。

「if(is_dir($str)) {」から「<a href="javascript:document.fa<?php echo($count); ?>.submit();"><?php echo($arrayinfo["basename"])?></a>」は取得したファイルの要素がディレクトリであった場合、public.htmlのリンク、文字列の「ディレクトリ」を出力します。また、隠しフィールド(hidden)としてサブディレクトリのパスを引き渡します。

「<?php } else { ?>」から「<td><?php echo(round(filesize($str)/1024)); ?>KB</td>」は取得したファイルの要素がファイルの場合、output.phpへのリンク、拡張子、ファイルのサイズを出力します。output.phpに引き渡すファイル名は隠しフィールド(hidden)で指定しています。またファイルサイズは「<?php echo(round(filesize($str)/1024)); ?>KB」によりキロバイトに変換しています。

「$d->close();」により開いたディレクトリへの参照を閉じています。

output.php

    <?php

        header("content-type: application/octet-stream");

        header("content-disposition: attachment; filename=".basename($_POST['directory']));

        $file = fopen($_POST['directory'],"rb");

        echo(fread($file,filesize($_POST['directory'])));

        fclose($file);

    ?>
    

「header("content-type: application/octet-stream");」の「application/octet-stream」と指定することで、クライアント側は通常のバイナリデータと認識し、ファイル名をクリックした際、ダウンロード用ダイアログを表示します。

「header("content-disposition: attachment; filename=".basename($_POST['directory']));」の「content-disposition」はコンテンツの内容を決定するもので、このスクリプトではダウンロードするファイル名をセットしています。

「$file = fopen($_POST['directory'],"rb");の「fopen($_POST['directory'],"rb");」はfopen関数で開く対象のデータがバイナリデータである場合を想定し、「b」を記述する必要があります。このスクリプトでは「rb」と記述しています。これによって、ファイルをバイナリモードであり、読み取り専用として開いています。

バイナリモードとは、ファイルをバイナリファイルとして扱うモードのことです。バイナリファイルとは、文字情報以外のすべての値を含むファイルのことで、実行形式のファイル、画像ファイルなどです。

「(fread($file,filesize($_POST['directory']));」はfread関数によって第二引数で、指定されたバイト数だけを読み込みます。ここではfilesize関数によって、対象のファイルサイズを取得していますので、ファイル全体を読み込みます。そして、ファイルの内容は、ダウンロードデータとして、出力されます。

実行結果

ファイル公開リスト(上位階層ディレクトリ)

ファイルが情報が取得され、出力されていることがわかります。

ファイル公開リスト(1.topディレクトリ内)

「1.top」をクリックしました。「1.top」のサブフォルダに入っています。また、サブフォルダに入りましたので、「上位階層」へのリンクも出現しています。

ダウンロードダイアログ

「001test」をクリックしダウンロードダイアログを出現させました。

エラーページを変更する。

ここではエラーページを変更するスクリプトを見ていきましょう。

エラーログを保存するためのディレクトリをスクリプトと同階層に作成します。

testerror.phpは作成したエラーを発生させるためのスクリプトです。errordefinition.phpはエラーの処理関数を記述したインクルードファイルです。error.phpはエラー発生時に表示させる変更したエラーページです。

testerror.php

    <?php

        require_once("errordefinition.php");

        function mkErr($num = "") {

            if(!is_numeric($num)) {

                trigger_error("設定した値は数値ではありません!",E_USER_ERROR);

            }

            if($num < 50 || $num > 150) {

                trigger_error("設定した値が範囲内にありません!",E_USER_WARNING);

            }

            if($num == "") {

                trigger_error("設定値が設定されていません!",E_USER_NOTICE);

            }

        }

        mkErr();

    ?>
    

「require_once("errordefinition.php");」はエラー処理の関数を記述した「errordefinition.php」をインクルードします。

「function mkErr($num = "") {」から「if($num == "") { trigger_error("設定値が設定されていません!",E_USER_NOTICE); } }」は カスタマイズしたエラーを発生させる仕組みを定義しています。trigger_error関数は第一引数にエラーメッセージを記述し第二引数にエラーの種類を設定することで、ユーザーがカスタマイズしたエラーを発生させることができます。

「mkErr();」は定義したmkErr関数を呼び出し、エラーを発生させます。

errordefinition.php

    <?php

        error_reporting(0);

        set_error_handler("testerror");

        function testerror($error_no,$error_messa,$file_nam,$line_num) {

            $error_str = array (
                1 => "実行時のエラー",
                2 => "実行時の警告",
                4 => "解析時のエラー",
                8 => "解析時の警告",
                256 => "ユーザー定義エラー",
                512 => "ユーザー定義警告"
            );

            $usr_error = "$errstr[$error_no]\t$error_messa\t$file_nam\t$line_num\n";

            $usr_error. = var_export(debug_backtrace(),TRUE);

            $str_flag = "./errorlog/error".date("Ymd").".log";

            error_log($usr_error,4,$str_flag);

            if($error_no == E_USER_ERROR) {

                error_log("$error_messa\t$file_nam\tline_num",0);

            }

            require_once("error.php");

        }

    ?>
    

「error_reporting(0);」のerror_reporting関数は表示するエラーレベルを指定します。「0」はすべてのエラーを無視します。

「set_error_handler("testerror");」のset_error_handler関数はエラーが発生した時に、呼び出すエラー処理関数を呼び出します。ここでは「"testerror")」を呼び出しています。"testerror"は引数として「$error_no(エラーの番号)」「$error_messa(エラーメッセージ)」「$file_nam(エラーが発生したファイル名)」「$line_num(エラーが発生した行番号)」を受け取ります。これら引数には、エラーが発生した時に、対応した値が入ります。

$error_str = array ( 1 => "実行時のエラー", 2 => "実行時の警告", 4 => "解析時のエラー", 8 => "解析時の警告", 256 => "ユーザー定義エラー", 512 => "ユーザー定義警告" ); はエラー番号に対するエラーの種類を配列に入れておきます。

「$usr_error = "$errstr[$error_no]\t$error_messa\t$file_nam\t$line_num\n";」はファイルに書き込む対応したデータを変数にセットします。「¥t」によりタブで区切ります。

「$usr_error. = var_export(debug_backtrace(),TRUE);」はエラーのトレース情報を書き込みます。var_export関数によって連想配列に入っている要素を文字列に変換し、変数「$usr_error」に追記しています。var_export関数の第二引数はTRUEで変換される内容をそのまま表示し、FALSEで文字列をとして表示します。debug_backtrace関数エラー発生時、情報を連想配列として返します。

「$str_flag = "./errorlog/error".date("Ymd").".log";);」はエラーのログを記録するファイルを生成します。

「error_log($usr_error,4,$str_flag);」は変数$usr_errorに内容を追記で書き込みします。error_log関数はファイルに記録をする、メールアドレスに送信する機能があります。

「if($error_no == E_USER_ERROR) { error_log("$error_messa\t$file_nam\tline_num",0); }」 はエラーの種類が「エラー」である場合、イベントログ「0」にエラー情報を書き込みます。

「require_once("error.php");」はエラー記録処理が完了したあと変更したエラーページであるスクリプトerror.phpを出力します。

error.php

    <html>
    <head>
    <title>エラー発生</title>
    </head>
    <body>
    <h1>
        エラー発生
    </h1>

    <p>
        現在の作業領域において、エラーが発生しました。
    </p>
    <p>
        エラーが改善されない場合は、<a href="mailto:deffd@ddd.com">こちらのサポートセンター</a>まで
        ご一報ください。
    </p>

    </body>
    </html>
    

上記のerror.phpはエラーが発生した場合に、表示する変更したエラーページです。このerror.phpにはスクリプトと呼ばれるものは記述されていません。

変更したエラーページ

うまく実行できれば、上記のようなエラー画面が表示されるはずです。また、スクリプトと同階層に作成したディレクトリには、エラーログが書き込まれたファイルが生成されているはずです。