タイトル: $_POST
SEOタイトル: PHP $_POST スーパーグローバル変数完全ガイド
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>$_POST</strong> は <code><form method="post"></code> から送信されたデータが入る連想配列型のスーパーグローバル変数</li><li><code>$_POST["name"]</code> でアクセス。<strong>未定義キー</strong>には <code>isset()</code> または <code>?? null</code> で防御</li><li>XSS / SQL Injection / CSRF を必ず対策。<strong>htmlspecialchars</strong> / <strong>PDO Prepared</strong> / <strong>CSRF Token</strong> が三種の神器</li><li>JSON / multipart は <code>$_POST</code> に入らない場合がある → <code>php://input</code> や <code>$_FILES</code> で取得</li><li>Laravel では <code>$request->input("name")</code> や <code>$request->validated()</code> が定石</li></ul></td></tr></table>
<h2>$_POST とは</h2>
<p>PHP の<strong>スーパーグローバル変数</strong>の 1 つで、HTTP POST リクエストで送信された値が<strong>連想配列</strong>として格納されています。スーパーグローバルなので、<strong>関数の中でも <code>global</code> 宣言不要</strong>でアクセスできます。</p>
<h2>HTML フォームと $_POST の対応</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!-- form.html -->
<form method="post" action="/register.php">
<input type="text" name="username">
<input type="email" name="email">
<input type="password" name="password">
<input type="checkbox" name="agree" value="1">
<select name="role">
<option value="user">一般</option>
<option value="admin">管理者</option>
</select>
<button type="submit">登録</button>
</form></code></pre><p><img alt="POST フォームの例" src="/uploadImage/20170809014116_2945120.jpg" style="max-width:640px;width:100%;height:auto;border:1px solid #ccc;border-radius:4px"></p>
<p>↑ HTML フォーム例(上記のような POST フォームを想定)。送信先 <code>register.php</code> での受け取り:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php"><?php
// register.php
var_dump($_POST);
/*
array(5) {
["username"] => string(4) "Taro"
["email"] => string(13) "taro@test.com"
["password"] => string(8) "secret12"
["agree"] => string(1) "1"
["role"] => string(4) "user"
}
*/
$username = $_POST['username'];
$email = $_POST['email'];</code></pre><p><img alt="$_POST で取得した値の確認" src="/uploadImage/20170809014405_4709410.jpg" style="max-width:640px;width:100%;height:auto;border:1px solid #ccc;border-radius:4px"></p>
<p>↑ 受信側 PHP で <code>$_POST</code> の中身を確認したイメージ。<code>var_dump</code> や <code>print_r</code> で連想配列の内容が見えます。</p>
<h2>未定義キー対策</h2>
<p>checkbox が未チェック / select が未選択だと、そのキー自体が <code>$_POST</code> に<strong>存在しない</strong>。直接アクセスすると Warning:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// ❌ Warning: Undefined array key "agree"
$agree = $_POST['agree'];
// ✅ isset で防御
$agree = isset($_POST['agree']) ? $_POST['agree'] : '0';
// ✅ Null Coalescing (PHP 7+)
$agree = $_POST['agree'] ?? '0';
// ✅ filter_input
$agree = filter_input(INPUT_POST, 'agree', FILTER_VALIDATE_INT);
// 推奨: バリデーション関数にまとめる
function postInput(string $key, string $default = ''): string {
return isset($_POST[$key]) ? trim((string)$_POST[$key]) : $default;
}
$username = postInput('username');</code></pre>
<h2>セキュリティ対策の三種の神器</h2>
<h3>1. XSS 対策(出力時)</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">$comment = $_POST['comment'] ?? '';
// ❌ XSS 脆弱
echo "<p>$comment</p>";
// $comment = "<script>alert(1)</script>" だと実行される
// ✅ htmlspecialchars で対策
echo '<p>' . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8') . '</p>';</code></pre>
<h3>2. SQL Injection 対策</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// ❌ SQL Injection 脆弱
$email = $_POST['email'];
$pdo->query("SELECT * FROM users WHERE email = '$email'");
// ✅ PDO Prepared Statement
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// ✅ 名前付きバインド
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (:n, :e)');
$stmt->execute([
':n' => $_POST['username'],
':e' => $_POST['email'],
]);</code></pre>
<h3>3. CSRF 対策</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">session_start();
// トークン生成(フォーム表示時)
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['csrf_token'];
// HTML 側
// <input type="hidden" name="_token" value="<?= $token ?>">
// 受信時に検証
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['_token'] ?? '')) {
http_response_code(419);
die('CSRF token mismatch');
}</code></pre>
<h2>$_POST に入らないケース</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3e0"><th>ケース</th><th>取得方法</th></tr></thead>
<tbody>
<tr><td>JSON ボディ(<code>Content-Type: application/json</code>)</td><td><code>json_decode(file_get_contents("php://input"), true)</code></td></tr>
<tr><td>ファイルアップロード(<code>multipart/form-data</code>)</td><td><code>$_FILES</code></td></tr>
<tr><td>PUT / DELETE / PATCH リクエスト</td><td><code>php://input</code> から自前パース</td></tr>
<tr><td>カスタム Content-Type</td><td><code>php://input</code></td></tr>
<tr><td><code>post_max_size</code> 超過</td><td>空になる + warning。php.ini で <code>post_max_size</code> 拡大</td></tr>
</tbody>
</table>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// JSON 受信
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!is_array($data)) {
http_response_code(400);
die('Invalid JSON');
}
$username = $data['username'] ?? '';
// ファイルアップロード
if ($_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
move_uploaded_file(
$_FILES['avatar']['tmp_name'],
__DIR__ . '/uploads/' . basename($_FILES['avatar']['name'])
);
}</code></pre>
<h2>$_POST vs $_GET vs $_REQUEST</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>変数</th><th>取得元</th><th>用途</th></tr></thead>
<tbody>
<tr><td><code>$_GET</code></td><td>URL クエリ文字列 (<code>?key=val</code>)</td><td>検索・ページング・冪等な参照</td></tr>
<tr><td><code>$_POST</code></td><td>POST ボディ (<code>application/x-www-form-urlencoded</code>)</td><td>登録・更新・削除</td></tr>
<tr><td><code>$_REQUEST</code></td><td>GET + POST + COOKIE 混合</td><td>★ 非推奨。意図不明になる</td></tr>
<tr><td><code>$_FILES</code></td><td>multipart アップロード</td><td>ファイル受信</td></tr>
<tr><td><code>$_COOKIE</code></td><td>クッキー</td><td>セッション識別等</td></tr>
<tr><td><code>$_SERVER</code></td><td>サーバ環境変数</td><td>REMOTE_ADDR 等</td></tr>
</tbody>
</table>
<h2>filter_input でバリデーション</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// 整数
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, [
'options' => ['min_range' => 0, 'max_range' => 150]
]);
if ($age === false) {
die('年齢が不正');
}
// メール
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if ($email === false) {
die('メール形式不正');
}
// URL
$url = filter_input(INPUT_POST, 'url', FILTER_VALIDATE_URL);
// 配列も一括フィルタ
$data = filter_input_array(INPUT_POST, [
'name' => FILTER_DEFAULT,
'age' => FILTER_VALIDATE_INT,
'email' => FILTER_VALIDATE_EMAIL,
]);</code></pre>
<h2>Laravel での書き方</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">use Illuminate\Http\Request;
public function store(Request $request)
{
// 個別取得
$name = $request->input('name');
$age = $request->input('age', 0); // デフォルト値
// 一括取得
$all = $request->all();
$sub = $request->only(['name', 'email']);
$exc = $request->except(['password']);
// バリデーション
$validated = $request->validate([
'name' => 'required|string|max:100',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
]);
User::create($validated);
}
// FormRequest クラスでバリデーション分離(推奨)
class StoreUserRequest extends FormRequest {
public function rules(): array {
return [
'name' => 'required|string|max:100',
'email' => 'required|email|unique:users',
];
}
}</code></pre>
<h2>FAQ</h2>
<p><strong>Q: <code>$_POST</code> が空になる</strong><br />
A: <code>enctype="multipart/form-data"</code> でもキー名が一致していれば入ります。<code>post_max_size</code> 超過 / <code>Content-Type</code> 違い / <code>php://input</code> を既に読んだ後だと空になることも。</p>
<p><strong>Q: 配列キーで送信したい</strong><br />
A: <code><input name="items[]"></code> や <code><input name="user[name]"></code> で <code>$_POST["items"]</code> や <code>$_POST["user"]["name"]</code> として受け取れます。</p>
<p><strong>Q: 受信した値の型は?</strong><br />
A: 基本<strong>すべて文字列</strong>。数値も <code>"123"</code> として入る。型変換は自前で <code>(int)$_POST["age"]</code> 等。</p>
<p><strong>Q: Magic Quotes でクォートに <code>\</code> が付く</strong><br />
A: PHP 5.4 で廃止済み。古い環境なら <code>stripslashes()</code> で除去。</p>