JavaScript+PHP5でつくる簡易バリデータ(入力検証、入力制限)

2009/4/22; Javascriptがバグっていたので修正。
2009/4/22; Javascriptのコメント行を修正。

          • -

2009/4/21; Javascriptがバグっていたので修正。

          • -

前回のログで、Javascriptの簡易バリデータ(MyValidator.js)を作成してみた。
クライアントサイドでのバリデーション(入力検証、入力制限)は補助的なもので、サーバーサイドにも同様の機能があることが望ましい。

ということで、サーバーサイドのバリデータを作ってみた。
クライアントからは、

  1. XHR(XMLHttpRequest)で、非同期に検証キー(検証の種類を表すキー)と値を送る。
  2. 非同期処理で検証した結果(エラーメッセージ)を返却して、それを適当な場所に表示する。

とした。

検証内容、サンプルの見た目と挙動は、先のMyValidator.jsのサンプルと同様にした。

以下が、初期画面。


以下が、ひらがなのみ入力可能なフィールドに、カタカナを入力し、フォーカスを外したとき(検証はonblurイベントのハンドラーとして仕込んでいる)のスナップ。

以下の環境でテストした。

  1. Windows XP
  2. PHP5.2
  3. Apache2.2
  4. Firefox3.0, Safari3.2, Chrome1, IE7


核になるのは、以下のphpプログラム(MyValidator.class.php)で、リクエストを受けるphpプログラムから呼び出されることを前提にした汎用的(??)なクラス。

<?php
/**
 * MyValidator.class.php
 * 
 * (C) 2009, tetsuya.odaka(EzoGP).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* 
 * 入力検証用Class
 * 
 * author;		t.odaka
 * date;		2009/4/17
 * 
*/

class MyValidator {

	/* 
	 * プロパティー
	 *  
	*/

    /*
     * 検証コンポーネント識別子&メッセージテーブル(英語)
     * 		必要に応じて利用時に書き換えてください。
     */
    var $_valMsg = array(
    	"isRequired"	=> "required",
    	"isAlpha"	=> "alphabetic characters only",
    	"isCapAlpha" 	=> "capital alphabetic characters only",
        "isNum" 	=> "number only.",
        "isAlphaNum" 	=> "alphanumeric characters only",
        "isDate" 	=> "invalid date format. yyyy/mm/dd only.",
        "isEmail" 	=> "invalid e-mail format.",
        "isPhone" 	=> "invalid phone format",
        "isInteger" 	=> "integer only.",
        "isZip" 	=> "invalid zip format. 123-4567 or 1234567.",
        // 以下は、return用のみメッセージ
        "falseUse" 	=> "invalid call"
    );

    /*
     * 検証コンポーネント識別子&メッセージテーブル(日本語)
     * 		必要に応じて利用時に書き換えてください。
     *      
     */
    var $_valMsgJp = array(
    	"isRequired" 	=> "入力必須です.",
    	"isAlpha" 	=> "アルファベットでなくてはいけません.",
    	"isCapAlpha" 	=> "アルファベット(大文字)でなくてはいけません.",
        "isNum" 	=> "数値でなくてはいけません.",
        "isAlphaNum" 	=> "英数文字でなくてはいけません.",
        "isDate" 	=> "日付(yyyy/mm/dd)が正しくありません.",
        "isEmail" 	=> "メイルアドレスの形式が正しくありません.",
        "isPhone" 	=> "電話番号の形式が正しくありません.",
        "isInteger" 	=> "整数でなくてはいけません.",
        "isZip" 	=> "郵便番号の形式(123-4567,1234567)が正しくありません.",
        "isHiragana" 	=> "ひらがなで入力してください.",
        "isKatakana" 	=> "カタカナで入力してください.",
        // 以下は、return用のみメッセージ
        "falseUse" 	=> "システムエラー;呼び出しが誤っています."
    );
    
    /*
     * 検証コンポーネント正規表現テーブル
     * 		必要に応じて利用時に書き換えてください。
     */
    var $_valRex = array(
    	"isRequired" => "/[^.*]/", // key(isRequired)だけ使う
       	"isAlpha" => "/^[a-z\s\._-]+$/i",
       	"isCapAlpha" => "/^[A-Z\s\._-]+$/",
        "isNum" => "/^[-+]?\d*\.?\d+$/",
        "isAlphaNum" => "/^[a-z0-9\s\._-]+$/i",
        "isDate" => "/^\d{4}\/(\d{1}|\d{2})\/(\d{1}|\d{2})/", // yyyy/mm/dd
        "isEmail" => "/^[a-z0-9\._%-]+@[a-z0-9\.-]+\.[a-z]{2,4}$/i",
        "isPhone" => "/^[\d\s()-]+$/",
        "isInteger" => "/^[-+]?\d+$/",
        "isZip" => "/^\d{3}-?\d{4}$/",
        "isHiragana" => "/^[あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろをん ]+$/u",
        "isKatakana" => "/^[アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン ]+$/u"
	);
        
