PHP 入門

PHP $_GET と $_POST

PHP のスーパーグローバル変数(Superglobals)は、ビルトインのグローバル変数であり、スクリプト内のあらゆるスコープで常に利用可能です。これらの特殊な変数には、サーバー、クライアント、現在のスクリプトなどに関する多くの情報が格納されています。本章では、最も頻繁に使用される 2 つのスーパーグローバル変数、$_GET$_POST に焦点を当てます。これらは HTML フォームから送信されたデータを処理するための基石であり、PHP スクリプトがクライアントから送信されたデータを読み取ることを可能にすることで、動的でインタラクティブな Web アプリケーションを実現します。

1. スーパーグローバル変数とは

スーパーグローバル変数は、PHP が提供するアレイ(配列)変数のセットであり、現在のスコープに関係なく、スクリプトのどこからでもアクセスできます。つまり、関数、メソッド、およびグローバルなスクリプト領域内で直接使用でき、通常のグローバル変数の場合のように global キーワードを使用して明示的に宣言する必要はありません。各スーパーグローバル変数には特定の用途があり、リクエスト、セッション、サーバー、または環境に関連する異なるタイプのデータを格納するために使用されます。

例えば、関数の外部で通常の変数を定義したとします。関数内部でその変数にアクセスするには、通常、引数として渡すか、global として宣言する必要があります。スーパーグローバル変数はこの制限をバイパスし、特定のコアデータに対してグローバルなアクセスを可能にします。

2. $_GET スーパーグローバル変数

$_GET スーパーグローバル変数は連想配列であり、URL パラメータを介して現在のスクリプトに渡されたすべてのバリアブル(変数)が含まれます。ユーザーがリンクをクリックしたり、HTTP GET メソッドを使用してフォームを送信したりすると、データは URL の末尾に付加され、いわゆる「クエリ文字列(Query String)」を形成します。$_GET アレイはこのクエリ文字列を自動的にパース(解析)し、各パラメータをキー・バリュー・ペア(Key-Value pairs)に変換して利用できるようにします。

2.1 $_GET の仕組み

