跳转至

PHP 8.1+ 知识体系

本目录面向希望系统学习现代 PHP、Web 开发与安全编码的读者。 全文以 PHP 8.1 及以上版本为基线,示例默认开启严格类型并采用安全写法。 PHP 是动态类型语言,但支持标量类型、联合类型、属性类型和静态分析。 它常由 Web 服务器为每个请求执行入口脚本,也可作为 CLI 程序运行。

使用边界

安全章节只面向本地实验、授权 CTF、代码审计和防御学习。 不得将示例用于未获授权的系统,也不要构造或传播武器化 payload。 面对真实漏洞,应保留证据、停止扩散并按组织流程负责任披露。

阅读路线

  1. 环境、语法与类型
  2. 数组、控制流与函数
  3. Web 请求、Cookie、Session 与上传
  4. 数据库与 PDO
  5. PHP 安全与 CTF 建议按顺序阅读;已有 PHP 基础的读者可从请求生命周期或 PDO 开始。 安全篇依赖前文对比较、包含、文件路径、数据库和 HTTP 状态的理解。

确认运行环境

检查版本与配置文件位置:

php --version
php --ini
php -m
本体系要求版本输出至少为 8.1。 生产环境应使用仍在官方安全支持期内的 PHP 小版本。 不要依赖教程中已经移除的 mysql_*、each() 或魔术引号等旧机制。

最小程序

<?php
declare(strict_types=1);

$name = 'PHP';
echo "Hello, {$name}!\n";
保存为 hello.php 后运行:
php hello.php
纯 PHP 文件通常省略结尾 ?>,避免其后的空白意外进入响应体。

本地内置服务器

在仅含学习代码的目录运行:

php -S 127.0.0.1:8000
浏览器访问 http://127.0.0.1:8000/。 内置服务器面向开发和调试,不具备生产服务器所需的并发、隔离和加固能力。 绑定 127.0.0.1 可避免无意向局域网暴露实验服务。

PHP 的执行模型

典型 Web 请求经历以下阶段: 1. 客户端向 Web 服务器发送 HTTP 请求。 2. Web 服务器或 PHP-FPM 将脚本、请求元数据和请求体交给 PHP。 3. PHP 初始化配置、扩展、超全局变量和会话等请求状态。 4. 入口脚本加载依赖,验证输入,执行业务逻辑并访问外部资源。 5. 程序设置状态码和响应头,再输出响应体。 6. 请求结束后执行清理;普通请求内变量不会自动跨请求保留。 PHP-FPM 工作进程可以复用,但应用不能把普通全局变量当作可靠持久存储。 跨请求状态应放入数据库、缓存或正确配置的 Session 存储。

核心心智模型

值、类型与转换

变量名以 $ 开头,值在运行时具有类型:

$count = 3;
$label = 'items';
$enabled = true;
declare(strict_types=1) 主要影响当前文件中的标量参数与返回值调用检查, 不会把 PHP 变成静态类型语言,也不会禁止所有隐式转换。

数组是有序映射

PHP 的 array 同时承担列表和字典角色:

$colors = ['red', 'blue'];
$user = ['id' => 7, 'name' => 'Lin'];
数组键只能是整数或字符串,部分看似数字的字符串键会转换为整数。 需要严格列表语义时,可用 array_is_list() 检查。

输入永远先是不可信数据

$_GET、$_POST、Cookie、请求头、上传文件和数据库旧数据都可能被操控。 安全处理不是“统一过滤”,而是按用途执行: - 先验证输入是否符合业务格式和范围。 - 数据库值通过参数化查询绑定。 - HTML 输出按 HTML 上下文转义。 - 文件名与路径通过固定根目录和规范化策略约束。 - 外部命令优先改用 PHP API;无法避免时严格固定程序和参数边界。

推荐工程工具

Composer 是 PHP 生态常用依赖管理器:

composer --version
composer install
只从可信来源安装 Composer,提交 composer.json 与应用的 composer.lock。 常见质量工具包括 PHPUnit、PHPStan 或 Psalm,以及 PHP-CS-Fixer。 它们是第三方工具,应由项目统一锁定版本和配置。

错误与异常基线

开发环境可提高错误可见性,但生产响应不能展示堆栈和路径:

display_errors=Off
log_errors=On
error_reporting=E_ALL
配置应由部署环境管理,不要在公共入口中随意覆盖生产策略。 现代 PHP 中许多引擎错误实现 Throwable,业务代码通常抛出 Exception 子类。 捕获异常时应记录必要上下文,但不得记录密码、令牌、完整 Cookie 或敏感请求体。

编码与输出

源码通常使用 UTF-8。字符串本质上是字节序列,不自动保证有效 UTF-8。 处理 Unicode 长度和截取时使用 mb_* 扩展并明确编码。 HTML 输出应指定字符集:

header('Content-Type: text/html; charset=UTF-8');
echo htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
JSON 响应可使用 JSON_THROW_ON_ERROR,让编码失败成为明确异常。

配置与秘密

数据库密码、Session 密钥和第三方令牌不应写入源码或提交 Git。 使用部署平台的秘密管理、受权限保护的环境配置或专用配置服务。 环境变量仍可能被进程、日志或错误页泄露,读取后同样要验证。 生产配置应关闭目录列表,限制上传大小,并设置合理的执行与资源上限。

最低安全要求

  • 使用仍受支持的 PHP、框架、扩展和依赖版本。
  • 所有 SQL 都使用 PDO 参数化查询,不拼接外部输入。
  • 所有输出根据 HTML、URL、JSON 等具体上下文编码。
  • Session Cookie 设置 Secure、HttpOnly 和合适的 SameSite。
  • 上传文件限制大小、类型和数量,并存放在 Web 根目录之外。
  • 文件路径采用允许列表和固定根目录,不直接包含用户提供的路径。
  • 不对不可信数据调用 unserialize(),优先使用 JSON。
  • 不把用户文本拼接进 shell 命令、代码或模板表达式。
  • 日志避免敏感数据,并为异常响应使用统一错误页。

常见误区

filter_input() 不是万能净化器;验证规则必须与业务字段语义一致。 htmlspecialchars() 防的是特定 HTML 文本/属性上下文,不防 SQL 注入。 预处理语句防 SQL 结构注入,但不能代替授权检查和数据范围验证。 隐藏报错只减少信息泄露,不会修复漏洞。 扩展名、客户端 MIME 和 Content-Type 都不能单独证明上传文件安全。 弱类型不是漏洞本身;宽松比较与错误验证组合时才常形成安全问题。

学习方法

每个示例先在本地最小目录运行,再修改一个条件观察行为。 使用 var_dump() 同时观察值与类型:

var_dump('0' == 0);
var_dump('0' === 0);
调试输出不能留在生产响应中,否则可能破坏 JSON、下载或响应头。 查询语言边界时优先查阅对应版本的 PHP 官方手册。

完成后的自检

  • CLI 与内置服务器分别适合什么场景?
  • strict_types 能保证什么,又不能保证什么?
  • PHP 数组为什么既像列表又像映射?
  • 一个 HTTP 请求如何形成超全局变量和响应?
  • Cookie 与 Session 的数据分别保存在哪里?
  • 上传文件为什么必须重新命名并移出 Web 根目录?
  • PDO 参数为什么不能代替表名或排序关键字?
  • == 与 === 的差异如何影响认证和校验?
  • 文件包含、命令执行和反序列化为什么必须有明确数据边界? 能准确回答这些问题后,再结合具体框架学习路由、中间件、模板和 ORM。