1 PHP 与 Web 服务器
PHP 天生为 Web 而生。与 Node.js 或 Python 不同,PHP 不需要你手动创建 HTTP 服务器——Web 服务器(Apache/Nginx)负责接收请求,然后将其交给 PHP 处理。
两种主流部署模式
Apache + mod_php
PHP 作为 Apache 模块直接运行,配置简单,适合开发环境。
# Apache 配置示例
LoadModule php_module modules/libphp.so
AddHandler php-script .php
# .htaccess 路由重写
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
Nginx + PHP-FPM(推荐生产环境)
Nginx 处理静态文件,动态请求转发给 PHP-FPM 进程池,性能更优。
# Nginx 配置示例
server {
listen 80;
root /var/www/html;
index index.php;
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
请求生命周期
# PHP 请求生命周期(每次请求都是独立的!)
客户端请求 → Web 服务器 → PHP 进程启动 → 执行脚本 → 返回响应 → 进程结束
# 这意味着:
# 1. 每次请求都是全新的环境(无状态)
# 2. 变量不会在请求间保留
# 3. 需要 Session/数据库来持久化数据
超全局变量(Superglobals)
PHP 自动将请求信息注入到预定义的超全局变量中,无需任何导入或初始化:
<?php
// $_SERVER - 服务器和请求环境信息
echo $_SERVER['REQUEST_METHOD']; // GET, POST, PUT, DELETE...
echo $_SERVER['REQUEST_URI']; // /api/users?page=1
echo $_SERVER['HTTP_HOST']; // www.example.com
echo $_SERVER['REMOTE_ADDR']; // 客户端 IP
echo $_SERVER['HTTP_USER_AGENT']; // 浏览器信息
echo $_SERVER['CONTENT_TYPE']; // 请求的 Content-Type
// $_GET - URL 查询参数
// URL: /search?q=php&page=2
echo $_GET['q']; // "php"
echo $_GET['page']; // "2"
// $_POST - POST 表单数据(Content-Type: application/x-www-form-urlencoded)
echo $_POST['username'];
echo $_POST['password'];
// $_REQUEST - 合并 $_GET + $_POST + $_COOKIE(不推荐,来源不明确)
echo $_REQUEST['key'];
// $_FILES - 上传的文件信息
$file = $_FILES['avatar'];
echo $file['name']; // 原始文件名
echo $file['tmp_name']; // 服务器临时路径
echo $file['size']; // 文件大小(字节)
echo $file['type']; // MIME 类型
echo $file['error']; // 错误码,0 表示成功
🔄 对比其他语言
PHP 的 Web 模型是「被动响应」——你只需写处理逻辑,服务器会调用你的代码。其他语言则需要你主动创建服务器:
// PHP:直接写逻辑,放到 Web 目录即可
// index.php
$name = $_GET['name'] ?? 'World';
echo "Hello, $name!";
# Node.js:必须手动创建 HTTP 服务器
# const http = require('http');
# http.createServer((req, res) => {
# res.end('Hello!');
# }).listen(3000);
# Python Flask:也需要显式创建应用
# from flask import Flask, request
# app = Flask(__name__)
# @app.route('/')
# def hello():
# return f"Hello, {request.args.get('name', 'World')}!"
优势:PHP 部署极简,创建 .php 文件放入 Web 目录即可访问,零配置启动。
劣势:每次请求都重新初始化,不适合需要长连接(如 WebSocket)的场景。
内置开发服务器
# 快速启动开发服务器(仅用于开发!)
php -S localhost:8080
# 指定文档根目录
php -S localhost:8080 -t public/
# 使用路由脚本
php -S localhost:8080 router.php
2 处理 GET/POST 请求
读取请求参数
<?php
// 安全地读取 GET 参数(避免 undefined index 警告)
$page = $_GET['page'] ?? 1;
$keyword = $_GET['q'] ?? '';
// 安全地读取 POST 参数
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
// 检查参数是否存在
if (isset($_GET['id'])) {
$id = (int)$_GET['id'];
}
// 使用 empty() 检查是否为空
if (!empty($_POST['username'])) {
$username = $_POST['username'];
}
输入过滤与验证
永远不要信任用户输入。PHP 提供了 filter_input() 和 filter_var() 来过滤和验证数据:
<?php
// filter_input() - 从指定来源获取并过滤
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
// 返回 int 值或 false(验证失败)或 null(参数不存在)
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$url = filter_input(INPUT_GET, 'url', FILTER_VALIDATE_URL);
// 带选项的过滤
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, [
'options' => ['min_range' => 1, 'max_range' => 150]
]);
// filter_var() - 过滤任意变量
$email = filter_var($rawEmail, FILTER_VALIDATE_EMAIL);
$ip = filter_var($rawIp, FILTER_VALIDATE_IP);
// 净化(Sanitize)而非验证——移除非法字符
$cleanEmail = filter_var($input, FILTER_SANITIZE_EMAIL);
$cleanString = filter_var($input, FILTER_SANITIZE_SPECIAL_CHARS);
$cleanInt = filter_var($input, FILTER_SANITIZE_NUMBER_INT);
// 批量过滤
$filters = [
'username' => FILTER_SANITIZE_SPECIAL_CHARS,
'email' => FILTER_VALIDATE_EMAIL,
'age' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0]],
];
$result = filter_input_array(INPUT_POST, $filters);
XSS 防御:htmlspecialchars()
<?php
// 输出到 HTML 时必须转义,防止 XSS 攻击
$userInput = '<script>alert("XSS")</script>';
// 错误:直接输出(会执行恶意脚本)
echo $userInput;
// 正确:转义后输出
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// 输出: <script>alert("XSS")</script>
// 封装一个简便函数
function e(string $str): string {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
echo '<p>Welcome, ' . e($username) . '</p>';
完整的表单处理示例
<?php
// contact.php - 同时处理表单显示和提交
$errors = [];
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 验证姓名
$name = trim($_POST['name'] ?? '');
if (empty($name)) {
$errors['name'] = '姓名不能为空';
} elseif (mb_strlen($name) > 50) {
$errors['name'] = '姓名不能超过 50 个字符';
}
// 验证邮箱
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if ($email === false || $email === null) {
$errors['email'] = '请输入有效的邮箱地址';
}
// 验证消息
$message = trim($_POST['message'] ?? '');
if (empty($message)) {
$errors['message'] = '消息不能为空';
}
// 无错误则处理提交
if (empty($errors)) {
// 保存到数据库或发送邮件...
$success = true;
}
}
?>
<!DOCTYPE html>
<html>
<body>
<?php if ($success): ?>
<div class="success">提交成功!感谢您的反馈。</div>
<?php else: ?>
<form method="POST" action="">
<div>
<label>姓名</label>
<input type="text" name="name"
value="<?= htmlspecialchars($name ?? '', ENT_QUOTES, 'UTF-8') ?>">
<?php if (isset($errors['name'])): ?>
<span class="error"><?= $errors['name'] ?></span>
<?php endif; ?>
</div>
<div>
<label>邮箱</label>
<input type="email" name="email"
value="<?= htmlspecialchars($_POST['email'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
<?php if (isset($errors['email'])): ?>
<span class="error"><?= $errors['email'] ?></span>
<?php endif; ?>
</div>
<div>
<label>消息</label>
<textarea name="message"><?= htmlspecialchars($message ?? '', ENT_QUOTES, 'UTF-8') ?></textarea>
<?php if (isset($errors['message'])): ?>
<span class="error"><?= $errors['message'] ?></span>
<?php endif; ?>
</div>
<button type="submit">提交</button>
</form>
<?php endif; ?>
</body>
</html>
3 构建 JSON API
核心要点:现代 PHP 开发中,大量场景是构建 JSON API 而非传统的服务端渲染页面。PHP 处理 JSON 非常简洁——json_encode() / json_decode() 是内置函数,无需额外库。
JSON 编解码基础
<?php
// PHP 数组/对象 → JSON 字符串
$data = ['name' => '张三', 'age' => 30, 'skills' => ['PHP', 'MySQL']];
$json = json_encode($data);
// {"name":"张三","age":30,"skills":["PHP","MySQL"]}
// 美化输出 + 不转义 Unicode
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// JSON 字符串 → PHP 关联数组
$decoded = json_decode($json, true); // true 返回数组,false/省略返回 stdClass 对象
echo $decoded['name']; // 张三
// 错误处理(PHP 8.4)
$result = json_decode('invalid json', true);
if (json_last_error() !== JSON_ERROR_NONE) {
echo json_last_error_msg(); // "Syntax error"
}
// 或使用 JSON_THROW_ON_ERROR 标志抛出异常
try {
$data = json_decode('invalid', true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo $e->getMessage();
}
读取原始请求体
<?php
// 当客户端发送 JSON(Content-Type: application/json)时,
// $_POST 不会包含数据,需要手动读取原始输入
$rawBody = file_get_contents('php://input');
$data = json_decode($rawBody, true);
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => '无效的 JSON 数据']);
exit;
}
// 现在可以使用 $data['key'] 访问数据
$username = $data['username'] ?? null;
完整 REST API 示例:用户 CRUD
<?php
// api/users.php - 简单的用户 REST API
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
// 模拟数据库连接(实际中使用 PDO)
function getDb(): PDO {
$pdo = new PDO('mysql:host=localhost;dbname=myapp;charset=utf8mb4', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
// 封装 JSON 响应
function jsonResponse(mixed $data, int $code = 200): never {
http_response_code($code);
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
$method = $_SERVER['REQUEST_METHOD'];
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
try {
$db = getDb();
match ($method) {
'GET' => handleGet($db, $id),
'POST' => handlePost($db),
'PUT' => handlePut($db, $id),
'DELETE' => handleDelete($db, $id),
default => jsonResponse(['error' => '不支持的方法'], 405),
};
} catch (PDOException $e) {
jsonResponse(['error' => '数据库错误'], 500);
}
function handleGet(PDO $db, ?int $id): never {
if ($id) {
$stmt = $db->prepare('SELECT id, name, email FROM users WHERE id = ?');
$stmt->execute([$id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
$user ? jsonResponse($user) : jsonResponse(['error' => '用户不存在'], 404);
}
// 列表(带分页)
$page = max(1, (int)($_GET['page'] ?? 1));
$limit = 20;
$offset = ($page - 1) * $limit;
$stmt = $db->prepare('SELECT id, name, email FROM users LIMIT ? OFFSET ?');
$stmt->execute([$limit, $offset]);
jsonResponse([
'data' => $stmt->fetchAll(PDO::FETCH_ASSOC),
'page' => $page,
]);
}
function handlePost(PDO $db): never {
$input = json_decode(file_get_contents('php://input'), true);
if (!$input || empty($input['name']) || empty($input['email'])) {
jsonResponse(['error' => '缺少必填字段 name, email'], 422);
}
$stmt = $db->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->execute([$input['name'], $input['email']]);
jsonResponse(['id' => (int)$db->lastInsertId(), 'message' => '创建成功'], 201);
}
function handlePut(PDO $db, ?int $id): never {
if (!$id) jsonResponse(['error' => '缺少用户 ID'], 400);
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $db->prepare('UPDATE users SET name = ?, email = ? WHERE id = ?');
$stmt->execute([$input['name'], $input['email'], $id]);
jsonResponse(['message' => '更新成功']);
}
function handleDelete(PDO $db, ?int $id): never {
if (!$id) jsonResponse(['error' => '缺少用户 ID'], 400);
$stmt = $db->prepare('DELETE FROM users WHERE id = ?');
$stmt->execute([$id]);
$stmt->rowCount() > 0
? jsonResponse(['message' => '删除成功'])
: jsonResponse(['error' => '用户不存在'], 404);
}
调用示例:
# 获取用户列表
curl http://localhost:8080/api/users.php
# 获取单个用户
curl http://localhost:8080/api/users.php?id=1
# 创建用户
curl -X POST http://localhost:8080/api/users.php \
-H "Content-Type: application/json" \
-d '{"name":"李四","email":"lisi@example.com"}'
# 更新用户
curl -X PUT "http://localhost:8080/api/users.php?id=1" \
-H "Content-Type: application/json" \
-d '{"name":"张三丰","email":"zsf@example.com"}'
# 删除用户
curl -X DELETE "http://localhost:8080/api/users.php?id=1"
4 cURL 发送 HTTP 请求
PHP 的 cURL 扩展是发送 HTTP 请求的标准方式,功能强大,支持各种协议和选项。
GET 请求
<?php
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.example.com/users?page=1',
CURLOPT_RETURNTRANSFER => true, // 返回响应内容而非直接输出
CURLOPT_TIMEOUT => 10, // 超时秒数
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer your-token-here',
],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
echo "cURL 错误: $error";
} elseif ($httpCode === 200) {
$data = json_decode($response, true);
print_r($data);
} else {
echo "HTTP 错误: $httpCode";
}
POST 请求(发送 JSON)
<?php
$postData = json_encode([
'name' => '王五',
'email' => 'wangwu@example.com',
]);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.example.com/users',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Content-Length: ' . strlen($postData),
],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 201) {
echo '创建成功: ' . $response;
}
POST 表单数据 & 文件上传
<?php
// 发送表单数据(application/x-www-form-urlencoded)
$ch = curl_init('https://api.example.com/login');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'username' => 'admin',
'password' => 'secret',
]),
]);
$response = curl_exec($ch);
curl_close($ch);
// 上传文件(multipart/form-data)
$ch = curl_init('https://api.example.com/upload');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('/path/to/photo.jpg', 'image/jpeg', 'photo.jpg'),
'description' => '个人照片',
],
]);
$response = curl_exec($ch);
curl_close($ch);
封装 HTTP 客户端函数
<?php
function httpRequest(
string $url,
string $method = 'GET',
?array $data = null,
array $headers = [],
int $timeout = 10,
): array {
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_FOLLOWLOCATION => true, // 自动跟随重定向
CURLOPT_MAXREDIRS => 5,
];
if ($method === 'POST') {
$options[CURLOPT_POST] = true;
} elseif ($method !== 'GET') {
$options[CURLOPT_CUSTOMREQUEST] = $method;
}
if ($data !== null) {
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
$options[CURLOPT_POSTFIELDS] = $json;
$headers[] = 'Content-Type: application/json';
$headers[] = 'Content-Length: ' . strlen($json);
}
if (!empty($headers)) {
$options[CURLOPT_HTTPHEADER] = $headers;
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'status' => $info['http_code'],
'body' => $response,
'json' => json_decode($response, true),
'error' => $error ?: null,
'time' => $info['total_time'],
];
}
// 使用示例
$result = httpRequest('https://api.example.com/users');
if ($result['status'] === 200) {
foreach ($result['json'] as $user) {
echo "{$user['name']} - {$user['email']}\n";
}
}
$result = httpRequest(
'https://api.example.com/users',
'POST',
['name' => '赵六', 'email' => 'zhaoliu@example.com'],
['Authorization: Bearer token123'],
);
echo "状态码: {$result['status']}, 耗时: {$result['time']}s\n";
5 file_get_contents 简易请求
对于简单的 HTTP 请求,file_get_contents() 提供了最简洁的方式,无需 cURL 扩展。
简单 GET 请求
<?php
// 一行代码发起 GET 请求(需要 php.ini 中 allow_url_fopen = On)
$response = file_get_contents('https://api.example.com/users');
$users = json_decode($response, true);
// 错误处理
$response = @file_get_contents('https://api.example.com/data');
if ($response === false) {
echo "请求失败";
}
// 获取响应头
$response = file_get_contents('https://api.example.com/data');
// 请求后 $http_response_header 自动可用
print_r($http_response_header);
// ["HTTP/1.1 200 OK", "Content-Type: application/json", ...]
使用 Stream Context 发送 POST
<?php
$data = json_encode(['name' => '钱七', 'email' => 'qianqi@example.com']);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => implode("\r\n", [
'Content-Type: application/json',
'Authorization: Bearer token123',
'Content-Length: ' . strlen($data),
]),
'content' => $data,
'timeout' => 10,
],
]);
$response = file_get_contents('https://api.example.com/users', false, $context);
$result = json_decode($response, true);
file_get_contents vs cURL
| 特性 | file_get_contents | cURL |
|---|---|---|
| 简洁性 | ⭐ 极简,一行代码 | 需要多行配置 |
| 功能完整性 | 基础 GET/POST | ⭐ 完整 HTTP 支持 |
| 错误处理 | 有限(需 @抑制) | ⭐ 详细错误信息 |
| 文件上传 | ✘ 不支持 | ⭐ CURLFile |
| Cookie 管理 | ✘ 不支持 | ⭐ CURLOPT_COOKIEJAR |
| 适用场景 | 快速取数据、脚本 | 生产 API 调用 |
建议:简单场景用 file_get_contents,生产环境推荐 cURL 或更高层的 Guzzle 库。
6 Session 与 Cookie
HTTP 是无状态协议,PHP 通过 Session 和 Cookie 实现状态保持。Session 数据存储在服务器端,Cookie 存储在客户端。
Cookie 操作
<?php
// 设置 Cookie(必须在任何输出之前调用)
setcookie('theme', 'dark', [
'expires' => time() + 86400 * 30, // 30 天后过期
'path' => '/', // 整个站点有效
'domain' => '.example.com', // 含子域名
'secure' => true, // 仅 HTTPS
'httponly' => true, // JS 不可访问(防 XSS)
'samesite' => 'Lax', // CSRF 防护
]);
// 读取 Cookie
$theme = $_COOKIE['theme'] ?? 'light';
// 删除 Cookie(设置过期时间为过去)
setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']);
Session 基础
<?php
// 启动 Session(必须在任何输出之前调用)
session_start();
// 写入 Session 数据
$_SESSION['user_id'] = 42;
$_SESSION['username'] = '张三';
$_SESSION['role'] = 'admin';
// 读取 Session
echo $_SESSION['username']; // 张三
// 检查 Session 值是否存在
if (isset($_SESSION['user_id'])) {
echo '已登录';
}
// 删除特定 Session 值
unset($_SESSION['role']);
// 销毁整个 Session(登出时使用)
session_unset(); // 清除所有 Session 变量
session_destroy(); // 销毁 Session 文件
// 同时清除 Session Cookie
setcookie(session_name(), '', ['expires' => time() - 3600, 'path' => '/']);
Session 工作原理
# Session 生命周期:
1. session_start() → PHP 生成唯一 Session ID(如 abc123def456)
2. 通过 Cookie(PHPSESSID=abc123def456)发送给客户端
3. 数据序列化存储到服务器(默认:/tmp/sess_abc123def456)
4. 下次请求时,PHP 根据 Cookie 中的 ID 找到对应文件
5. 反序列化恢复 $_SESSION 数据
# 默认存储路径
# Linux: /tmp/
# 可通过 session.save_path 配置
# 查看当前 Session 配置
# php -i | grep session
Session 安全配置
<?php
// 推荐的 Session 安全配置(在 session_start() 之前)
ini_set('session.cookie_httponly', '1'); // 禁止 JS 访问
ini_set('session.cookie_secure', '1'); // 仅 HTTPS
ini_set('session.cookie_samesite', 'Lax');
ini_set('session.use_strict_mode', '1'); // 拒绝未初始化的 Session ID
// 重新生成 Session ID(防止 Session 固定攻击)
session_regenerate_id(true); // true 表示删除旧 Session 文件
完整登录流程示例
<?php
// login.php - 登录处理
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
// 从数据库查询用户(使用预处理语句)
$pdo = new PDO('mysql:host=localhost;dbname=myapp;charset=utf8mb4', 'root', '');
$stmt = $pdo->prepare('SELECT id, username, password_hash FROM users WHERE username = ?');
$stmt->execute([$username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// 验证密码(password_hash + password_verify)
if ($user && password_verify($password, $user['password_hash'])) {
// 登录成功:重新生成 Session ID 防止固定攻击
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['login_time'] = time();
header('Location: /dashboard.php');
exit;
}
$error = '用户名或密码错误';
}
// auth_check.php - 鉴权中间件(其他页面引入)
function requireLogin(): void {
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: /login.php');
exit;
}
// 检查 Session 是否过期(30 分钟无操作)
if (isset($_SESSION['login_time']) && time() - $_SESSION['login_time'] > 1800) {
session_unset();
session_destroy();
header('Location: /login.php?expired=1');
exit;
}
// 刷新活跃时间
$_SESSION['login_time'] = time();
}
// logout.php - 登出
session_start();
session_unset();
session_destroy();
setcookie(session_name(), '', ['expires' => time() - 3600, 'path' => '/']);
header('Location: /login.php');
exit;
7 HTTP 响应控制
设置状态码
<?php
// 使用 http_response_code()(推荐)
http_response_code(200); // OK
http_response_code(201); // Created
http_response_code(301); // Moved Permanently
http_response_code(400); // Bad Request
http_response_code(401); // Unauthorized
http_response_code(403); // Forbidden
http_response_code(404); // Not Found
http_response_code(500); // Internal Server Error
// 获取当前状态码
$code = http_response_code();
重定向
<?php
// 302 临时重定向(默认)
header('Location: /dashboard.php');
exit; // 重定向后必须 exit!
// 301 永久重定向
header('Location: https://new.example.com/page', true, 301);
exit;
// 使用 http_response_code + header
http_response_code(301);
header('Location: /new-url');
exit;
// PRG 模式(Post/Redirect/Get)——防止表单重复提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 处理表单...
header('Location: /success.php');
exit;
}
Content-Type 响应头
<?php
// JSON
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['status' => 'ok']);
// HTML(默认,通常不需要显式设置)
header('Content-Type: text/html; charset=utf-8');
// 纯文本
header('Content-Type: text/plain; charset=utf-8');
// XML
header('Content-Type: application/xml; charset=utf-8');
// 文件下载
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="report.csv"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit;
缓存控制与常用响应头
<?php
// 禁止缓存(API 响应)
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
// 允许缓存(静态资源)
header('Cache-Control: public, max-age=86400'); // 缓存 1 天
header('ETag: "' . md5_file($filePath) . '"');
// CORS 跨域设置
header('Access-Control-Allow-Origin: https://frontend.example.com');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
// 安全相关头
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
注意:所有 header() 调用必须在任何输出之前执行(包括空格和 BOM)。如果已有输出,header() 会失败并产生 Warning。使用 headers_sent() 可以检查是否已发送头信息。
8 本章要点
🏗️ 请求模型
- • PHP 天然集成 Web 服务器
- • 每次请求独立进程,无共享状态
- •
$_GET/$_POST/$_SERVER超全局变量
🛡️ 输入安全
- •
filter_input()过滤验证 - •
htmlspecialchars()防 XSS - • 永远不信任用户输入
📡 JSON API
- •
json_encode()/json_decode() - •
php://input读取原始请求体 - • 设置正确的 Content-Type
🔗 HTTP 客户端
- • cURL:功能完整,生产首选
- •
file_get_contents:简易请求 - • 生产环境推荐 Guzzle 库
🔐 会话管理
- • Session 服务端存储,Cookie 客户端
- •
session_regenerate_id()防固定攻击 - • 安全属性:httponly, secure, samesite
📤 响应控制
- •
http_response_code()设置状态码 - •
header('Location: ...')重定向 - • 缓存、CORS、安全头设置
💡 最佳实践速查
✓ 始终使用 filter_input() 过滤用户输入
✓ 输出 HTML 时用 htmlspecialchars()
✓ 登录后调用 session_regenerate_id(true)
✓ Cookie 设置 httponly + secure + samesite
✗ 不要直接输出 $_GET/$_POST 的值
✗ 不要使用 $_REQUEST(来源不明确)
✗ 不要忘记 header() 后的 exit
✗ 不要在输出后调用 header()