データが GET メソッドを介して送信されると、それは URL 内のクエスチョンマーク (?) の後に表示されます。各パラメータはキー・バリュー・ペアであり、パラメータ間はアンパサンド (&) で区切られます。例えば、[www.example.com/script.php?name=John&age=30](https://www.example.com/script.php?name=John&age=30) という URL があるとします。この URL では、nameage がキー(Key)であり、John30 がそれらに対応する値(Value)です。

PHP は自動的に $_GET アレイにデータを入力します:

  • $_GET['name'] の値は 'John' になります。
  • $_GET['age'] の値は '30' になります。

2.2 $_GET の実践的な活用例

ケース 1:リンクを介したデータ伝送

シンプルな製品カタログを想像してください。ユーザーが特定の製品をクリックして、その詳細情報を表示します。製品 ID は URL を介して渡すことができます。

<?php
// product_list.php (製品リストページ)
echo '<a href="product_details.php?id=101&category=electronics">製品 101 を見る</a><br>';
echo '<a href="product_details.php?id=102&category=books">製品 102 を見る</a>';
?>

次に、product_details.php でこれらのデータを取得できます:

<?php
// product_details.php (製品詳細ページ)
if (isset($_GET['id'])) {
    $productId = $_GET['id'];
    echo "製品 ID: " . htmlspecialchars($productId) . "<br>";
} else {
    echo "製品 ID が指定されていません。<br>";
}

if (isset($_GET['category'])) {
    $productCategory = $_GET['category'];
    echo "カテゴリー: " . htmlspecialchars($productCategory) . "<br>";
} else {
    echo "カテゴリーが指定されていません。<br>";
}
?>

この例では、htmlspecialchars() 関数を使用して XSS(クロスサイトスクリプティング) 攻撃を防止しています。これは特殊文字を HTML エンティティに変換するもので、ユーザーが提供したデータを出力する際の極めて重要なセキュリティ対策です。

ケース 2:シンプルな検索機能

検索バーでは通常、検索ワードを送信するために GET メソッドが使用されます。

<form action="search_results.php" method="GET">
    <label for="query">検索:</label>
    <input type="text" id="query" name="search_query">
    <input type="submit" value="検索">
</form>

ユーザーが "PHP basics" と入力してフォームを送信すると、URL は search_results.php?search_query=PHP+basics のようになります。

<?php
// search_results.php (検索結果ページ)
if (isset($_GET['search_query'])) {
    $searchQuery = $_GET['search_query'];
    echo "検索ワード: " . htmlspecialchars($searchQuery) . "<br>";
    // 実際のアプリケーションでは、このクエリワードを使用してデータベースから結果を取得します。
} else {
    echo "検索ワードを入力してください。";
}
?>

URL 内の + 記号はスペースを表し、$_GET はそれを自動的にスペース文字としてデコードします。

2.3 $_GET のユースケースと注意点

  • ブックマークと共有: GET パラメータを含む URL は、ページの状態(検索結果、フィルタリングされたリストなど)が URL に直接反映されるため、簡単にブックマークしたり共有したりできます。
  • ステートレスな操作: GET リクエストは通常、サーバーからデータを取得するために使用されます。このようなリクエストは、サーバー側の状態を変更すべきではありません。
  • 制限事項:
    • URL 長の制限: ほとんどのブラウザとサーバーは URL の長さに制限(通常は 2048 文字)を設けており、大量のデータを伝送するには不向きです。
    • セキュリティ: GET で送信されたデータは URL、ブラウザ履歴、およびサーバーログに表示されます。そのため、パスワードや個人情報などの機密情報を送信するために使用してはなりません。
    • べき等性 (Idempotence): GET リクエストはべき等であるべきです。つまり、同じリクエストを何度実行しても、結果は 1 回実行したときと同じである必要があります(サーバーに副作用を与えてはいけません)。

3. $_POST スーパーグローバル変数

$_POST スーパーグローバル変数は連想配列であり、HTTP POST メソッドを介して現在のスクリプトに渡されたすべてのバリアブルが含まれます。GET とは異なり、POST を介して送信されたデータは URL の末尾に付加されません。代わりに、HTTP リクエストのリクエストボディ(Body)に含まれます。これにより、データが直接露出しないため、大量のデータや機密情報を安全に送信するのに適しています。

3.1 $_POST の仕組み

POST メソッドを使用してフォームを送信すると、ブラウザはフォームデータを HTTP リクエストボディにパッケージ化して送信します。PHP はこのリクエストボディを自動的にパースし、フォームフィールドの name 属性をキーとし、ユーザーの入力内容を値として $_POST アレイに格納します。

3.2 $_POST の実践的な活用例

ケース 1:ユーザー登録フォーム
登録フォームでは通常、パスワードなどの機密情報を含むユーザーデータを送信するために POST が使用されます。

<form action="process_registration.php" method="POST">
    <label for="username">ユーザー名:</label>
    <input type="text" id="username" name="username" required><br><br>
        
    <label for="password">パスワード:</label>
    <input type="password" id="password" name="password" required><br><br>
        
    <label for="email">メールアドレス:</label>
    <input type="email" id="email" name="email" required><br><br>
        
    <input type="submit" value="登録">
</form>

process_registration.php スクリプトでは、$_POST を使用してこれらのデータにアクセスします:

<?php
// process_registration.php (登録リクエストの処理)
if ($_SERVER["REQUEST_METHOD"] == "POST") { // フォームが POST 経由で送信されたかチェック
    $username = htmlspecialchars($_POST['username']);
    $email = htmlspecialchars($_POST['email']);
    $password = $_POST['password']; // パスワードは保存前に必ずハッシュ化する必要があります!
        
    echo "登録成功、ようこそ: " . $username . "さん<br>";
    echo "メールアドレス: " . $email . "<br>";
        
    // 実際のアプリケーションでは、パスワードをハッシュ化してユーザーデータをデータベースに保存します。
    // 例: $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
    // その後、$hashedPassword をデータベースに保存します。
} else {
    echo "アクセスが拒否されました。このページはフォーム送信からのみアクセス可能です。";
}
?>

$_SERVER["REQUEST_METHOD"] == "POST" を使用したチェックは一般的な手法であり、スクリプトが POST リクエストを受信したときにのみ処理ロジックを実行することを保証します。これにより、ユーザーがブラウザののアドレスバーから直接スクリプトにアクセスするのを防ぐことができます。

ケース 2:コメントの投稿
コメントフォームも通常、コメント内容とユーザーの詳細を送信するために POST を使用します。

<form action="add_comment.php" method="POST">
    <label for="author">お名前:</label>
    <input type="text" id="author" name="author" required><br><br>
        
    <label for="comment_text">コメント内容:</label><br>
    <textarea id="comment_text" name="comment_text" rows="5" cols="40" required></textarea><br><br>
        
    <input type="hidden" name="article_id" value="123"> 
    <input type="submit" value="コメントを投稿">
</form>

add_comment.php スクリプト:

<?php
// add_comment.php (コメント投稿の処理)
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $author = htmlspecialchars($_POST['author']);
    $commentText = htmlspecialchars($_POST['comment_text']);
    $articleId = htmlspecialchars($_POST['article_id']); // 隠しフィールドから取得
        
    echo $author . "さんが記事 ID " . $articleId . " にコメントしました:<br>";
    echo "<blockquote>" . $commentText . "</blockquote>";
        
    // 実際のアプリケーションでは、このコメントをデータベースに保存します。
} else {
    echo "無効なリクエストメソッドです。";
}
?>

隠し入力フィールド(<input type="hidden">)は、ユーザーに見せる必要はないがバックエンド処理に不可欠なデータ(ここでは article_id)を送信するためによく使われます。

