php数据库输入校验核心是全程隔离数据与指令,必须用预处理语句(pdo/mysqli)参数化所有外部输入,禁用拼接与过时转义;语义校验需按业务规则严格验证类型、格式与范围,并对标识符采用白名单控制,同时配合数据库层约束实现纵深防御。

PHP 中数据库输入校验的核心不是“先校验再插入”,而是“不信任任何外部输入,全程隔离数据与指令”。关键在于用预处理语句(Prepared Statements)阻断 SQL 注入,辅以语义校验和类型约束,而非依赖正则或字符串过滤。
用 PDO 或 MySQLi 预处理语句代替拼接
这是防御 SQL 注入的底线。无论输入看起来多“安全”,只要进 SQL,就必须参数化。
- PDO 示例:$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->execute([$name, $email]); - MySQLi 示例:$stmt = $mysqli->prepare("SELECT * FROM posts WHERE status = ? AND category_id = ?");
$stmt->bind_param("si", $status, $catId);
$stmt->execute(); - 禁止使用
mysql_real_escape_string(已废弃)或手动加引号、转义——它无法覆盖所有上下文(如数字字段、ORDER BY、表名等)
校验应在绑定前完成,且按业务语义设计
预处理解决语法注入,但不防止逻辑错误或脏数据。校验目标是“这个值是否符合当前字段的业务含义”。
- 邮箱:用
filter_var($email, FILTER_VALIDATE_EMAIL),再结合长度(≤254 字符)、格式(不含空格/控制字符)检查 - 手机号:明确国家码规则(如中国 +86 开头、11 位纯数字),不用模糊正则
/^1[3-9]d{9}$/而忽略国际场景 - 整数 ID 或状态码:用
is_int()或filter_var($val, FILTER_VALIDATE_INT, ['min_range' => 0, 'max_range' => 3]),拒绝字符串 "0" 或浮点 "1.0" - 用户名:限制长度(如 3–20 字符)、允许字母数字下划线、禁用保留词(admin、null、select 等)
对动态结构(表名、字段名、排序方向)绝不参数化,必须白名单硬编码
预处理语句只支持数据占位符(? 或 :name),不支持标识符(表名、列名、ORDER BY 子句)。强行拼接极易被绕过。
立即学习“PHP免费学习笔记(深入)”;
- 错误做法:
"SELECT * FROM {$table} ORDER BY {$column} {$direction}" - 正确做法:定义允许值数组,严格比对
$allowedTables = ['users', 'posts', 'comments'];
if (!in_array($table, $allowedTables, true)) { throw new InvalidArgumentException('Invalid table'); } - 排序字段同理:
$allowedSorts = ['created_at' => 'created_at', 'title' => 'title'];<br>$orderBy = $allowedSorts[$inputSort] ?? 'created_at';
配合数据库层约束,形成纵深防御
PHP 校验是第一道门,但不能替代数据库自身约束。两者缺一不可。
- 设 NOT NULL、UNIQUE、CHECK(如 MySQL 8.0+ 支持
CHECK (email REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')) - 用 ENUM 或 TINYINT 表示有限状态(如 status ENUM('draft','published','archived')),避免 PHP 层传入非法字符串
- 设置字段长度(VARCHAR(255))、数值范围(TINYINT UNSIGNED),让数据库在写入时自动截断或报错
- 开启 PDO 的异常模式:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);,便于捕获约束违规