        /* 
	 * デフォルトのコンストラクタ 
	 */
	function __construct() {
	}

        /* 
	 * 検証メソッド
	 *   パラメータ
	 *    $_locale	        :クライアントのロケール(jaと以外を想定) 
	 *    $_opt		:検証の種類(isRequiredなど)
	 *    $_str		:検証する文字列
	 */
	function validate($_locale, $_opt, $_str){

		// リクエストからlocaleを取り出す。
		if($_locale == 'ja'){
			$_mArray = &$this->_valMsgJp;
		}else{
			$_mArray = &$this->_valMsg;
		}
		
		$_ret = "";
		if($_opt == "isRequired"){
			$_ret = $this->isReq($_str, &$_mArray);
		}else{
			if (array_key_exists($_opt, $this->_valRex)) {
			     $_ret = $this->pregCheck($_opt, $_str, &$_mArray);
			}
		}
		
		return $_ret;
	}

	/* 
	 * 入力必須のチェック
	 *   検証の種類が(isRequired)の場合
	 *   パラメータ
	 *    $_str		:検証する文字列
	 *    $_mArray          :localeに対応したメッセージテーブルの参照
	 * 
	 */
	function isReq($_str, $_mArray){
		if($_str==null || strlen($_str)==0){
			return 	$_mArray["isRequired"];		
		}
		return "";
	}
	
	/*
	 * 正規表現でチェック 
	 *   パラメータ
	 *    $_opt		:検証の種類(isAlphaなど)
	 *    $_str		:検証する文字列
	 *    $_mArray          :localeに対応したメッセージテーブルの参照
	 */
	function pregCheck($_opt, $_str, $_mArray){
		
		mb_regex_encoding('UTF-8');
		$_retArray = preg_match($this->_valRex[$_opt],$_str);
		
		if($_retArray.length==0){
			return 	$_mArray[$_opt];		
		}
		return "";
	}

}
?>


以下が、HTML(Javascript)と上のクラスを呼び出すphpプログラム。
scriptには、YUI2.7.0を利用した。

<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<META http-equiv="Content-Style-Type" content="text/css">
<TITLE>MyValidate</TITLE>

<style type="text/css">

input.ez_initguide {
	/* grey */
    color:#808080;
}

</style>
<!-- 読み込むjs --> 
<script type="text/javascript" src="../scripts/lib/yui/build/yahoo/yahoo-min.js">
</script>
<script type="text/javascript" src="../scripts/lib/yui/build/yahoo-dom-event/yahoo-dom-event.js">
</script>
<script type="text/javascript" src="../scripts/lib/yui/build/connection/connection-min.js" >
</script>

<script type="text/javascript">
/*
 * 入力検証
 *  (モジュールパターンで実装)
 */