3.3 $_POST のユースケースと注意点

  • 機密データ: データが URL に露出しないため、パスワードやクレジットカード番号などの機密情報を送信する際の第一選択肢です。
  • 大容量データ: POST を介して送信されるデータ量には通常、実質的な制限はありません(サーバー設定による制限はあります)。
  • 状態変更操作: POST リクエストは通常、サーバー上のデータを変更する操作(新規レコードの作成、既存レコードの更新、削除など)に使用されます。
  • 非べき等性: POST リクエストは通常、べき等ではありません。同じリクエストを複数回送信すると、複数のレコードが作成されたり更新されたりする可能性があります(例:送信ボタンを 2 回連続でクリックすると、同じレコードが 2 つ作成される可能性があります)。
  • 履歴/ブックマーク: POST データはブラウザの履歴やブックマークに保存されません。これにより、機密性の高い操作やトランザクション操作に適しています。

4. Web 開発におけるセキュリティの基礎

$_GET$_POST を使用する際、セキュリティは最優先事項です。クライアントからのいかなるデータも決して信頼してはいけません。悪意のあるユーザーは、URL やフォーム送信を偽装して有害なコードを注入しようとする可能性があります。

4.1 XSS(クロスサイトスクリプティング)対策

前述のケースで示したように、htmlspecialchars() は XSS 攻撃を防ぐためのコア関数です。これは特殊な HTML 文字(<, >, &, ", ')を対応する HTML エンティティに変換します。ユーザーが提供したデータをエスケープせずに出力すると、攻撃者が <script> タグなどを注入し、他のユーザーのブラウザで悪意のあるコードを実行させる可能性があります。

// 不安全:ユーザー入力を直接出力
echo "こんにちは、" . $_GET['name']; 
// もし $_GET['name'] が "<script>alert('XSS!')</script>" だった場合、非常に危険です。

// 安全:出力前にユーザー入力をエスケープ
echo "こんにちは、" . htmlspecialchars($_GET['name']); 
// 安全に "こんにちは、<script>alert('XSS!')</script>" と出力されます。

4.2 SQL インジェクション対策

$_GET$_POST データを直接 SQL クエリに使用する場合(例:フォームから送信されたユーザー名に基づいてデータベースから詳細を取得するなど)、プリペアドステートメントとパラメータ化クエリを使用しなければなりません。ユーザー入力を SQL 文字列に直接連結することは極めて危険であり、SQL インジェクション攻撃を招きます。これについては本章の範囲外ですが、データベース接続を学ぶ際に必ず覚えておくべき重要な概念です。

5. 総合実践:シンプルな挨拶プログラムの構築

$_GET$_POST の知識を組み合わせて、シンプルな挨拶アプリケーションを構築してみましょう。フォームから POST で名前を送信することも、GET リンクから直接名前を渡すこともできるプログラムを作成します。

まず、HTML ファイル greeting_form.html を作成します:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>挨拶フォーム</title>
</head>
<body>
    <h2>挨拶をしましょう!</h2>
        
    <form action="greet.php" method="POST">
        <label for="name_post">お名前を入力してください (POST方式):</label><br>
        <input type="text" id="name_post" name="user_name_post" required><br><br>
        <input type="submit" value="POST で挨拶する">
    </form>
        
    <hr>
    <h3>またはリンクをクリックして挨拶:</h3>
    <p>
        <a href="greet.php?user_name_get=アリス">アリスに挨拶</a><br>
        <a href="greet.php?user_name_get=ボブ">ボブに挨拶</a><br>
        <a href="greet.php?user_name_get=チャーリー">チャーリーに挨拶</a>
    </p>
        
    <hr>
    <p>URL に直接名前を入力することもできます:</p>
    <p><code>http://localhost/greet.php?user_name_get=あなたのお名前</code></p>
</body>
</html>

次に、PHP スクリプト greet.php を作成します:

<?php
// greet.php
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>挨拶ページ</title>
</head>
<body>
    <h1>こんにちは!</h1>
    <?php
    $greetingName = '';
        
    // POST で名前が送信されたかチェック
    if (isset($_POST['user_name_post']) && !empty($_POST['user_name_post'])) {
        $greetingName = htmlspecialchars($_POST['user_name_post']);
        echo "<p>こんにちは、" . $greetingName . "さん! (POSTデータより)</p>";
    }
    // GET で名前が送信されたかチェック
    else if (isset($_GET['user_name_get']) && !empty($_GET['user_name_get'])) {
        $greetingName = htmlspecialchars($_GET['user_name_get']);
        echo "<p>こんにちは、" . $greetingName . "さん! (GETデータより)</p>";
    }
    // 名前が提供されなかった場合
    else {
        echo "<p>ハロー!名前を教えてくれたら挨拶するよ。</p>";
        echo "<p>上のフォームを使うか、<code>greet.php?user_name_get=名前</code> のように URL パラメータを使ってみてね。</p>";
    }
    ?>
    <p><a href="greeting_form.html">フォームページに戻る</a></p>
</body>
</html>

このサンプルを実行するには、これら 2 つのファイルを Web サーバーのドキュメントルート(例:XAMPP の htdocs フォルダなど)に保存し、ブラウザから http://localhost/greeting_form.html にアクセスしてテストしてください。この例は、変数の使用前に isset()!empty() でチェックする方法、および htmlspecialchars() の重要性を明確に示しています。