PHP $_GET 变量详解

在PHP中,$_GET是一个超全局变量,用于收集来自使用method="get"的表单数据,或者通过URL查询字符串传递的参数。它是Web开发中获取用户输入的重要方式之一。

$_GET 变量基础

$_GET是一个关联数组,包含了通过URL查询字符串传递的所有参数。当表单使用method="get"提交时,表单数据会自动添加到$_GET数组中。

特点 说明
数据类型 关联数组
数据来源 URL查询字符串(?key=value&key2=value2)
数据可见性 数据在URL中可见
数据长度限制 受URL长度限制(通常2000字符左右)
缓存 可被浏览器缓存
安全性 不适合传输敏感信息

基础使用示例

HTML 表单示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>GET方法表单示例</title>
</head>
<body>
    <h1>用户信息表单(GET方法)</h1>

    <form action="process_get.php" method="get">
        <div style="margin-bottom: 15px;">
            <label for="fname">名字:</label>
            <input type="text" id="fname" name="fname" placeholder="请输入您的名字">
        </div>

        <div style="margin-bottom: 15px;">
            <label for="age">年龄:</label>
            <input type="number" id="age" name="age" placeholder="请输入您的年龄" min="1" max="120">
        </div>

        <div style="margin-bottom: 15px;">
            <label for="country">国家:</label>
            <select id="country" name="country">
                <option value="">请选择国家</option>
                <option value="china">中国</option>
                <option value="usa">美国</option>
                <option value="uk">英国</option>
            </select>
        </div>

        <div style="margin-bottom: 15px;">
            <label>兴趣爱好:</label><br>
            <input type="checkbox" id="coding" name="hobbies[]" value="coding">
            <label for="coding">编程</label>

            <input type="checkbox" id="reading" name="hobbies[]" value="reading">
            <label for="reading">阅读</label>

            <input type="checkbox" id="sports" name="hobbies[]" value="sports">
            <label for="sports">运动</label>
        </div>

        <input type="submit" value="提交" style="padding: 10px 20px;">
    </form>
</body>
</html>

PHP 处理脚本 (process_get.php):

<?php
// 检查是否有GET参数传递
echo "<h1>接收到的GET数据</h1>";

// 方法1:直接访问特定参数(不安全,不推荐)
if (isset($_GET['fname'])) {
    echo "<p><strong>名字:</strong> " . htmlspecialchars($_GET['fname']) . "</p>";
}

if (isset($_GET['age'])) {
    echo "<p><strong>年龄:</strong> " . htmlspecialchars($_GET['age']) . "</p>";
}

// 方法2:遍历所有GET参数(更安全)
echo "<h2>所有GET参数:</h2>";
echo "<table border='1' cellpadding='8' style='border-collapse: collapse;'>";
echo "<tr><th>参数名</th><th>参数值</th></tr>";

foreach ($_GET as $key => $value) {
    echo "<tr>";
    echo "<td>" . htmlspecialchars($key) . "</td>";

    // 处理数组值(如复选框)
    if (is_array($value)) {
        echo "<td>" . htmlspecialchars(implode(', ', $value)) . "</td>";
    } else {
        echo "<td>" . htmlspecialchars($value) . "</td>";
    }

    echo "</tr>";
}

echo "</table>";

// 显示完整的URL
echo "<h3>完整的URL:</h3>";
echo "<p>" . htmlspecialchars($_SERVER['REQUEST_URI']) . "</p>";

// 显示查询字符串
echo "<h3>查询字符串:</h3>";
echo "<p>" . htmlspecialchars($_SERVER['QUERY_STRING']) . "</p>";
?>

URL参数传递示例

除了表单提交,$_GET也常用于处理URL中的查询参数。

示例:产品详情页

<?php
// product.php - 产品详情页面

// 定义产品数据(在实际应用中可能来自数据库)
$products = [
    'p001' => [
        'name' => 'iPhone 14',
        'price' => 6999,
        'category' => '手机',
        'description' => '苹果最新款智能手机'
    ],
    'p002' => [
        'name' => 'MacBook Pro',
        'price' => 12999,
        'category' => '电脑',
        'description' => '专业级笔记本电脑'
    ],
    'p003' => [
        'name' => 'AirPods Pro',
        'price' => 1999,
        'category' => '配件',
        'description' => '降噪无线耳机'
    ]
];

