PHP

PHPでフォームの値を配列・連想配列で送る方法|name属性の書き方

PHPのフォームで複数の値を配列・連想配列として送信するname属性の書き方を解説します。input、checkbox、multiple select、二次元配列の受け取り方、未送信値・型検証・HTMLエスケープ・max_input_vars・CSRFの注意点まで紹介します。

この記事の目次
  1. name属性とPHP配列の対応表
  2. 複数のinputを配列としてPOSTする
  3. 空欄を除いて整形する
  4. 連想配列としてPOSTする
  5. 連想配列の存在と型を検証する
  6. 複数レコードを二次元配列で送る
  7. 各行を検証して必要なキーだけ取り出す
  8. checkboxの複数選択を配列で送る
  9. multiple selectを配列で送る
  10. GETとPOSTのどちらを使うか
  11. 配列が途中で欠けるときはmax_input_varsを確認する
  12. 受信した値をHTMLへ表示するときの注意
  13. hidden fieldとCSRFの注意点
  14. よくあるエラーと原因
  15. Undefined array keyが出る
  16. foreach() argument must be of type arrayが出る
  17. 同じnameの最後の値しか取得できない
  18. checkboxの値が送られない
  19. 配列の後半だけ取得できない
  20. よくある質問
  21. name=”items[]”の番号はどう決まりますか?
  22. 配列の値をそのままデータベースへ保存できますか?
  23. filter_input_arrayを使えば安全ですか?
  24. パスワードもaccount[password]で送れますか?
  25. まとめ

HTMLフォームのname属性へ角括弧を付けると、PHPで値を配列として受け取れます。

<input type="text" name="staffs[]">
<input type="text" name="staffs[]">

POST送信後は$_POST['staffs']が配列になります。

<?php

$staffs = $_POST['staffs'] ?? [];

if (!is_array($staffs)) {
    $staffs = [];
}

ポイントは、角括弧の付け方と、受信後に「本当に配列か」を確認することです。フォーム値は利用者が変更できる外部入力なので、HTMLを正しく書いただけでは安全とは限りません。

name属性とPHP配列の対応表

name属性 PHPでの形 用途
staffs[] $_POST['staffs'][0] 同じ種類の値を順番に送る
account[name] $_POST['account']['name'] 関連項目をキー付きでまとめる
items[0][name] $_POST['items'][0]['name'] 複数レコードを二次元配列で送る
categories[] $_POST['categories'] checkboxやmultiple select

角括弧なしで同じnameを複数送ると、期待した配列にならず最後の値だけが残る場合があります。複数値を受け取る項目には[]を付けます。

複数のinputを配列としてPOSTする

同じ種類の値を順番付き配列として送る例です。

<form action="receive.php" method="post">
  <label>
    担当者1
    <input type="text" name="staffs[]">
  </label>

  <label>
    担当者2
    <input type="text" name="staffs[]">
  </label>

  <label>
    担当者3
    <input type="text" name="staffs[]">
  </label>

  <button type="submit">送信する</button>
</form>

次の入力を行ったとします。

  • 山田
  • 佐藤
  • 鈴木

PHP側では次の配列として受け取ります。

<?php

var_dump($_POST['staffs'] ?? null);

// array(3) {
//   [0]=> string(6) "山田"
//   [1]=> string(6) "佐藤"
//   [2]=> string(6) "鈴木"
// }

本番画面でvar_dump()print_r()を出し続けないでください。これは開発中に構造を確認するための例です。

空欄を除いて整形する

受信値が配列か確認し、各要素が文字列の場合だけtrimします。

<?php

$rawStaffs = $_POST['staffs'] ?? [];
$staffs = [];

if (is_array($rawStaffs)) {
    foreach ($rawStaffs as $value) {
        if (!is_string($value)) {
            continue;
        }

        $name = trim($value);

        if ($name !== '') {
            $staffs[] = $name;
        }
    }
}

リクエストを改変してstaffs[0][x]=...のような入れ子を送ることもできるため、各要素が文字列かも確認します。

連想配列としてPOSTする

アカウント情報のように意味の異なる項目をひとまとめにする場合は、角括弧内へキーを書きます。

<form action="receive.php" method="post">
  <label>
    ユーザーID
    <input type="number" name="account[id]" min="1">
  </label>

  <label>
    名前
    <input type="text" name="account[name]">
  </label>

  <label>
    メールアドレス
    <input type="email" name="account[email]">
  </label>

  <button type="submit">送信する</button>
</form>

受信結果は次の形です。

