在任何 MySQL 语句中添加 PHP 变量的规则非常简单:
1. 使用准备好的语句
此规则涵盖了 99% 的查询,尤其是您的查询。任何表示 SQL 数据文字 (或者,简单地说 - SQL 字符串或数字)都必须通过准备好的语句添加。 没有例外 。常量值可以按原样放置。
该方法涉及四个基本步骤
-
在 SQL 语句中,将所有变量替换为 占位符
-
准备 结果查询
-
变量 绑定
-
执行 查询
使用所有流行的 PHP 数据库驱动程序执行此操作的方法如下:
使用添加数据文字 mysqli
从 PHP 8.2 开始,您可以在一次调用中完成整个准备/绑定/执行序列:
$type = 'testing';
$reporter = "John O'Hara";
$sql = "INSERT INTO contents (type,reporter,description) VALUES ('whatever',?,?)";
$mysqli->execute_query($sql, [$reporter, $description]);
如果你的 PHP 版本较旧,则必须明确完成准备/绑定/执行:
$type = 'testing';
$reporter = "John O'Hara";
$sql = "INSERT INTO contents (type,reporter,description) VALUES ('whatever',?,?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ss", $reporter, $description);
$stmt->execute();
代码有点复杂,但所有这些运算符的详细解释可以在我的文章“ 如何使用 Mysqli 运行 INSERT 查询” ,以及大大简化该过程的解决方案。
对于 SELECT 查询,您可以使用与上述相同的方法:
$reporter = "John O'Hara";
$result = $mysqli->execute_query("SELECT * FROM users WHERE name=?", [$reporter]);
$row = $result->fetch_assoc(); // or while (...)
但是,如果你的 PHP 版本较旧,你将需要经过准备/绑定/执行例程并添加对 get_result()
方法的调用,以便获得熟悉的 mysqli_result
方法,你可以以通常的方式从中获取数据:
$reporter = "John O'Hara";
$stmt = $mysqli->prepare("SELECT * FROM users WHERE name=?");
$stmt->bind_param("s", $reporter);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc(); // or while (...)
使用 PDO 添加数据文字
$type = 'testing';
$reporter = "John O'Hara";
$sql = "INSERT INTO contents (type,reporter,description) VALUES ('whatever',?,?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$reporter, $description]);
在 PDO 中,我们可以将绑定和执行部分合并在一起,这非常方便。PDO 还支持命名占位符,有些人认为这非常方便。
2.使用白名单过滤
任何其他查询部分(例如 SQL 关键字、表或字段名称或运算符)都必须通过 白名单 .
有时我们必须添加一个代表查询另一部分的变量,例如关键字或标识符(数据库、表或字段名称)。这种情况很少见,但最好做好准备。
脚本中 明确 值列表检查变量 《根据用户的选择在 ORDER BY 子句中添加字段名称》中 :
不幸的是,PDO 没有标识符(表和字段名称)的占位符,因此开发人员必须手动过滤掉它们。这种过滤器通常称为“白名单”(我们只列出允许的值),而不是“黑名单”(我们列出不允许的值)。
因此,我们必须明确列出 PHP 代码中所有可能的变体,然后从中进行选择。
以下是一个例子:
$orderby = $_GET['orderby'] ?: "name"; // set the default value
$allowed = ["name","price","qty"]; // the white list of allowed field names
$key = array_search($orderby, $allowed, true); // see if we have such a name
if ($key === false) {
throw new InvalidArgumentException("Invalid field name");
}
对于方向,应该使用完全相同的方法,
$direction = $_GET['direction'] ?: "ASC";
$allowed = ["ASC","DESC"];
$key = array_search($direction, $allowed, true);
if ($key === false) {
throw new InvalidArgumentException("Invalid ORDER BY direction");
}
经过这样的代码后, $direction
和 $orderby
变量都可以安全地放入 SQL 查询中,因为它们要么等于允许的变体之一,要么会抛出错误。
关于标识符最后要说的是,它们也必须根据特定的数据库语法进行格式化。对于 MySQL,它应该是 backtick
标识符周围的字符。因此,我们的 order by 示例的最终查询字符串将是
$query = "SELECT * FROM `table` ORDER BY `$orderby` $direction";