// 检查是否有产品ID参数
if (isset($_GET['product_id']) && isset($products[$_GET['product_id']])) {
    $product_id = $_GET['product_id'];
    $product = $products[$product_id];

    echo "<h1>产品详情</h1>";
    echo "<div style='border: 1px solid #ddd; padding: 20px; border-radius: 5px;'>";
    echo "<h2>" . htmlspecialchars($product['name']) . "</h2>";
    echo "<p><strong>价格:</strong> ¥" . number_format($product['price'], 2) . "</p>";
    echo "<p><strong>分类:</strong> " . htmlspecialchars($product['category']) . "</p>";
    echo "<p><strong>描述:</strong> " . htmlspecialchars($product['description']) . "</p>";
    echo "</div>";

    // 显示URL以便参考
    echo "<p style='margin-top: 20px; color: #666;'>";
    echo "当前URL: " . htmlspecialchars($_SERVER['REQUEST_URI']);
    echo "</p>";
} else {
    echo "<h2>产品列表</h2>";
    echo "<ul>";

    foreach ($products as $id => $product) {
        // 生成包含product_id参数的链接
        $product_url = "product.php?product_id=" . urlencode($id);
        echo "<li><a href='" . htmlspecialchars($product_url) . "'>";
        echo htmlspecialchars($product['name']) . " - ¥" . number_format($product['price'], 2);
        echo "</a></li>";
    }

    echo "</ul>";
}
?>

示例:分页和搜索功能

<?php
// search.php - 搜索和分页示例

// 模拟数据(实际应用中来自数据库)
$all_items = [
    ['id' => 1, 'title' => 'PHP基础教程', 'category' => '教程'],
    ['id' => 2, 'title' => 'MySQL数据库', 'category' => '数据库'],
    ['id' => 3, 'title' => 'JavaScript高级', 'category' => '前端'],
    ['id' => 4, 'title' => 'Python数据分析', 'category' => '数据分析'],
    ['id' => 5, 'title' => 'PHP实战项目', 'category' => '教程'],
    ['id' => 6, 'title' => 'CSS3动画', 'category' => '前端'],
    ['id' => 7, 'title' => 'Redis缓存', 'category' => '数据库'],
    ['id' => 8, 'title' => 'Vue.js框架', 'category' => '前端']
];

// 获取GET参数(带默认值)
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$keyword = isset($_GET['keyword']) ? trim($_GET['keyword']) : '';
$category = isset($_GET['category']) ? $_GET['category'] : '';

// 数据过滤
$filtered_items = $all_items;

// 关键词搜索
if (!empty($keyword)) {
    $filtered_items = array_filter($filtered_items, function($item) use ($keyword) {
        return stripos($item['title'], $keyword) !== false;
    });
}

// 分类筛选
if (!empty($category)) {
    $filtered_items = array_filter($filtered_items, function($item) use ($category) {
        return $item['category'] === $category;
    });
}

// 分页设置
$items_per_page = 3;
$total_items = count($filtered_items);
$total_pages = ceil($total_items / $items_per_page);

// 确保当前页有效
$page = max(1, min($page, $total_pages));

// 计算起始位置
$start = ($page - 1) * $items_per_page;
$current_items = array_slice($filtered_items, $start, $items_per_page);

// 显示搜索表单
echo "<form method='get' action='search.php' style='margin-bottom: 20px;'>";
echo "<input type='text' name='keyword' value='" . htmlspecialchars($keyword) . "' placeholder='输入关键词...'>";
echo "<select name='category'>";
echo "<option value=''>所有分类</option>";
echo "<option value='教程' " . ($category == '教程' ? 'selected' : '') . ">教程</option>";
echo "<option value='数据库' " . ($category == '数据库' ? 'selected' : '') . ">数据库</option>";
echo "<option value='前端' " . ($category == '前端' ? 'selected' : '') . ">前端</option>";
echo "<option value='数据分析' " . ($category == '数据分析' ? 'selected' : '') . ">数据分析</option>";
echo "</select>";
echo "<input type='submit' value='搜索'>";
echo "</form>";