$_POST['account'] = [
    'id' => '12',
    'name' => '山田太郎',
    'email' => 'taro@example.com',
];

input type="number"でもHTTPで届く値は基本的に文字列です。PHP側で整数として使う前に検証します。

連想配列の存在と型を検証する

<?php

$rawAccount = $_POST['account'] ?? null;

if (!is_array($rawAccount)) {
    http_response_code(400);
    exit('入力形式が正しくありません');
}

$id = filter_var(
    $rawAccount['id'] ?? null,
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]],
);

$name = is_string($rawAccount['name'] ?? null)
    ? trim($rawAccount['name'])
    : '';

$email = filter_var(
    $rawAccount['email'] ?? null,
    FILTER_VALIDATE_EMAIL,
);

$errors = [];

if ($id === false) {
    $errors['id'] = '正しいIDを入力してください';
}

if ($name === '') {
    $errors['name'] = '名前を入力してください';
}

if ($email === false) {
    $errors['email'] = '正しいメールアドレスを入力してください';
}

HTML側のtyperequiredは使いやすさのために有効ですが、利用者はリクエストを直接変更できます。サーバー側の検証を省略できません。

複数レコードを二次元配列で送る

商品名と数量の組を複数送る場合は、行番号と項目名を組み合わせます。

<form action="receive.php" method="post">
  <fieldset>
    <legend>商品1</legend>
    <input type="text" name="items[0][name]">
    <input type="number" name="items[0][quantity]" min="1">
  </fieldset>

  <fieldset>
    <legend>商品2</legend>
    <input type="text" name="items[1][name]">
    <input type="number" name="items[1][quantity]" min="1">
  </fieldset>

  <button type="submit">送信する</button>
</form>

PHPでは次のような二次元配列になります。

$_POST['items'] = [
    0 => [
        'name' => 'キーボード',
        'quantity' => '2',
    ],
    1 => [
        'name' => 'マウス',
        'quantity' => '1',
    ],
];

各行を検証して必要なキーだけ取り出す

<?php

$rawItems = $_POST['items'] ?? [];
$items = [];

if (is_array($rawItems)) {
    foreach ($rawItems as $rawItem) {
        if (!is_array($rawItem)) {
            continue;
        }

        $name = is_string($rawItem['name'] ?? null)
            ? trim($rawItem['name'])
            : '';

        $quantity = filter_var(
            $rawItem['quantity'] ?? null,
            FILTER_VALIDATE_INT,
            ['options' => ['min_range' => 1, 'max_range' => 999]],
        );

        if ($name === '' || $quantity === false) {
            continue;
        }

        $items[] = [
            'name' => $name,
            'quantity' => $quantity,
        ];
    }
}

入力に含まれる未知のキーをそのまま保存せず、必要なキーだけで新しい配列を作ると後続処理が安定します。

checkboxの複数選択を配列で送る

複数のcheckboxへ同じname="categories[]"を付けます。

<label>
  <input type="checkbox" name="categories[]" value="php">
  PHP
</label>

<label>
  <input type="checkbox" name="categories[]" value="javascript">
  JavaScript
</label>

<label>
  <input type="checkbox" name="categories[]" value="css">
  CSS
</label>

PHP側では、許可した値だけを残します。

<?php

$allowedCategories = ['php', 'javascript', 'css'];
$rawCategories = $_POST['categories'] ?? [];
$categories = [];

if (is_array($rawCategories)) {
    $categories = array_values(array_intersect(
        $allowedCategories,
        array_filter($rawCategories, 'is_string'),
    ));
}

checkboxが一つも選択されていない場合、categoriesというキー自体が送信されません。$_POST['categories'] ?? []のように初期値を用意します。

multiple selectを配列で送る

複数選択できるselectにも[]が必要です。

<label for="skills">スキル</label>
<select id="skills" name="skills[]" multiple>
  <option value="php">PHP</option>
  <option value="mysql">MySQL</option>
  <option value="html">HTML</option>
</select>

選択された値は$_POST['skills']の配列として届きます。checkboxと同じように、サーバー側で許可リストと照合します。

GETとPOSTのどちらを使うか

formのmethodを省略するとGETになります。データ更新、長い配列、個人情報を含む入力では、通常POSTを明示します。

<form action="receive.php" method="post">

検索条件のようにURLへ残して共有したい値はGETが向いています。ただし、GETでもPOSTでも受信値を信頼せず検証する点は同じです。

詳しい違いはPHPフォームのGETとPOSTの違いを参照してください。

配列が途中で欠けるときはmax_input_varsを確認する

