Webフォーム:ブラウザからサーバにデータを送るためのしくみ

2015年3月24日(火)
野田 貴子

(2)2回目以降のアクセスの場合

2回目以降のアクセスかどうかのチェックは、(1)でもうできているので、次に進みましょう。

(3)すべてのデータが適切かどうかチェックする

入力されたデータを何でもかんでも受け入れてしまうと、データの管理が難しくなり、(年齢が1200歳などという)おかしなことが生じてしまいます。というわけで、受け入れたくない形式のデータが入力された場合は、エラーメッセージを表示するようにしましょう。今回は、ルールを以下のように決めます。

a. 名前

  1. 空っぽではないこと
  2. 文字数が3文字以内であること(「あ」とかダメ)
  3. 文字数が20文字以内であること

b. 年齢

  1. 数字であること(twentyはダメ)
  2. 正数であること(-1歳はダメ)
  3. 整数であること(20.5歳はダメ)

c. 性別

  1. 空っぽではないこと
  2. 選択肢にある値であること

d. 趣味

  1. 選択肢にある値であること

e. 住所

  1. 選択肢にある値であること

f. コメント

  1. 文字数が100文字以内であること

それでは各項目を1つずつ見ていきましょう。

  • a-1. [名前]が空っぽではないこと

空っぽにはいくつかのパターンがあります。

  • $_POST["name"] 自体が存在しない
  • $_POST["name"] は存在するが、中身が空("")である
  • $_POST["name"] は存在するが、中身が空白文字("  ")のみである

これらをすべて考慮すると、以下のようなプログラムになります。

<?php
$errorMsg = array();
$form = array();

$form["name"] = $_POST["name"];

// $_POST["name"] が存在しない場合
if (! array_key_exists("name", $_POST)) {
    $errorMsg[] = "名前を入力してください。";
}
// $_POST["name"] が存在する場合
else {
    // 前後の空白を除去する(空白文字のみの場合は空になる)
    $form["name"] = trim($form["name"]);

    // 中身が空("")である場合
    if ($form["name"] === "") {
        $errorMsg[] = "名前を入力してください。";
    }
}
?>
  • a-2. [名前]が3文字以内であること
  • a-3. [名前]が20文字以内であること

これらは以前、PHPで文字列を扱うための4つの方法でも取り上げましたが、覚えていますでしょうか。名前に関する入力チェックをすべてまとめると以下のようになります。

<?php
$errorMsg = array();
$form = array();

/*------------------------------------------------------------
    名前の入力チェック
------------------------------------------------------------*/
$form["name"] = $_POST["name"];

// $_POST["name"] が存在しない場合
if (! array_key_exists("name", $_POST)) {
    $errorMsg[] = "名前を入力してください。";
}
// $_POST["name"] が存在する場合
else {
    // 前後の空白を除去する(空白文字のみの場合は空になる)
    $form["name"] = trim($form["name"]);

    // 中身が空("")である場合
    if ($form["name"] === "") {
        $errorMsg[] = "名前を入力してください。";
    }
    // 中身が空("")でない場合
    else {
        $length = strlen($form["name"]);
        if ($length < 3 || $length > 20) {
            $errorMsg[] = "名前は3文字以上20文字以内で入力してください。";
        }
    }
}
?>
  • b-1. [年齢]が数字であること(twentyはダメ)

数字であることを確認するには、いくつか方法があります。今回は、受け取った文字列型の数字を数値型に直した後また文字列型に直し、変化がないことを確認する方法を取ります。

<?php
// 変化しない例(整数の場合)
var_dump(strval(intval("0")));      // string(1) "0"
var_dump(strval(intval("25")));     // string(2) "25

// 変化する例(数字の部分だけ抜き取られ、それ以外は0になる)
var_dump(strval(intval("20.5")));   // string(1) "20"
var_dump(strval(intval("2a")));     // string(1) "2"
var_dump(strval(intval("0x10")));   // string(1) "0"
var_dump(strval(intval("twenty"))); // string(1) "0"
?>

