给定一个ID数组,$galleries = array(1,2,5)
我想拥有一个SQL查询,该查询使用其WHERE子句中的数组值,例如:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
如何生成此查询字符串以用于MySQL?
给定一个ID数组,$galleries = array(1,2,5)
我想拥有一个SQL查询,该查询使用其WHERE子句中的数组值,例如:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
如何生成此查询字符串以用于MySQL?
Answers:
谨防!该答案包含一个严重的SQL注入漏洞。在不确保任何外部输入都经过消毒的情况下,请勿使用此处介绍的代码示例。
$ids = join("','",$galleries);
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
$galleries
在此语句之前应输入验证的警告!预处理语句无法处理数组AFAIK,因此,如果习惯于绑定变量,则可以在此处轻松地进行SQL注入。
$galleries
具有以下值:array('1); SELECT password FROM users;/*')
。如果您不进行清理,查询将显示为SELECT * FROM galleries WHERE id IN (1); SELECT password FROM users;/*)
。将表和列名更改为数据库中的名称,然后尝试查询,并检查结果。您将找到结果密码列表,而不是画廊列表。根据数据的输出方式或脚本对一系列意外数据的处理方式,可能会将其输出到公共视图中……哎呀。
$galleries
然后使用前面提到的“ sql注入漏洞”来利用它呢?如果不能,请给我200美元。那个怎么样?
使用PDO:[1]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $pdo->prepare($select);
$statement->execute($ids);
使用MySQLi [2]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $mysqli->prepare($select);
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
$statement->execute();
$result = $statement->get_result();
说明:
IN()
运算符检查给定列表中是否存在值。一般来说,它看起来像这样:
expr IN (value,...)
我们可以构建一个表达式以放置在()
数组中。请注意,圆括号内必须至少有一个值,否则MySQL将返回错误。这等于确保我们的输入数组至少具有一个值。为了帮助防止SQL注入攻击,请首先?
为每个输入项生成一个,以创建参数化查询。在这里,我假设包含您的ID的数组称为$ids
:
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
给定一个包含三个项目的输入数组,$select
如下所示:
SELECT *
FROM galleries
WHERE id IN (?, ?, ?)
再次注意,?
输入数组中的每个项目都有一个。然后,我们将使用PDO或MySQLi来准备和执行上述查询。
IN()
字符串使用运算符由于绑定了参数,因此很容易在字符串和整数之间进行更改。对于PDO,无需更改;对于MySQLi,更改str_repeat('i',
为str_repeat('s',
是否需要检查字符串。
[1]:为简洁起见,我省略了一些错误检查。您需要检查每种数据库方法的常见错误(或将数据库驱动程序设置为引发异常)。
[2]:需要PHP 5.6或更高版本。同样,为了简洁起见,我省略了一些错误检查。
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
的...
是ID,则将ID从数组扩展为多个参数。如果您所指的是,expr IN (value,...)
那意味着可以有更多的值,例如WHERE id IN (1, 3, 4)
。至少需要有一个。
...
:wiki.php.net/rfc/argument_unpacking
采用:
select id from galleries where id in (1, 2, 5);
一个简单的for each
循环将起作用。
Flavius / AvatarKava的方法更好,但是请确保所有数组值都不包含逗号。
作为Flavius Stef的答案,您可以intval()
用来确保所有id
都是int值:
$ids = join(',', array_map('intval', $galleries));
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
对于具有转义功能的MySQLi:
$ids = array_map(function($a) use($mysqli) {
return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a;
}, $ids);
$ids = join(',', $ids);
$result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)");
对于具有准备好的语句的PDO:
$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
我们应该注意SQL注入漏洞和空条件。我将按以下方式处理这两个问题。
对于纯数字数组,使用适当类型的转换即intval
或floatval
或doubleval
在每个元件。mysqli_real_escape_string()
如果需要,还可以将字符串类型应用于数字值。MySQL允许数字和日期变体作为字符串。
要在传递给查询之前适当地转义值,请创建类似于以下内容的函数:
function escape($string)
{
// Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
return mysqli_real_escape_string($db, $string);
}
这样的功能很可能已经在您的应用程序中提供给您,或者您可能已经创建了。
像这样清理字符串数组:
$values = array_map('escape', $gallaries);
可以使用intval
或floatval
或doubleval
适当地清除数字数组:
$values = array_map('intval', $gallaries);
然后最终建立查询条件
$where = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;
要么
$where = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;
由于有时数组也可以为空,$galleries = array();
因此我们应该注意,IN ()
不允许有空列表。也可以使用它OR
,但是问题仍然存在。因此,上述检查,count($values)
是为了确保相同。
并将其添加到最终查询中:
$query = 'SELECT * FROM `galleries` WHERE ' . $where;
提示:如果要在空数组的情况下显示所有记录(不过滤),而不是隐藏所有行,只需在三元组的假部分将0替换为1。
$query = 'SELECT * FROM galleries WHERE ' . (count($gallaries) ? "id IN ('" . implode("', '", array_map('escape', $gallaries)) . "')" : 0);
如果我们正确过滤输入数组,则可以使用此“ WHERE id IN”子句。像这样:
$galleries = array();
foreach ($_REQUEST['gallery_id'] as $key => $val) {
$galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}
像下面的例子:
$galleryIds = implode(',', $galleries);
即现在您应该安全使用 $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
你可能有桌子texts
(T_ID (int), T_TEXT (text))
和桌子test
(id (int), var (varchar(255)))
在insert into test values (1, '1,2,3') ;
从表中的文本下面将输出行,其中T_ID IN (1,2,3)
:
SELECT * FROM `texts` WHERE (SELECT FIND_IN_SET( T_ID, ( SELECT var FROM test WHERE id =1 ) ) AS tm) >0
这样,您可以管理简单的n2m数据库关系,而无需使用额外的表,而仅使用SQL,而无需使用PHP或其他某种编程语言。
更多示例:
$galleryIds = [1, '2', 'Vitruvian Man'];
$ids = array_filter($galleryIds, function($n){return (is_numeric($n));});
$ids = implode(', ', $ids);
$sql = "SELECT * FROM galleries WHERE id IN ({$ids})";
// output: 'SELECT * FROM galleries WHERE id IN (1, 2)'
$statement = $pdo->prepare($sql);
$statement->execute();
除了使用IN查询外,您还有两种选择,因为在IN查询中存在SQL注入漏洞的风险。您可以使用循环获取所需的确切数据,也可以将查询与OR案例一起使用
1. SELECT *
FROM galleries WHERE id=1 or id=2 or id=5;
2. $ids = array(1, 2, 5);
foreach ($ids as $id) {
$data[] = SELECT *
FROM galleries WHERE id= $id;
}
因为原始问题与数字数组有关,并且我使用的是字符串数组,所以无法使给定的示例起作用。
我发现每个字符串都需要封装在单引号中才能使用该IN()
功能。
这是我的解决方案
foreach($status as $status_a) {
$status_sql[] = '\''.$status_a.'\'';
}
$status = implode(',',$status_sql);
$sql = mysql_query("SELECT * FROM table WHERE id IN ($status)");
如您所见,第一个函数将每个数组变量包装在其中single quotes (\')
,然后内嵌到数组中。
注意:$status
SQL语句中没有单引号。
可能有一种更好的添加引号的方法,但这可行。
$filter = "'" . implode("','",$status) . "'";
'
在字符串里面?SQL注入易受攻击。使用PDO :: quote或mysqli_real_escape_string。
下面是我使用的方法,将PDO与命名占位符一起用于其他数据。为了克服SQL注入,我正在过滤数组以仅接受整数值,并拒绝所有其他值。
$owner_id = 123;
$galleries = array(1,2,5,'abc');
$good_galleries = array_filter($chapter_arr, 'is_numeric');
$sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
"OWNER_ID" => $owner_id,
));
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
防止SQL注入的基本方法是:
使用准备好的语句和参数化查询是更好的做法,但是如果选择转义字符方法,则可以尝试下面的示例。
您可以通过在的array_map
每个元素上添加单引号来生成查询$galleries
:
$galleries = array(1,2,5);
$galleries_str = implode(', ',
array_map(function(&$item){
return "'" .mysql_real_escape_string($item) . "'";
}, $galleries));
$sql = "SELECT * FROM gallery WHERE id IN (" . $galleries_str . ");";
生成的$ sql var将为:
SELECT * FROM gallery WHERE id IN ('1', '2', '5');
注意:mysql_real_escape_string 如其文档所述,在PHP 5.5.0中已弃用,在PHP 7.0.0中已删除。相反,应使用MySQLi或PDO_MySQL扩展。另请参见MySQL:选择API指南和相关的FAQ,以获取更多信息。此功能的替代方法包括:
mysqli_real_escape_string()
PDO :: quote()