validate = function() {
	var	Dom 	= YAHOO.util.Dom;	
	var	Event 	= YAHOO.util.Event;
		
	/**
	* Inputにフォーカスが当たったときのハンドラー
	*/
	var onFocusHdlr = function(_evt,_obj) {

		// 初期値(入力ガイド)の場合だけ、入力値を消す。
		//  Dom.getStyleColor()は
		//    rgb(128,128,128); mozilla, chrome, 
		//    #808080; IE7 
		//  と異なった戻り値となる。
		// _objは,HTMLInputElement;
		
		_e = YAHOO.util.Event.getTarget(_evt)
		var _id = _e.id;
		var _resId = _id + '_res';
		
		var _fColor=Dom.getStyle(_id, 'color');
		if( _fColor == 'rgb(128, 128, 128)' ||
				_fColor == '#808080'){ // for IE
			Dom.get(_id).value="";
		}
		// 入力フィールドの色を白にする。
		Dom.setStyle(_id, 'background-color', 'white');
		// 入力フィールドの文字の色をグレーから黒に変える
		Dom.setStyle(_id, 'color', 'black');
		// メッセージフィールドを消す
		Dom.get(_resId).innerHTML="";
	};

	/**
	* Inputからフォーカスが外れたときのハンドラー
	*/
	var onBlurHdlr = function(_evt,_obj){
		
		_e = YAHOO.util.Event.getTarget(_evt)
		var _id = _e.id;
		var _resId = _id + '_res';

		// 検証キーをclass属性から取得.
		if(document.all){
			//for IE
			var _keys = Dom.get(_id).getAttribute('className');
		}else{
			// for FF, Chrome, Safari
			var _keys = Dom.get(_id).getAttribute('class');
		}

		// 検証キーを取り出す
		var _keyArray = _keys.split(" ");
		
		// 検証対象を取得.
		var _str  = Dom.get(_id).value;

		// サーバーで検証
		var _url = "test_validate.php"
		for(var i = 0; i < _keyArray.length; i++){

			// class名の判別(my_isXxxxのみを選別する)
			var _idx = _keyArray[i].toLowerCase().indexOf('my_is',0);

			if(_idx != -1){
				// リクエストパラメータのセット
				var _opt = _keyArray[i].replace(/^(my_)/,''); // my_をトリムする。
				// Validator.class.phpの仕様にしたがって、パラメータを設定する。
				var _parm = 'opt='+_opt+'&'+'val='+_str;

				var _arg ={
					'id': _id
				};
				// 戻ってきたときのために、エレメントのidをセットする。
				ajaxCallback.argument = _arg;
		
				// ajaxで検証
				YAHOO.util.Connect.asyncRequest('POST',_url,
								ajaxCallback, _parm);
			}
		}
	};
		
	/*
	 * Ajaxハンドラー
	 *  
	 */
	var ajaxHandlers = {
		
		// 受信成功時の処理
		responseSuccess: function(_oj){
			//alert("responseSuccess");
			var _id = _oj.argument.id;
			var _resId = _id + '_res';
			// データの取得
			var _ret = _oj.responseText;
			//alert("response "+_oj.responseText);
			if(_ret.length>0){
				// エラーのハンドル(皆さん適当に)
				// 入力フィールドの色をピンクにする。
				Dom.setStyle(_id, 'background-color', 'pink');
				// エラーが複数のときは複数行にして表示する。
				if(Dom.get(_resId).innerHTML.length>0){
					Dom.get(_resId).innerHTML += ' &nbsp ' +_ret;
				}else{
					Dom.get(_resId).innerHTML = _ret;
				}
			}
		},

		// 受信失敗時の処理
		responseFailure: function(_oj){
			alert("responseFailure");
			var _id = _oj.argument.id;
			var _resId = _id + '_res';

			var _ret = 'ステータス: ' + _oj.status + 'ステータステキスト: ' +
							_oj.statusText + '読み込みに失敗しました。';
			// エラーのハンドル(皆さん適当に)
			// 入力フィールドの色をピンクにする。
			Dom.setStyle(_id, 'background-color', 'pink');
			Dom.get(_resId).innerHTML = _ret;
		}
	};

	/*
	 * コールバック成功/失敗時の振り分け
	 *  
	 */
	var ajaxCallback =
	{
		success: ajaxHandlers.responseSuccess,
		failure: ajaxHandlers.responseFailure,
		cache: false,
		scope: ajaxHandlers,
		argument: null
	};
	
	return {
		/**
		* 初期処理
		*/
    	init: function() {
			

		// HTMLInputElementの配列を取得する。
		var _inObj = document.getElementsByTagName('input');
		for(var i = 0; i < _inObj.length; i++){
			// type=textに、focusとblurにイベントを仕込む
			if(_inObj[i].getAttribute('type') == 'text'){
				Event.addFocusListener(_inObj[i].id, onFocusHdlr, _inObj[i]);		    
				Event.addBlurListener(_inObj[i].id, onBlurHdlr, _inObj[i]);		    
			}
		}
	} // initの終わり
	
	};
}();


</script> 

</HEAD>
<BODY onload="validate.init()">
<h3>
サーバーサイドでのバリデーション確認用
</h3>

<form method="post" action='#'>
<!--// 入力検証項目 -->

未入力のチェック;
<input id="v_required" class="ez_initguide my_isRequired my_isRequired" type="text" size=50	
	value="入力してください." />
<div id="v_required_res"></div>

<br>
アルファベットのチェック;
<input id="v_alpha" class="ez_initguide my_isAlpha" type="text"
size=50 value="アルファベットを入力してください." />
<div id="v_alpha_res"></div>

<br>
ひらがなのチェック;
<input id="v_hiragana" class="ez_initguide my_isHiragana" type="text" size=50
	value="ひらがなを入力してください." />
<div id="v_hiragana_res"></div>

</form>


<br>
</body>
</HTML>
<?php
/* MyValidator.class.phpを使ったサンプル
	      
		author	; t.odaka
		date	; 2009/4/20

		requestパラメータとして、
			opt; 検証のオプション
			val; 検証対象の値
		の2つを受け取り、Validator.class.phpのvalidateメソッドによって検証を行う。

*/

include("../Myznala/MyValidator.class.php");

switch($_SERVER['REQUEST_METHOD']) {
	case 'GET'	: $rMethod = &$_GET; break;
	case 'POST'	: $rMethod = &$_POST; break;
	default:
}

// リクエストからlocaleを取り出す。
$locale 
	= substr(strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']),0,2);
$m_log->debug("locale: ".$locale); // Debugログ

// MyValidatorをインスタンス化する。
$valObj = new MyValidator();

$opt = $rMethod['opt'];
$val = $rMethod['val'];

$ret = $valObj->validate($locale, $opt , $val);

// 結果の返却
header("Content-Type:text/html");
echo $ret;

?>