PHPのmax_input_varsは、GET、POST、COOKIEごとに受け付ける入力変数数の上限です。既定値は1,000です。

上限を超えると警告が発生し、後ろの入力変数が切り捨てられます。大量の行や深い入れ子を送るフォームで「末尾だけ届かない」場合は確認します。

<?php

echo ini_get('max_input_vars');
echo ini_get('max_input_nesting_level');

上限をただ増やす前に、フォーム項目数が本当に必要か、複数回に分けられないか、JSON APIなど別の設計が適切かを検討します。上限は大量入力によるリソース消費を抑える役割もあります。

受信した値をHTMLへ表示するときの注意

外部入力をHTMLへ出力するときは、HTML文脈に合わせてエスケープします。

<?php foreach ($staffs as $staff): ?>
  <li>
    <?= htmlspecialchars(
        $staff,
        ENT_QUOTES | ENT_SUBSTITUTE,
        'UTF-8',
    ) ?>
  </li>
<?php endforeach; ?>

htmlspecialchars()はHTML出力時の対策です。メールアドレス、整数、カテゴリ値などが要件どおりかを判断する入力検証とは役割が違います。

SQLへ保存する場合は、文字列連結でSQLを作らず、PDOなどのprepared statementへ値を渡してください。

hidden fieldとCSRFの注意点

hidden fieldもブラウザ開発者ツールや直接リクエストで変更できます。商品価格、権限、ユーザーIDなどをhidden値だけで信用してはいけません。

hidden fieldの基本はPHPでhidden fieldを送信する方法と注意点で解説しています。

ログイン済みユーザーがデータを変更するPOSTフォームでは、CSRF tokenも検証します。簡略化したセッションtokenの例です。

<?php
session_start();

if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>

<form action="receive.php" method="post">
  <input
    type="hidden"
    name="csrf_token"
    value="<?= htmlspecialchars(
        $_SESSION['csrf_token'],
        ENT_QUOTES | ENT_SUBSTITUTE,
        'UTF-8',
    ) ?>"
  >
</form>

受信側では、tokenの存在と一致を確認します。

<?php
session_start();

$token = $_POST['csrf_token'] ?? '';

if (
    !is_string($token)
    || !isset($_SESSION['csrf_token'])
    || !hash_equals($_SESSION['csrf_token'], $token)
) {
    http_response_code(403);
    exit('不正なリクエストです');
}

実際のアプリケーションでは、利用しているフレームワークのCSRF機能を優先してください。

よくあるエラーと原因

Undefined array keyが出る

項目が未送信なのに$_POST['items']を直接参照しています。$_POST['items'] ?? []で初期値を用意し、必要項目ならエラーとして扱います。

foreach() argument must be of type arrayが出る

利用者がitems=文字列のように配列ではない値を送った可能性があります。foreachの前にis_array()を確認します。

同じnameの最後の値しか取得できない

複数の値を同じnameで送る場合は、name="staffs[]"のように末尾へ角括弧を付けます。

checkboxの値が送られない

未選択のcheckboxは送信されません。また、複数選択で[]を忘れると配列になりません。

配列の後半だけ取得できない

入力変数がmax_input_varsを超えていないか、入れ子がmax_input_nesting_levelを超えていないか確認します。

よくある質問

name=”items[]”の番号はどう決まりますか?

キーを省略した角括弧は、送信された順に0、1、2と数値キーが割り当てられます。特定のキーが必要ならitems[email]のように明示します。

配列の値をそのままデータベースへ保存できますか?

そのまま保存せず、配列・各要素の型、必須値、文字数、許可値を検証します。複数レコードならトランザクションの要否も検討し、prepared statementを使います。

filter_input_arrayを使えば安全ですか?

入力取得とフィルター適用をまとめられますが、期待する配列構造、業務上の許可値、項目間の関係までは自動で保証しません。配列を要求する場合はFILTER_REQUIRE_ARRAYも検討し、最終的にはアプリケーション要件で検証します。

パスワードもaccount[password]で送れますか?

形式上は送れますが、ログやprint_r()へ出してはいけません。HTTPS、CSRF対策、適切なpassword hashing、再表示しない設計など、認証情報として別途安全に扱う必要があります。

まとめ

PHPフォームで配列を送るにはname="staffs[]"、連想配列ならname="account[name]"、複数レコードならname="items[0][name]"のように角括弧を使います。

受信側では、キーの存在だけでなくis_array()と各要素の型を確認し、必要な値だけで新しい配列を作ります。未選択checkbox、入力上限、HTML出力時のエスケープ、hidden値の改ざん、CSRFにも注意してください。