カテゴリー: PHP

  • 【PHP】header関数とErrorDocument 404

    WEBサイトの制作において、エラーページの表示は重要です。

    基本的には、404エラー等は、.htaccess に記述する、ErrorDocument により制御できます。

    たとえば、次のように .htaccessに記述します。

    ErrorDocument 404 /error/404.html

    基本中の基本ですね。ちなみに、遷移先のパスは相対パスにしないと404ステータスにならない場合があります。つまり、絶対パス(URL)ではなく、相対パスにするということです。

    他方で、PHPのスクリプトによっても、404ステータスを発行することが可能です。

    PHPの場合には、header関数により、次のように記述します。

    header("HTTP/1.0 404 Not Found");

    http://jp1.php.net/manual/ja/function.header.php

    上記の記述により、ブラウザに404ステータスを送出できます。

    しかし、ここで大きな問題に直面します。

    どういう問題かというと、PHPのheader関数により、404ステータスを送出した場合には、自動的には、/error/404.html には遷移しないのです。

    これは、少々、面倒なことになります。

    というのも、header(“HTTP/1.0 404 Not Found”); の後に、header(“Location: http://~”); とロケーションを付け足してやるとエラーページに遷移はするものの、ヘッダーステータスが302に変更され、404ではなくなってしまうからです。

    この対応策ですが、/error/404.htmlをfile_get_contents ()関数などで、読み込んで、出力するのも一つの方法となります。

    header("HTTP/1.0 404 Not Found");
    
    $error_page = file_get_contents('../error/404.html')
    
    echo $error_page;
    
    exit;

    こうすることで、一応、ErrorDocumentの場合とPHPの場合で同一の404ファイルを出力することができます。
    また、file_get_contentsの代わりに、require_once等でエラーファイルを読み込む方法も考えられます。

    なお、取得するファイルのパスは、サイトの構造により修正が必要です。

    もっとも、404ファイルを動的に作成している場合には、上手くいかない場合もあるので注意が必要です。

    この件で、他に良い方法があれば、コメントください。

    以上です。

  • 【JavaScript】メールフォームのスパム対策【PHP】

    WEBサイトを運営していてメールフォームを設置すると、遅かれ早かれスパム投稿に悩まされるようになります。

    メールフォームのスパム対策には、画像認証を付けるなど、いくつかの方法があるのですが、より簡単に実装できるのが、JavaScriptによるフォーム値の上書き方法です。

    どういう方法かというと、フォームにhiddenタグを埋め込みトークンを値にセットします。このトークン値をPOSTされる際に、JavaScriptにより書き換えてしまうというものです。

    以下、具体例です。

    (略)
    <input id="token" type="hidden" name="token" value="some_token">
    
    <input onclick="change_token();" type="submit" value="送信">
    
    </form>

    上記コードには、hiddenタグに、トークンとして、some_tokenがセットされています。

    この状態で送信すると、原則的には、スパムは、token=some_token を送信してきます。

    この原則を、JavaScriptの関数により変更します。つまり、送信ボタンが押されたときに、トークンの値を書き換えて、その書き換え後の値でなければ、スクリプト側でスパムとして送信を弾くようにします。

    JavaScriptは次のようになります。(外部ファイルにする場合)

    function change_token(){
        var token = 10 * 10;
        document.getElementById('token').value = token;
    }

    ここでは、例として、トークンの値を10 * 10 の100 としています。送信されたスクリプト側(PHPなど)で、入力チェックをするのですが、このtokenの値が100でなければスパムによる送信と判断するわけです。

    スパムは、JavaScriptを実行しないことを利用したものですが、何も対策をしないよりは効果があると思います。

    以上です。

  • PHPを使いサーバー監視をする方法

    WEBサイトを運営する場合、気になるのがサーバーの死活状況です。

    サーバーがダウンしてユーザーに迷惑をかけてしまっては一大事です。
    ただ、そうかといって、ずっとサーバーの負荷状況を目視で確認するのも非現実的な話です。

    そこで、PHPスクリプトを組み、サーバーを監視します。

    ■ 監視方法の概要は次のようになります。

    ・ 甲サーバーを監視するため、外部乙サーバーからPHPスクリプトにより死活状況監視
    ・ 乙サーバーのPHPスクリプトは、CRONの仕組みにより定期的に実行
    ・ 甲サーバーにおいてエラーが生じていたらメールにて通知

    それでは、実際のPHPスクリプトです。

    <?php
    
    /**** サーバー設定 ****/
    
    mb_language('ja');
    mb_internal_encoding('UTF-8');
    
    /**** サーバー情報初期化 ****/
    
    $server_ip   = '20*.*72.*4.5*';
    $server_name = 'example.com';
    $page_url    = 'http://example.com/sample_page.html';
    $mail_to     = 'your-mail';
    $mail_from   = 'your-mail';
    
    /**** チェック開始 ****/
    
    $result = '';
    
    //■ 1.pingチェック
    $ping_command = 'ping -c 3 -w 5 '. $server_ip;
    
    if (strstr(`$ping_command`, '100% packet loss')) {
    
        $result .= '1.pingが停止しています。'."\n";
    }
    
    //■ 2.socketチェック
    $socket = @fsockopen($server_name, '80', $errno, $errstr, 5);
    
    if ($socket === FALSE) {
    
        $result .= '2.socketを接続できません。'.$errno.$errstr."\n";
    }
    
    //■ 3.webアクセスチェック
    if (!$res = @file_get_contents($page_url)) {
    
        $result .= '3.pageを取得できません。'."\n";
    }
    
    /**** メール送信 ****/
    
    //停止していればをメール送信
    if ($result != '') {
    
        $subject = 'エラー!停止しています。('. $server_name .')';
        mb_send_mail($mail_to, $subject, $result, 'From: '. $mail_from);
    
    } else {
    
        $subject = '正常に稼動してます。('. $server_name .')';
        mb_send_mail($mail_to, $subject, '正常稼動中', 'From: '. $mail_from);
    }

    それでは、少し補足です。

    ■ 1では、pingを送信し正常かを確認しています。もし、送信したパケットが100%失われていれば、エラーとします。

    通常だとpingは次のようなメッセージを生成します。

    PING 20*.*72.*6.*0 (20*.*72.*6.*0) 56(84) bytes of data. 
    64 bytes from 20*.*72.*6.*0: icmp_seq=1 ttl=64 time=2.71 ms 
    64 bytes from 20*.*72.*6.*0: icmp_seq=2 ttl=64 time=0.146 ms 
    64 bytes from 20*.*72.*6.*0: icmp_seq=3 ttl=64 time=0.143 ms
     --- 20*.*72.*6.*0 ping statistics --- 
    3 packets transmitted, 3 received, 0% packet loss, 
    time 2002ms rtt min/avg/max/mdev = 0.143/1.000/2.713/1.211 ms

    つまり、‘ping -c 3 -w 5 ‘ のコマンドを実行すると、-cオプションの回数だけ、-wオプションの秒数を限度にパケットを送信するのですが、そのパケットのロスの割合を出力します。このロスの割合が、100%ならば、エラーと判断するわけです。

    なお、pingのコマンドは、バッククオート`コマンド`で実行しています。(@マークのキーで出る記号です。)

    ■ 2では、サーバーにソケット接続して、正常に接続できるかを判断しています。

    @fsockopen($server_name, ’80’, $errno, $errstr, 5);の部分ですが、これは、指定したサーバーの80ポート(httpポート)にソケット接続するための関数を実行しています。

    引数に、$errno, $errstr などといきなり変数が出てきていますが、この変数に結果が参照で入るのでこのように指定しておきます。なお、5とあるのは、5秒を限度に接続を試みるというものです。

    ちなみに、先頭の@マークは、スクリプトエラーが生じた場合に、エラーメッセージを表示させないための記号です。

    ■ 3では、@file_get_contents($page_url)を実行しています。@マークは上と同様です。

    file_get_contentsで正常にファイルを取得できると、その結果を返しますが、失敗するとFALSEを返します。ここで、データベースを使用しているページを指定すると、データベースがきちんと動いているかも推定できます。

    最後は、エラー結果に基づき、場合分けしてメールを送信しています。エラーの処理だけ欲しい場合には、else以降は不要となります。

    以上です。

    (参考)
    http://blog.asial.co.jp/486
    http://www.seeds-std.co.jp/seedsblog/315.html
    http://www.okushin.co.jp/kodanuki_note/2012/09/php-2.html

    感謝です。

  • 【書評】たったコレだけでPHPプログラミングが理解できる本

    ■ 書評対象 たったコレだけでPHPプログラミングが理解できる本  大家正登(著)

    ■ 良い点

    ・ 説明が易しい
    ・ 図がわかりやすい
    ・ 基礎からDB(SQL)およびセッションまで扱っている
    ・ PDOを扱っている
    ・ 要点が絞られている

    ■ 悪い点

    ・ 若干、癖のあるコードがある
    ・ 誤字脱字が少々

    ■ 総評

    PHP初心者の総復習に向いた一冊です。基本からデータベース(SQL)そして、セッションまで扱っています。

    堅苦しい文法書ではなく、軽い読み物風のため、比較的、楽に読み進められます。SQLにも詳しい内容になっているので、SQLに不安のある人には特にオススメです。

    内容的には、それぞれの章のテーマに沿った、アプリケーションを作成するつくりになっています。

    ホテルの予約システムや、口コミサイト、ショッピングカートなどの作成の概要を通して、PHPの重要ポイントを学ぶことができます。

    ただ、書籍内のサンプルコードは、一部省略されている箇所もあるので、きっちり学びたい人は、 コードをダウンロードしてじっくり学習するのが良いと思います。

  • 【PHP】__FILE__や$_SERVER[‘SCRIPT_NAME’]等の違い

    PHPでスクリプトを書いていると、絶対パスやら相対パスを取得することが多々あります。

    PHPの定数やグローバル変数には、それらのパスを返すものがあるのですが、若干、ややこしいです。そこで、少しまとめてみました。

    なお、以下の例は、CORESERVERでの結果を表示しています。

    ■ まず、定数から。

    __FILE__

    これは、「__FILE__」が記述されたファイルそのものの絶対パスを返します。インクルードされるファイルならそのファイルです。
    例:/virtual/account/public_html/examle.com/foo/bar.php

    次に、

    dirname(__FILE__) 又は __DIR__

    これは、「dirname(__FILE__)」が記述されたファイルのディレクトリまでの絶対パスを返します。最後はスラッシュは付かないです。
    例:/virtual/account/public_html/examle.com/foo

    ■ ここからは、$_SERVER変数です。

    $_SERVER['SCRIPT_FILENAME']

    現在実行しているスクリプトの絶対パス。__FILE__とは異なり、全体としての実行ファイルの絶対パスを指しします。
    例:/virtual/account/public_html/examle.com/foo/filename.php

    __FILE__は、それが記述されたファイル名であり、インクルードされるファイルであるならそのインクルードされるファイルを指します。

    これに対し、$_SERVER[‘SCRIPT_FILENAME’]は、URL(と同等とも言うべき)の絶対パスを返します。つまり、インクルードする側のファイル名です。

    なお、mod_rewriteでURLの読み替えを行っていても、読み替え前の素のファイル名を返すので注意が必要です。

    $_SERVER['SCRIPT_NAME']

    これは、現在のスクリプトのパス。ドメイン下のディレクトリからファイル名までのパスとなります。?以後のパラメーターがつく場合には、その部分は無視します。
    なお、mod_rewriteの場合は、読み替え前のファイルです。
    例:/foo/filename.php

    $_SERVER['PHP_SELF']

    これは、現在実行しているスクリプトのファイル名です。$_SERVER[‘SCRIPT_NAME’]とほぼ同じです。ただし、?以後にパラメーターが付く場合、そのパラメーターも表示してしまいXSSの脆弱性につながるとのこと。
    例:/foo/filename.php

    つまり、パラメーター部分にJavaScriptを仕込まれると、ブラウザ上で実行してしまう危険性があるというわけです。使う場合には、htmlspecialchars等でサニタイズすることが必要とのこと。

    ■ require_once()について

    require_once()

    最後に、ファイルを読み込む処理の方である、require_once()についてですが、これは、require_once()が実行されるファイル全体を起点として、ファイルの読込を行います。

    そのため、ディレクトリ階層が違う複数のファイルからインクルードされる特定のファイルにおいて、require_once()をする場合には注意が必要です。

    その場合には、一度、__FILE__を利用して絶対パスに修正してから使います。

    $path = dirname(__FILE__).'/../../../data/init.php';
    require_once($path);

    なお、../の付いた相対パスだと見づらい場合には、realpath()関数で正規の絶対パスに変更することも可能です。

    $path = realpath(dirname(__FILE__).'/../../../data/init.php');

    以上です。