tetu式

ゲームと音楽・作曲の自己満足と悩みどころの多いプログラムのブログ。

バリデーションってなんなんだー

Web勤怠管理を会社で作り始めて1ヶ月半・・・ようやくそれらしいものはできました。

機能としては勤怠時刻と作業内容の登録、登録した勤怠情報の一覧表示、1ヶ月の勤怠時間をもとにした給与明細といったところ。

 

自分ではそこそこの出来になったんじゃないかと思ってたのですが、家でも見れるように公開してみたら問題がこれでもかというほど発生・・・

 

問題1.対応しきれてないブラウザがある。

開発中はずっとUbuntuのデフォルトウラウザであるFireFoxのみでやっていたので他のブラウザでの検証が全然出来てませんでした。

自宅ではGoogle Chromeを使用していて、それで開いてみたらフォームの部分がFireFoxと全然違う・・・

違うが故に本来自動で入るべきはずの数値が入って無かったり、フォーム自体の幅が変わってレイアウトが変になったりしてました。

あと文字サイズもだいぶ崩れていて表示しきれてない部分があったり・・・

 

ブラウザも結構な種類がありますけどもっと統一性が欲しいですね・・・

 

 

 

問題2.セキュリティ的な問題

フォーム入力で機密情報を入力する以上、避けては通れない項目なのですが具体的にどういう対策をとればいいのかさっぱりでした。

とりあえず先輩に言われてすぐにやったのがパスワードのハッシュ化とログインの仕様変更。

この辺はまた後で記事にします。

 

あとはバリデーションについてです。今回の記事はこちら。

バリデーションって言葉は初めて聞いたのですが、調べてみるとフォームの入力規約を自動化して不正な入力を弾く仕組みのようです。

簡単な例で言うとこのフォームには半角英数字で○文字以上、○文字以下の入力のみを受けつけ、それ以外の入力値に関してはエラーを返すと言う感じです。

あとは空白のフォーム送信を弾いたりとか。

 

フレームワークfuelPHPを使っていますが自分の中ではどうしてもif-elseを使った文ばっかり頭に浮かびます。

フレームワークが無い場合はこれでいいんでしょうけどfuelPHPにはバリデーションのクラスが備わっているようなのでどうせなら使ってあげたいところです。

 

使い方についてはfuelPHPのドキュメントにも書いてありそうですが、自分が理解するためにもちまちまと書きたいと思います。

 

まずはViews(ほぼHTML部分)のフォーム内容から。

 

<body>
    <center>
    <form name="loginform" method="post" action="<?php echo Uri::create('top/login');?>">
    <fieldset>
    <?php echo Session::get('message')?>
    <table>
    <tr>
        <td align="right"><label for="userid">ユーザID</label></td>
        <td><input name="userid" type="text" id="userid" value=""></td>
    </tr>
    <tr>
        <td align="right"><label for="password">パスワード</label></td>
        <td><input name="password" type="password" id="password"></td>
    </tr>
    </table>
    <input type="submit" id="login" name="login" value="ログイン">
    </fieldset>
    </form>
    </center>
</table>
</form>
</body>

 

formの部分もfuelPHPの関数で作れたりするのですがなぜかaction部分のみに使ってました。

この辺も後でfuelPHPらしい書き方にせねば・・・

 

とりあえずformの中にはuseidとpasswordという2つの入力フォーム<input>と送信用のloginボタン<submit>があります。

今回は送信ボタンが一つだけなので送信ボタンにidやnameをつける必要は無いのですが、つける癖は付けといた方がいいらしいです。

 

次にcontoroller。

<?php echo Uri::create('top/login');?>の部分にあるようにtopコントローラーのlogin(action_login)部分の処理になります。

今回はこの部分にバリデーションを使います。

 

public static function action_login()
    {
        $validation = Validation::forge();
       
        $validation->add('userid' , 'ユーザID')
                    ->add_rule('required');
        if( $validation->run() )    //バリデーション実行
        {
            //ログイン確認
            if(Model_Common::login_check(Input::post('userid'),Input::post('password')) == 1)
            {
                Response::redirect(Uri::create('top/main'));
            }
            else {
                //認証失敗
                //Model_Common::fail_login();
            }
        }
        else
        {
            Model_Common::fail_login();
        }
        return Response::forge(View::forge('attendance/login'));
    }

 