関数のis_numericctype_digitは癖があるので、PHPに慣れてから使ってみてください(例えば is_numeric の場合、「0x10」という文字列も(16進数の)数値だと判断して 、true になります)。

  • b-2. [年齢]が正数であること(-1歳はダメ)
  • b-3. [年齢]が整数であること(20.5歳はダメ)

この2つは特記することはないでしょう。まとめると以下のようになります。

<?php
/*------------------------------------------------------------
    年齢の入力チェック
------------------------------------------------------------*/
$form["age"] = $_POST["age"];

// $_POST["age"] が存在する場合
if (array_key_exists("name", $_POST)) {

    // 中身が空("")でない場合
    if ($form["age"] !== "") {

        // 整数でない場合
        if (strval(intval($form["age"])) !== $form["age"]) {
            $errorMsg[] = "年齢は整数で入力してください。";
        }
        // 正の整数でない場合
        else if ($form["age"] < 0) {
            $errorMsg[] = "年齢は0歳以上で入力してください。";
        }
    }
}
?>

このような処理は、これしか方法がない訳ではありません。例えば「20.5」など小数の数字が入力された場合は小数点以下を無視するなど、何通りかの方法が考えられます。

  • c-1. [性別]が空っぽではないこと

こちらは a-1 と同様なので省略します。

  • c-2. [性別]が選択肢にある値であること

性別は「male」または「female」のみを受け入れることにしましょう。まずは、選択肢の配列と、初期値の配列(あとから趣味や住所の初期値も追加するので、配列にしておきます)を作ります。

<?php
$sexes = array("male" => "男", "female" => "女");
$form["sex"] = "male";
?>

ついでに、先ほどのWebフォームの部分を改良します。選択肢の配列をベースに作っておけば、後から選択肢を追加するときに簡単です(性別の選択肢は増えないと思っていますか? 「その他」「不明」「未解答」などがありますよ)。

変更前

<?php
echo '
    性別:<label><input type="radio" name="sex" value="male" checked>男</label>
          <label><input type="radio" name="sex" value="female">女</label><br>
';
?>

変更後

<?php
echo '性別:';
foreach($sexes as $value => $label) {
    $checked = '';
    // 初期値の場合、「checked」をつける
    if ($value === $form["sex"]) {
        $checked = ' checked'; // checkedの前に半角スペースがありますよ
    }
    echo '<label><input type="radio" name="sex" value="' . $value . '"' . $checked . '>' . $label . '</label>';
}
echo '<br>';
?>

次に本題のチェック部分です。選択肢にある値であるかどうかは、$sexes配列に「male」などのキーがあるかどうかを調べるので、array_key_existsを使います。まとめると以下のようになります。選択肢にあるもののみ許可をする、いわゆる「ホワイトリスト」形式は、チェックが簡単ですよね。

<?php
/*------------------------------------------------------------
    性別の入力チェック
------------------------------------------------------------*/
$form["sex"] = $_POST["sex"];

// $_POST["sex"] が存在しない場合
if (! array_key_exists("sex", $_POST)) {
    $errorMsg[] = "性別を選択してください。"; // ラジオボタンなので「入力」ではなく「選択」で。
}
// $_POST["sex"] が存在する場合
else {
    // 選択肢にないものの場合
    if (! array_key_exists($form["sex"], $sexes)) {
        $errorMsg[] = "性別を正しく選択してください。";
    }
}
?>
  • d-1. [趣味]が選択肢にある値であること

性別と同じように処理します。ただし趣味は複数選択できることを考慮します。初期値は配列なので、初期値かどうかの判断には「===」ではなくin_arrayを使います。

変更前

<?php
echo '
    趣味:<label><input type="checkbox" name="hobbies[]" value="cooking" checked>料理</label>
          <label><input type="checkbox" name="hobbies[]" value="swimming" checked>水泳</label>
          <label><input type="checkbox" name="hobbies[]" value="running">ランニング</label><br>
