CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
PHP Web 入门/课时/Web 请求处理
RawBlame
课时目录12 节

PHP Web 入门

  1. 01PHP 是什么
  2. 02基础语法
  3. 03函数与命名空间
  4. 04面向对象
  5. 05Composer 依赖
  6. 06Web 请求处理
  7. 07PDO 与 MySQL
  8. 08REST API 风格
  9. 09PHP 8 新特性
  10. 10Docker 运行 PHP
  11. 11PHP vs Java 选型
  12. 12综合练习

Web 请求处理

$_GET/$_POST、路由直觉、与 MVC 对比。

35 分钟

Web 请求处理

这一课你将能做到

  1. 用 $_GET / $_POST / $_SERVER 等超全局变量拿到请求的全部信息
  2. 处理一个完整的 HTML 表单:校验、防 XSS 转义、PRG 重定向
  3. 用 Session 实现「登录一次,多页可用」的状态保持,理解 Cookie 与 Session 的分工

为什么要学

没有框架时,PHP 处理请求的原生方式就是超全局变量 + header + session——这些能力后来被 Laravel 的 Request、Session 门面包了一层,但内核没变。理解原生机制,你才能明白框架帮你做了什么(参数绑定、CSRF token、Session 驱动),也才能在排查「表单提交乱码」「Session 丢失」这类问题时定位到根因。对 Java 开发者来说,这一课相当于 Servlet 的 request/response/session 三件套。

人话:超全局变量是 PHP 帮你拆好的「快递包裹」(请求参数、请求头、文件),header() 是「盖章回执」(状态码、跳转),Session 是「储物柜」(跨请求记住你是谁)。

先建立图像

一次表单请求的数据流:

浏览器表单 POST /login
  │  请求体: username=tom&password=123
  ▼
PHP 引擎自动填充超全局变量
  $_POST['username'] / $_SERVER['REQUEST_METHOD'] / $_COOKIE[...]
  ▼
你的脚本:校验 → 查库 → $_SESSION['uid']=1
  ▼
setcookie(PHPSESSID) + 302 重定向 → 浏览器后续请求带着 Cookie
  ▼
每个请求 session_start() 凭 Cookie 找回 $_SESSION

跟着做

步骤 1:跑通最小请求-响应

新建 web/ 目录,放 hello.php:

<?php
declare(strict_types=1);
header('Content-Type: text/html; charset=utf-8');

$method = $_SERVER['REQUEST_METHOD'];
$user   = $_GET['name'] ?? '访客';

echo "<h1>请求方法:{$method}</h1>";
echo "<p>你好," . htmlspecialchars($user, ENT_QUOTES, 'UTF-8') . "</p>";

在该目录 php -S localhost:8000,访问 http://localhost:8000/hello.php?name=Spring。试试传 <b>粗体</b> 作为 name——被转义成文本原样显示,这就是 htmlspecialchars 的作用。

步骤 2:处理 POST 表单(PRG 模式)

login.php——自提交表单 + 校验 + 重定向:

<?php
declare(strict_types=1);
session_start();
header('Content-Type: text/html; charset=utf-8');

$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';

    if ($username === '') {
        $errors['username'] = '用户名不能为空';
    }
    if (strlen($password) < 6) {
        $errors['password'] = '密码至少 6 位';
    }

    if ($errors === []) {
        $_SESSION['username'] = $username;          // 登录态写入 Session
        header('Location: /welcome.php', true, 302); // PRG:成功后重定向
        exit;                                        // header 后必须 exit
    }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>登录</title></head>
<body>
<form method="post" action="login.php">
    <label>用户名 <input name="username" value="<?= htmlspecialchars($_POST['username'] ?? '') ?>"></label>
    <span style="color:red"><?= $errors['username'] ?? '' ?></span>
    <label>密码 <input type="password" name="password"></label>
    <span style="color:red"><?= $errors['password'] ?? '' ?></span>
    <button type="submit">登录</button>