// 显示搜索结果
echo "<h2>搜索结果(共 {$total_items} 条)</h2>";

if (empty($current_items)) {
    echo "<p>没有找到相关结果。</p>";
} else {
    echo "<ul>";
    foreach ($current_items as $item) {
        echo "<li>";
        echo "<strong>" . htmlspecialchars($item['title']) . "</strong>";
        echo " - <span style='color: #666;'>" . htmlspecialchars($item['category']) . "</span>";
        echo "</li>";
    }
    echo "</ul>";
}

// 显示分页链接
if ($total_pages > 1) {
    echo "<div class='pagination' style='margin-top: 20px;'>";

    // 构建基础URL(保留现有参数)
    $base_url = "search.php?";
    $params = [];

    if (!empty($keyword)) {
        $params[] = "keyword=" . urlencode($keyword);
    }
    if (!empty($category)) {
        $params[] = "category=" . urlencode($category);
    }

    $param_string = implode('&', $params);

    // 上一页
    if ($page > 1) {
        $prev_url = $base_url . $param_string . "&page=" . ($page - 1);
        echo "<a href='" . htmlspecialchars($prev_url) . "'>上一页</a> ";
    }

    // 页码
    for ($i = 1; $i <= $total_pages; $i++) {
        if ($i == $page) {
            echo "<strong>$i</strong> ";
        } else {
            $page_url = $base_url . $param_string . "&page=" . $i;
            echo "<a href='" . htmlspecialchars($page_url) . "'>$i</a> ";
        }
    }

    // 下一页
    if ($page < $total_pages) {
        $next_url = $base_url . $param_string . "&page=" . ($page + 1);
        echo "<a href='" . htmlspecialchars($next_url) . "'>下一页</a>";
    }

    echo "</div>";
}
?>

GET方法的安全注意事项

安全性警告

  • GET参数在URL中可见,不适合传输敏感信息(密码、信用卡号等)
  • GET请求可以被浏览器缓存和历史记录保存
  • GET请求可能被日志记录或引用头信息泄露
  • URL长度有限制,不适合大量数据

敏感信息的错误示例:

<?php
// ❌ 错误:使用GET传递敏感信息
// URL将显示为:login.php?username=admin&password=123456
if (isset($_GET['username']) && isset($_GET['password'])) {
    $username = $_GET['username'];
    $password = $_GET['password'];

    // 验证登录...
}

// ✅ 正确:使用POST传递敏感信息
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $username = $_POST['username'];
    $password = $_POST['password'];

    // 验证登录...
}
?>

安全的GET参数处理:

<?php
// 安全处理GET参数的函数
function safe_get_param($key, $default = '', $filter = FILTER_SANITIZE_STRING) {
    if (!isset($_GET[$key])) {
        return $default;
    }

    // 使用filter_var进行过滤
    $value = filter_var($_GET[$key], $filter);

    // 额外的清理(如果需要)
    $value = trim($value);
    $value = stripslashes($value);

    return $value;
}

// 使用示例
$product_id = safe_get_param('product_id', '', FILTER_SANITIZE_STRING);
$page = safe_get_param('page', 1, FILTER_VALIDATE_INT);

// 验证整数范围
if ($page < 1 || $page > 100) {
    $page = 1;
}

// 对于搜索关键词,进行额外的HTML转义
$keyword = safe_get_param('keyword', '');
$keyword = htmlspecialchars($keyword, ENT_QUOTES, 'UTF-8');
?>

GET vs POST 方法对比

特性 GET 方法 POST 方法
数据位置 URL查询字符串 HTTP请求体
数据可见性 可见(在URL中) 不可见
数据长度限制 受URL长度限制(~2000字符) 无限制
缓存性 可被缓存、书签收藏 不可缓存
安全性 较低(日志、历史记录可能记录) 较高(不在URL中暴露)
数据类型 只支持ASCII字符 支持二进制数据(如文件上传)
后退/刷新 无害(数据在URL中) 数据会重新提交(浏览器警告)
主要用途 获取数据、搜索、分页、过滤 提交数据、创建/更新资源、敏感操作

最佳实践