';
?>

変更後

<?php
$hobbies = array(
    "cooking"  => "料理",
    "swimming" => "水泳",
    "runnning" => "ランニング"
);
$form["hobbies"] = array("cooking", "swimming");

echo '趣味:';
foreach($hobbies as $value => $label) {
    $checked = '';
    // 初期値の場合、「checked」をつける
    if (in_array($value, $form["hobbies"])) {
        $checked = ' checked';
    }
    echo '<label><input type="checkbox" name="hobbies[]" value="' . $value . '"' . $checked . '>' . $label . '</label>';
}
echo '<br>';
?>

プログラムは以下のようになります。

<?php
/*------------------------------------------------------------
    趣味の入力チェック
------------------------------------------------------------*/
$form["hobbies"] = $_POST["hobbies"]; // hobbiesは配列で取得される

// $_POST["hobbies"] が存在する場合
if (array_key_exists("hobbies", $_POST)) {
    // 選択肢にないものの場合
    foreach($form["hobbies"] as $value) {
        if (! array_key_exists($value, $hobbies)) {
            $errorMsg[] = "趣味を正しく選択してください。";
        }
    }
}
?>
  • e-1. [住所]が選択肢にある値であること

性別と同じように処理します。

  • f-1. [コメント]が100文字以内であること

名前と同じように処理します。

以上、すべての入力値のチェック処理をまとめると以下のようになります。名前、年齢、コメントでも初期値用の変数を用意しました。これらはあとで活用します。

<?php
// エラーメッセージ保存用配列
$errorMsg = array();

// 選択肢
$sexes = array("male" => "男", "female" => "女");
$hobbies = array(
    "cooking"  => "料理",
    "swimming" => "水泳",
    "runnning" => "ランニング"
);
$addresses = array(
    "north" => "北日本",
    "east"  => "東日本",
    "west"  => "西日本",
    "south" => "南日本"
);

// 初期値
$form = array();
$form["name"] = "John";
$form["age"] = 25;
$form["sex"] = "male";
$form["hobbies"] = array("cooking", "swimming");
$form["address"] = "east";
$form["comment"] = "Hello";

/*------------------------------------------------------------
    2回目以降のアクセス
------------------------------------------------------------*/
if (array_key_exists("not_the_first_time", $_POST)) {

    /*------------------------------------------------------------
        名前の入力チェック
    ------------------------------------------------------------*/
    $form["name"] = $_POST["name"];

    // $_POST["name"] が存在しない場合
    if (! array_key_exists("name", $_POST)) {
        $errorMsg[] = "名前を入力してください。";
    }
    // $_POST["name"] が存在する場合
    else {
        // 前後の空白を除去する(空白文字のみの場合は空になる)
        $form["name"] = trim($form["name"]);

        // 中身が空("")である場合
        if ($form["name"] === "") {
            $errorMsg[] = "名前を入力してください。";
        }
        // 中身が空("")でない場合
        else {
            $length = strlen($form["name"]);
            if ($length < 3 || $length > 20) {
                $errorMsg[] = "名前は3文字以上20文字以内で入力してください。";
            }
        }
    }

    /*------------------------------------------------------------
        年齢の入力チェック
    ------------------------------------------------------------*/
    $form["age"] = $_POST["age"];

    // $_POST["age"] が存在する場合
    if (array_key_exists("name", $_POST)) {

        // 中身が空("")でない場合
        if ($form["age"] !== "") {

            // 整数でない場合
            if (strval(intval($form["age"])) !== $form["age"]) {
                $errorMsg[] = "年齢は整数で入力してください。";
            }
            // 正の整数でない場合
            else if ($form["age"] < 0) {
                $errorMsg[] = "年齢は0歳以上で入力してください。";
            }
        }
    }

    /*------------------------------------------------------------
        性別の入力チェック
    ------------------------------------------------------------*/
    $form["sex"] = $_POST["sex"];

    // $_POST["sex"] が存在しない場合
    if (! array_key_exists("sex", $_POST)) {
        $errorMsg[] = "性別を選択してください。"; // ラジオボタンなので「入力」ではなく「選択」で。
    }
    // $_POST["sex"] が存在する場合
    else {
        // 選択肢にないものの場合
        if (! array_key_exists($form["sex"], $sexes)) {
            $errorMsg[] = "性別を正しく選択してください。";
        }
    }

    /*------------------------------------------------------------
        趣味の入力チェック
    ------------------------------------------------------------*/
    $form["hobbies"] = $_POST["hobbies"]; // hobbiesは配列で取得される

    // $_POST["hobbies"] が存在する場合
    if (array_key_exists("hobbies", $_POST)) {
        // 選択肢にないものの場合
        foreach($form["hobbies"] as $value) {
            if (! array_key_exists($value, $hobbies)) {
                $errorMsg[] = "趣味を正しく選択してください。";
            }
        }
    }

    /*------------------------------------------------------------
        住所の入力チェック
    ------------------------------------------------------------*/
    $form["address"] = $_POST["address"];

    // $_POST["address"] が存在する場合
    if (array_key_exists("address", $_POST)) {
        // 選択肢にないものの場合
        if (! array_key_exists($form["address"], $addresses)) {
            $errorMsg[] = "住所を正しく選択してください。";
        }
    }

    /*------------------------------------------------------------
        コメントの入力チェック
    ------------------------------------------------------------*/
    $form["comment"] = $_POST["comment"];

    // $_POST["comment"] が存在する場合
    if (array_key_exists("name", $_POST)) {
        if (strlen($form["comment"]) > 100) {
            $errorMsg[] = "コメントは100文字以内で入力してください。";
        }
    }
}