</form>
</body>
</html>

welcome.php 验证登录态:

<?php
session_start();
if (!isset($_SESSION['username'])) {
    header('Location: /login.php', true, 302);
    exit;
}
echo '欢迎回来,' . htmlspecialchars($_SESSION['username'], ENT_QUOTES, 'UTF-8');

PRG(Post-Redirect-Get)解决「刷新重复提交」:POST 成功后立刻 302 跳转,浏览器地址栏变成 GET 页面,F5 刷新不会再次提交表单。

步骤 3:读懂 $_SERVER 关键键名

<?php
// 常用键速查(值因环境而异)
var_dump(
    $_SERVER['REQUEST_METHOD'],    // GET / POST / PUT ...
    $_SERVER['REQUEST_URI'],       // /hello.php?name=x
    $_SERVER['QUERY_STRING'],      // name=x
    $_SERVER['HTTP_USER_AGENT'],   // 请求头 User-Agent
    $_SERVER['HTTP_CONTENT_TYPE'] ?? '-',
    $_SERVER['REMOTE_ADDR'],       // 客户端 IP(经代理时是代理 IP)
    $_SERVER['SCRIPT_NAME']        // 当前脚本路径
);

// 自定义请求头:X-Token → $_SERVER['HTTP_X_TOKEN']
// 请求体原始内容(JSON API 场景):file_get_contents('php://input')

步骤 4:Cookie 与 Session 的分工

<?php
declare(strict_types=1);
session_start();

// Cookie:存在浏览器,每次请求自动携带,适合非敏感偏好
setcookie('theme', 'dark', [
    'expires'  => time() + 86400 * 7,
    'path'     => '/',
    'secure'   => true,     // 仅 HTTPS 传输
    'httponly' => true,     // JS 不可读,防 XSS 偷取
    'samesite' => 'Lax',    // 缓解 CSRF
]);

// Session:数据存服务端(默认文件),浏览器只存 PHPSESSID
$_SESSION['cart'] = ['键盘', '鼠标'];
$theme = $_COOKIE['theme'] ?? 'light';
echo "主题:{$theme},购物车:" . count($_SESSION['cart']) . ' 件';

// 退出登录
// session_unset(); session_destroy();

讲清楚要点

  1. 超全局变量八姐妹:$_GET、$_POST、$_COOKIE、$_SESSION、$_SERVER、$_FILES、$_ENV、$_REQUEST(= GET+POST+COOKIE 的混合,容易混淆来源,生产慎用)。它们在函数内也可直接访问,无需 global 声明。
  2. 所有输入都是不可信字符串:$_GET['page'] 拿到的是 string,比较数字前要 (int) 强转;$_POST['age'] 再像数字也不是数字。校验(filter_var)+ 类型转换 + 白名单,三步缺一不可。
  3. header() 之前不能有任何输出:包括空行、BOM、echo。一旦输出已发送,再设状态码/头会报 headers already sent。安全姿势:业务逻辑全写在前,模板渲染放最后,header 跳转后立即 exit。
  4. Session 生命周期:session_start() 按 Cookie 里的 PHPSESSID 加载服务端数据;默认存文件(session.save_path),生产可切 Redis(session.save_handler=redis)实现多机共享——这正是 Java 应用把 Session 放 Redis 的同款方案。