とりあえず非常に簡単な例から。

 

$validation = Validation::forge();

 

まずはバリデーションのインスタンスを作成。

POSTの中身を使うのに何も引数が無いじゃないかと思ったのですが、この処理をした時点で<form>~</form>内の入力フォームに入ってるname値とvalue値は全て格納されてるそうです、すげー。

forge()の()の中に入れるのはバリデーション自体の名前だそうです。

 

$validation->add('userid' , 'ユーザID')
               ->add_rule('required');

 

この部分でバリデーションの内容を追加していきます。

Viewsの方で言うと

<input name="userid" type="text" id="userid" value="">

この入力フォームに対してバリデーションを追加する。ということですね。

内容のrequiredは必ず入力しなければならない、というバリデーションになります。

そしてバリデーションを実行、if文で判定します。

 

if( $validation->run())

{

  成功時の処理

}

else

{

  失敗時の処理

}

 

バリデーションの実行結果を見てif文で判定・・・してるのかな、多分。

今回の場合はuseridを入れる入力フォームに何か入っていたら成功、完全な空欄であれば失敗の処理になっています。

何か入っていたらっていうのはスペースにも反応します。試したら成功時の処理に入りました。

 

その他、add_ruleには最低何文字とか、アルファベットのみかどうかの判定など色々種類があるみたいです。

しかし、基本的に文字判別はアルファベットはアルファベット、数字は数字と言う感じで分かれていて、初期の段階だと複合文字のバリデーションはできない様子。

なのでidにアルファベット以外に.とか数字とか入る場合には自分でバリデーションの種類を拡張してあげる必要があります。

 

拡張するにはまず、新しいPHPファイルを一つ用意する必要があります。

まずは「fuel/app/classes」にmyvalidation.phpを作成します。

その内容に

<?php
class MyValidation
{
    // 半角英数字.-チェック
    public static function _validation_alphanum($data)
    {
        if(!empty($data)) {
            if (preg_match("/^[a-zA-Z0-9\.\-]+$/", $data)) {
                return true;
            }
            else {
                return false;
            }
        }
        return true;
    }
}

こんな感じで文章を入れます。

こちらのサイトを参考にしドット(.)やハイフン(-)も読めるようにしたものです。

_validation_の後にくるのがルール名になります。

_validation_を入れないとバリデーションで使えるルールとして読み取ってくれないので注意。

 

保存したらcontorollerのバリデーションインスタンス作成のすぐ下の行に

 

$validation->add_callable('myvalidation');

 

これを追加。通常のvalidationで使えるものにこれを拡張しますよ、て感じです。

これでmyvalidationのalphanumルールが使えるようになりました。

扱い方はadd_ruleと一緒です。

 

 

さて、うっかりバリデーションのもう一つの役目を忘れてました。

入力規約を設定するのはもちろんですが入力規約から外れた時の処理も書かねばなりませんね。

これ書かないとユーザーが「何が原因で受け付けないのか」が分からないので結構大事な所でした。

 

HTMLの部分に<?php echo Session::get('message')?>と書いていたのに気づいたでしょうか?

このmessageというセッションオブジェクトにエラーメッセージを貼り付ければとりあえずOKです。

他にもいろいろ手段はあるでしょうが、自分はこれが融通の効くエラーメッセージ表示手段だと思って使ってます。

contorollerで書いた

 

if( validation->run() )

{

    成功時の処理

}

else

{

    失敗時の処理

}

 

これの失敗時の処理を書いていきましょう。

 

        // エラーメッセージ用HTMLを取得
        $error_html = $validation->show_errors();
        Session::set('message' ,$error_html);

セッションに入れた内容を表示するのがHTML内なので今回はこれだけでOK。

$validation->show_errors()ではrun()を実行したときにエラーになったルールを呼び出し、そのルールのエラーメッセージを呼び出します。(勝手にHTMLのリストタグになります)

fuelPHPの初期状態ならそのメッセージは「fuel/core/lang/en/validation.php」に配列で格納されています。

 

<?php