/*------------------------------------------------------------
    1回目のアクセス
------------------------------------------------------------*/
else {
    // Webフォームを表示する
    echo '
        <form method="POST" action="profile.php">
            名前:<input type="text" name="name" value="' . $form["name"] . '"><br>
            年齢:<input type="text" name="age" value="' . $form["age"] . '"><br>
    ';
    echo '性別:';
    foreach($sexes as $value => $label) {
        $checked = '';
        // 初期値の場合、「checked」をつける
        if ($value === $form["sex"]) {
            $checked = ' checked';
        }
        echo '<label><input type="radio" name="sex" value="' . $value . '"' . $checked . '>' . $label . '</label>';
    }
    echo '<br>';
    echo '趣味:';
    foreach($hobbies as $value => $label) {
        $checked = '';
        // 初期値の場合、「checked」をつける
        if (in_array($value, $form["hobbies"])) {
            $checked = ' checked';
        }
        echo '<label><input type="checkbox" name="hobbies[]" value="' . $value . '"' . $checked . '>' . $label . '</label>';
    }
    echo '<br>';
    echo '住所:<select name="address">';
    foreach($addresses as $value => $label) {
        $selected = '';
        // 初期値の場合、「selected」をつける
        if ($value === $form["address"]) {
            $selected = ' selected';
        }
        echo '<option value="' . $value . '"' . $selected . '>' . $label . '</option>';
    }
    echo '</select><br>';
    echo '
            コメント:<textarea type="text" name="name">' . $form["comment"] . '</textarea><br>
            <br>
            <input type="hidden" name="not_the_first_time" value="yes">
            <input type="submit" value="送信">
        </form>
    ';
}
?>

ハーフタイム

ここまでで入力されたデータのチェックが済みましたが、まだプログラムは完成していません。相当分量が多くなったので、「(4)適切でないデータがある場合、エラーメッセージと再入力用のWebフォームを表示する」以降は、来週に解説いたします。お楽しみに。それではまた。

1983年生まれ。大学卒業後、ソフトウェア開発の営業を経て、ソフトウェア開発業務に転向。現在は自社パッケージのフロントエンド開発のほか、PHPでの受託開発案件、日→英のローカライズ案件などを担当。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています