使用PHP7+PDO+SQLite,我正在寻找一种方法,使用用户定义的过滤器,以前台生成的数组的形式过滤表中的条目。为了清楚起见,这里有一个例子来说明我在寻找什么。
这个例子:
数据库中的表。
ID Firstname Lastname Age
+--------------+--------------+--------------+-------------+
| 0 | John | Doe | 21 |
| 1 | John | Smith | 35 |
| 2 | Alice | Smith | 35 |
| 3 | Bob | Smith | 40 |
+--------------+--------------+--------------+-------------+
过滤器。
/*
Filters follow the structure:
[
"Table Column Name 1" => [Match1 OR Match2 OR ...],
AND
"TCN 2" ...
]
Any column name not provided should match any value.
*/
[
"Firstname" => ["John"]
]
// ^ fetchAll() returns rows with ID 0 and 1
[
"Firstname" => ["John", "Alice"],
"Lastname" => ["Smith"]
]
// ^ ID 1 and 2 (but not 3)
我需要找到一个函数,可以将上面的数组转换为一个SQL查询,以获取所有符合过滤器的行。我想使用SQL(我没有太多经验)来完成这个任务,而不是为了性能而使用PHP来获取所有的行并进行过滤。
我知道我可以做这样的事情。
// Untested - for illustration purposes only
$filter = [
"Firstname" => ["John", "Alice"],
"Lastname" => ["Smith"]
];
$sql = "SELECT * FROM table_name WHERE ";
foreach ($filter as $column => $matching_values) {
foreach ($matching_values as $match) {
$sql .= $column . " == " . $match . " OR ";
}
// Ugly way of removing trailing ` OR `
$sql = substr($sql, 0, -4);
$sql .= " AND ";
}
// Ugly way of removing trailing ` AND `
$sql = substr($sql, 0, -5);
echo $sql;
但这会引入一个巨大的SQL注入安全漏洞。我想知道是否有一种简单而又高效安全的方法来实现这个目的。如果需要的话,还可以改变过滤器的格式,比如改成RegEx(如果有人能把上面例子中最里面的数组用RegEx代替,那就加分)。
另外(或者另外),用一种简单的方式用搜索的方式来描述我的问题可能会有帮助,因为我无法对这个问题做太多的研究,因为我无法很好的措辞。
这将是一个有趣的组合,我已经在我的网站上教授了几个技术,即
最后,它将是这样的
$allowed = ["Firstname", "Lastname"];
$conditions = [];
$parameters = [];
foreach ($filter as $key => $values) {
if (array_search($key, $allowed, true) === false) {
throw new InvalidArgumentException("invalid field name!");
}
$conditions[] = "`$key` in (".str_repeat('?,', count($values) - 1) . '?'.")";
$parameters = array_merge($parameters, $values);
}
$sql = "SELECT * FROM table_name ";
if ($conditions)
{
$sql .= " WHERE ".implode(" AND ", $conditions);
}
会产生一个你要找的查询。
SELECT * FROM table_name WHERE WHERE `Firstname` in (?,?) AND `Lastname` in (?)
以及一个数组的值,用于在 PDO::execute()
做出了一个防弹解决方案,其中的值通过参数来保护,而文件名则通过对白名单的过滤来保护。
我唯一怀疑的是,问题中出现的情况可能过于简单。一旦你不仅需要精确匹配,还需要部分匹配或更大程度的对比,代码的复杂度就会急剧上升。