你可能会懵的地方

  • GET 和 POST 除了位置还有什么区别? 语义区别:GET 幂等取资源,可缓存、可收藏、进日志;POST 改变状态、不缓存。长度上 URL 有实践上限(约 2KB~8KB),POST 受 post_max_size 限制。编码上 GET 只能 URL 编码,POST 表单支持 application/x-www-form-urlencoded、multipart/form-data(传文件必须)。
  • JSON 请求体为什么 $_POST 里没有? $_POST 只解析表单编码。前端 fetch 发 Content-Type: application/json 时,要用 json_decode(file_get_contents('php://input'), true) 手动解析——下一课 REST API 全靠它。
  • Session 和 Cookie 到底谁存数据? Cookie 本身存数据在浏览器(4KB 级、用户可见可改);Session 只在 Cookie 里存一个不记名 ID,数据在服务端。敏感信息(用户身份、权限)只能放 Session,Cookie 里放主题、语言这类可篡改无大碍的偏好。
  • 为什么每个文件都要 session_start()? Session 不会自动激活,session_start() 才会解析 Cookie、加载 $_SESSION。忘了它,$_SESSION 是空数组且改动不会保存——这是「Session 突然失效」的头号原因。
  • headers already sent by ... 怎么回事? 在 header()/session_start()/setcookie() 之前已有输出。检查该行号之前的 echo、HTML、UTF-8 BOM(保存为「无 BOM」格式)。

错法对照

错法问题正解
echo $_GET['name'] 直接输出反射型 XSS 注入点htmlspecialchars($x, ENT_QUOTES, 'UTF-8')
$_REQUEST['id'] 混取参数参数来源不可控,易被 COOKIE 注入明确用 $_GET 或 $_POST
header 跳转后不 exit后续代码继续执行header(...); exit;
把密码、手机号存进 Cookie可被用户查看与篡改敏感数据放 Session 或服务端
直接信任 $_POST['age'] 是 int实际是 string,比较出错先 filter 校验再 (int) 强转
上传文件后用 $_POST['file']文件在 $_FILES,POST 里没有读 $_FILES['file']['tmp_name'] 并 move_uploaded_file

卡点急救

  1. POST 了但 $_POST 是空的:查 Content-Type 是否 application/x-www-form-urlencoded 或 multipart/form-data;JSON 请求体必须走 php://input;再检查 post_max_size 是否被超过。
  2. Session 每次刷新都丢失:确认每个文件头部都有 session_start();浏览器没禁 Cookie;服务端 session.save_path 目录存在且可写;多机部署时 Session 已切 Redis。
  3. 中文表单提交乱码:三处统一 UTF-8——页面 <meta charset="UTF-8">、header('Content-Type: ...; charset=utf-8')、数据库连接 DSN 里 charset=utf8mb4。

面试常问

  • Cookie 和 Session 的区别与联系? Cookie 存于浏览器、随请求自动携带、容量小且可被用户篡改;Session 数据存服务端,Cookie 仅保存会话 ID。Session 通常以 Cookie 为载体建立关联;禁用 Cookie 时可用 URL 重写传递 SID。敏感状态一律放服务端。
  • 如何防止 XSS 和 CSRF? XSS:输出时按上下文转义(HTML 用 htmlspecialchars、JS 用 json_encode + JSON_HEX),配合 CSP 响应头与 Cookie 的 httponly。CSRF:表单嵌入随机 token,服务端 Session 校验;辅以 SameSite Cookie 属性和校验 Origin 头。
  • PHP 里如何接收 JSON 请求体并返回 JSON 响应? 请求体用 json_decode(file_get_contents('php://input'), true) 解析($_POST 不解析 JSON);响应前设 header('Content-Type: application/json; charset=utf-8'),echo json_encode($data, JSON_UNESCAPED_UNICODE) 输出。框架里的 Request::json() / JsonResponse 就是这两步的封装。

自测三问

  1. PRG 模式解决什么问题?三个字母分别是什么?
  2. $_FILES、php://input、$_POST 各在什么请求形态下使用?
  3. Session 数据默认存在哪里?多台 FPM 机器部署时会遇到什么问题?

小结一句话

Web 请求处理 = 超全局变量读输入 + htmlspecialchars 输出转义 + header 控制响应 + Session/Cookie 维持状态,四件套是框架封装前的原始真相。

延伸

  • PHP 官方手册:超全局变量
  • 下一课:PDO 与 MySQL——用预处理语句安全地操作数据库。
加载本节练习…
← 上一课Composer 依赖下一课 →PDO 与 MySQL