GET方法使用建议

  • 幂等操作 - GET请求应该是幂等的(多次执行结果相同)
  • 数据获取 - 用于获取数据,而不是修改数据
  • 书签功能 - 当需要页面可被收藏时使用GET
  • 缓存优化 - 利用GET的缓存特性提升性能
  • SEO友好 - 搜索引擎可以抓取GET参数
  • 参数验证 - 始终验证和过滤GET参数

实际应用场景:

  1. 搜索功能 - 搜索关键词、过滤条件使用GET
  2. 分页导航 - 页码参数使用GET
  3. 产品/内容详情 - ID参数使用GET
  4. 筛选和排序 - 筛选条件、排序方式使用GET
  5. 社交媒体分享 - 分享链接使用GET参数
  6. API调用 - RESTful API的GET请求用于读取资源

代码示例:RESTful API风格的GET请求

<?php
// api.php - RESTful API示例

header('Content-Type: application/json');

// 获取资源类型和ID
$resource = isset($_GET['resource']) ? $_GET['resource'] : '';
$id = isset($_GET['id']) ? $_GET['id'] : '';

// 模拟数据
$data = [
    'users' => [
        'u001' => ['id' => 'u001', 'name' => '张三', 'email' => 'zhangsan@example.com'],
        'u002' => ['id' => 'u002', 'name' => '李四', 'email' => 'lisi@example.com'],
    ],
    'products' => [
        'p001' => ['id' => 'p001', 'name' => '产品A', 'price' => 100],
        'p002' => ['id' => 'p002', 'name' => '产品B', 'price' => 200],
    ]
];

// 处理API请求
switch ($_SERVER['REQUEST_METHOD']) {
    case 'GET':
        if (empty($resource)) {
            // 列出所有可用资源
            echo json_encode(['resources' => array_keys($data)]);
        } elseif (isset($data[$resource])) {
            if (empty($id)) {
                // 获取资源列表
                echo json_encode(array_values($data[$resource]));
            } elseif (isset($data[$resource][$id])) {
                // 获取特定资源
                echo json_encode($data[$resource][$id]);
            } else {
                http_response_code(404);
                echo json_encode(['error' => '资源不存在']);
            }
        } else {
            http_response_code(404);
            echo json_encode(['error' => '资源类型不存在']);
        }
        break;

    default:
        http_response_code(405);
        echo json_encode(['error' => '方法不允许']);
}
?>

常见问题

问题1:URL参数包含特殊字符

<?php
// 使用urlencode()处理URL参数
$search_term = "PHP & MySQL";
$encoded_term = urlencode($search_term);
// 结果: "PHP+%26+MySQL"

$url = "search.php?q=" . $encoded_term;

// 接收时使用urldecode()
if (isset($_GET['q'])) {
    $search_term = urldecode($_GET['q']);
    // 记得进行安全过滤
    $search_term = htmlspecialchars($search_term, ENT_QUOTES, 'UTF-8');
}
?>

问题2:处理数组参数

<?php
// 表单中有 name="categories[]" 的多个复选框
// URL将显示为: process.php?categories[]=php&categories[]=mysql

if (isset($_GET['categories'])) {
    // $_GET['categories'] 是一个数组
    $categories = $_GET['categories'];

    // 验证数组内容
    if (is_array($categories)) {
        $categories = array_map('htmlspecialchars', $categories);
        echo "选择的分类: " . implode(', ', $categories);
    }
}
?>

问题3:防止参数篡改

<?php
// 添加参数签名防止篡改
function generate_signed_url($params) {
    $secret_key = 'your-secret-key';

    // 排序参数
    ksort($params);

    // 构建查询字符串
    $query_string = http_build_query($params);

    // 生成签名
    $signature = hash_hmac('sha256', $query_string, $secret_key);

    // 添加签名到参数
    $params['signature'] = $signature;

    return 'process.php?' . http_build_query($params);
}

// 验证签名
function verify_signature($params) {
    $secret_key = 'your-secret-key';

    // 获取签名
    $received_signature = $params['signature'] ?? '';
    unset($params['signature']);

    // 排序参数
    ksort($params);

    // 构建查询字符串
    $query_string = http_build_query($params);

    // 计算签名
    $expected_signature = hash_hmac('sha256', $query_string, $secret_key);

    return hash_equals($expected_signature, $received_signature);
}
?>