return array(
    'required'        => 'The field :label is required and must contain a value.',
    'min_length'      => 'The field :label has to contain at least :param:1 characters.',
    'max_length'      => 'The field :label may not contain more than :param:1 characters.',
    'exact_length'    => 'The field :label must contain exactly :param:1 characters.',
    'match_value'     => 'The field :label must contain the value :param:1.',
    'match_pattern'   => 'The field :label must match the pattern :param:1.',
    'match_field'     => 'The field :label must match the field :param:1.',
    'valid_email'     => 'The field :label must contain a valid email address.',
    'valid_emails'    => 'The field :label must contain a list of valid email addresses.',
    'valid_url'       => 'The field :label must contain a valid URL.',
    'valid_ip'        => 'The field :label must contain a valid IP address.',
    'numeric_min'     => 'The minimum numeric value of :label must be :param:1',
    'numeric_max'     => 'The maximum numeric value of :label must be :param:1',
    'numeric_between' => 'The field :label must contain a numeric value between :param:1 and :param:2',
    'valid_string'    => 'The valid string rule :rule(:param:1) failed for field :label',
    'required_with'   => 'The field :label must contain a value if :param:1 contains a value.',
);

 

中を見ると分かると思いますが、このルールでこのメッセージと言う風に連想配列で格納されています。

が、これは全部英語ですね。

日本語に全部直したいところですが、これはcoreフォルダ内にあるものなので基本的に編集はしない場所、というよりしちゃいけない場所です。

 

このvalidation.phpを普段編集しているappフォルダの方にコピーしましょう。

コピー先は「fuel/app/lang/ja/validation.php」にします。

何も新規フォルダを作ってない場合は「lang」には「en」しか入っていないと思います。

ここに「ja」という新規フォルダを作成してからコピーしましょう。

 

お気づきだと思いますがlangはlanguageの略、enはenglish、jaはjapaneseですね。

初期の設定だとそもそも「en」しかフォルダが存在しないようになっているので自分で作った追加フォルダは自分で読み込ませなければなりません。

「app/config/config.php」を編集し、読み込み先フォルダを変更しましょう。

 

    /**
     * Localization & internationalization settings
     */
    //'language'           => 'en', // Default language
    //'language_fallback'  => 'en', // Fallback language when file isn't available for default language
    // 'locale'             => 'en_US', // PHP set_locale() setting, null to not set

 

fuelPHPのバージョンによりけりだと思いますが、自分の1.6版では78行目あたりにありました。

 

    'language'           => 'ja', // Default language
    'language_fallback'  => 'en', // Fallback language when file isn't available for default language

 

コメントアウトを外して上の方の設定内容を'ja'に変更します。

下の方は「ja」が見つからなかったら読み込む場所なのでここはenのまま。

 

そうしたら今度は「fuel/app/lang/ja/validation.php」の編集。

 

<?php
return array(
    'required' => ':label は入力必須項目です。',
    'min_length' => ':label は :param:1 文字以上にしてください。',
    'max_length' => ':label は :param:1 文字以下にしてください。',
    'exact_length' => ':label は :param:1 文字にしてください。',
    'match_value' => ':label が :param:1 と一致しません。',
    'match_pattern' => ':label の形式が異なります。',
    'match_field' => ':label が :param:1 と異なります。',
    'valid_email' => ':value を正しいメールアドレスの形式にしてください。',
    'valid_emails' => ':label を正しいメールアドレスの形式にしてください。',
    'valid_url' => ':label を正しいURLの形式にしてください。',
    'valid_ip' => ':label を正しいipの形式にしてください。',
    'numeric_min' => ':label の値は :param:1 以上にしてください。',
    'numeric_max' => ':label の値は :param:1 以下にしてください。',
    'numeric_between' => ':label の値は :param:1 以上、 :param:2 以下にしてください。',
    'valid_string' => ':label に不正な文字が含まれています。',
    'required_with' => ':label は入力必須項目です。',
    'alphanum'        => ':label は半角英数字で入力してください。'
);

 

こんな感じにしちゃいます。

:labelは$validation->add('userid' , 'ユーザID')の'ユーザID'部分を示していて、文字列を持ってこれます。

:param:1は文字数指定のルールを使った時等に使う引数の値になります。引数が増えれば増えるほど数値も増えていきます。

:valueはフォームに入力した値がそのまま入ります。

自分で作ったルールのエラーメッセージもここに続けて入れておきます。

 

 

これでエラーメッセージも表示できるようになりました。

あとは至るところの入力フォームにバリデーションを追加していきましょう。

エラー画面を表示させないための第一歩としてなんとか頑張ります。

不正入力ダメ、ぜったい。(言われるまで気にしなかったが)