[英]Codeigniter issue with escaping values when passing array to query with “in” in the where clause
我在 codeigniter 项目的 model 中有下面的 function,变量$id
是一个数组,例如,包含(1,2,3)
。 现在我正在重新审视它,我认为我实际上并不是 escaping 我的数组$id
。 我想我必须将行$this->db->escape($id)
更改$id = $this->db->escape($id)
如果我这样做,那么它会在数组中的每个元素周围加上单引号,并将其视为一个长字符串,如下所示: '(1,2,3)'
。
有人可以确认我实际上不是 escaping 我的变量并建议解决方案或让我知道这是否是 codeigniter 框架中的错误?
function get_ratings($id)
{
$this->db->escape($id); // had to manually escape the variable since it's being used in an "in" in the where clause.
$sql = "select * from toys t
where t.toy_id in ($id)";
$query = $this->db->query($sql, $id);
if($query->num_rows() > 0)
{
return $query->result_array();
}
else
{
return false;
}
}
您可能对使用 CI活动记录class 感兴趣:
除了简单之外,使用 Active Record 功能的一个主要好处是它允许您创建独立于数据库的应用程序,因为查询语法是由每个数据库适配器生成的。 它还允许更安全的查询,因为系统会自动转义这些值。
您重写的查询将如下所示(假设$id
是一个数组):
$this->db->where_in('toy_id', $id)->get('toys');
旁白:我承认我有点困惑,因为看起来$ids
将是一个更合适的变量名,而您在查询中使用它的方式,我会假设它是一个字符串......
如果您不喜欢活动记录,您可能还会发现查询绑定很有用:
使用绑定的第二个好处是值会自动转义,从而产生更安全的查询。 您不必记住手动转义数据; 引擎会自动为您完成。
编辑:稍后回顾一下,看起来这就是你想要做的。 在这种情况下,请尝试替换:
$sql = "select * from toys t where t.toy_id in ($id)";
和:
$sql = "select * from toys t where t.toy_id in (?)";
并将$id
作为第二个参数传递给query()
,但作为逗号分隔的字符串( implode(',', $id)
如果$id
确实是一个数组)。
否则你可能想使用$this->db->escape_str()
。
$this->db->escape_str() 这个 function 对传递给它的数据进行转义,无论类型如何。
以下是 mysql 驱动程序源代码的摘录,或许可以让您放心。
function escape_str($str, $like = FALSE)
{
if (is_array($str))
{
foreach ($str as $key => $val)
{
$str[$key] = $this->escape_str($val, $like);
}
return $str;
}
// continued...
它循环通过 arrays 并转义它们的值。
看来$this->db->escape
escape 确实不适用于 arrays。
$this->db->escape() 这个 function 确定数据类型,以便它只能转义字符串数据。
这是来源:
function escape($str)
{
if (is_string($str))
{
$str = "'".$this->escape_str($str)."'";
}
elseif (is_bool($str))
{
$str = ($str === FALSE) ? 0 : 1;
}
elseif (is_null($str))
{
$str = 'NULL';
}
return $str;
}
看起来它忽略了 arrays。
无论如何,希望您找到适合您的解决方案。 我投票支持 Active Record。
您想要做的是转义数组中的各个值。 所以你可以先在数组上使用array_map 。
$id = array_map('some_escape_function', $id);
参见: http://php.net/manual/en/function.array-map.php
然后你可以这样做:
$in = join(",",$id);
您的 SQL 将是:
WHERE t.toy_id in ($in)
这给了你:
WHERE t.toy_id in ('1','2','3')
这是我使用 CI 2.1.2 的解决方案:
1) 将 /system/database/DB.php 复制到 application/database/DB.php,在第 123 行附近,使其看起来像:
...
if ( ! isset($active_record) OR $active_record == TRUE)
{
require_once(BASEPATH.'database/DB_active_rec.php');
require_once(APPPATH.'database/MY_DB_active_rec' . EXT);
if ( ! class_exists('CI_DB'))
{
eval('class CI_DB extends MY_DB_active_record { }');
}
}
...
2)在应用程序/核心中创建MY_Loader.php:
class MY_Loader extends CI_Loader
{
function __construct()
{
parent::__construct();
log_message('debug', 'MY Loader Class Initialized');
}
public function database($params = '', $return = FALSE, $active_record = NULL) {
// Grab the super object
$CI = & get_instance();
// Do we even need to load the database class?
if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db)) {
return FALSE;
}
//require_once(BASEPATH . 'database/DB.php');
require_once(APPPATH . 'database/DB' . EXT);
if ($return === TRUE) {
return DB($params, $active_record);
}
// Initialize the db variable. Needed to prevent
// reference errors with some configurations
$CI->db = '';
// Load the DB class
$CI->db = & DB($params, $active_record);
}
}
3)创建应用程序/数据库/MY_DB_active_rec.php:
class MY_DB_active_record extends CI_DB_active_record {
public function __construct($params)
{
parent::__construct($params);
log_message('debug', 'MY Active Record Database Driver Class Initialized');
}
private function _array_escape(&$str)
{
$str = "'" . $this->escape_str($str) . "'";
}
function escape($str)
{
if (is_array($str))
{
array_walk($str, array($this, '_array_escape'));
return implode(',', $str);
}
elseif (is_string($str))
{
$this->_array_escape($str);
}
elseif (is_bool($str))
{
$str = ($str === FALSE) ? 0 : 1;
}
elseif (is_null($str))
{
$str = 'NULL';
}
return $str;
}
}
然后你只需传入一个值数组:
$in_data = array(1, 2, 3);
$this->db->query('SELECT * FROM table WHERE id IN(?)', array($in_data));
它不漂亮,但它似乎可以解决问题!
你可以尝试这样的事情:
$sql = 'select * from toys t where t.toy_id in ('.
join(',',array_map(function($i) {
return $this->db->escape($i);
}, $id)).');';
*免责声明:我现在无法访问我的 PHP/MySQL 服务器,所以我没有对此进行验证。 可能需要进行一些修改和/或调整。
要绑定它们,您可以执行以下操作:
$queryParams = [];
// to add the appropriate amount of bindings ?
$idBindings = str_replace(' ', ',', trim(str_repeat("(?) ", count($ids))));
// add each id with int validation
foreach ($ids as $id) {
if(is_int(intVal($id)) == TRUE){
array_push($queryParams, intVal($id));
}
}
// the other option commented out below is to merge without checking -
// note: sometimes values look like numeric values but are actually strings
//queryParams = array_merge($queryParams, $ids);
$sql = "select * from toys t where t.toy_id in ('. $idBindings .')";
$query = $this->db->query($sql, $queryParams );
Code Igniter v3 现在自动转义数组值:
http://www.codeigniter.com/userguide3/database/queries.html
查询绑定
绑定使您能够通过让系统为您将查询放在一起来简化查询语法。 考虑以下示例:
$sql = "SELECT * FROM some_table WHERE id =? AND status =? AND author =?"; $this->db->query($sql, array(3, 'live', 'Rick'));
查询中的问号会自动替换为查询 function 的第二个参数中的数组中的 > 值。
绑定也适用于 arrays,它将转换为 IN 集:
$sql = "SELECT * FROM some_table WHERE id IN? AND status =? AND author =?"; $this->db->query($sql, array(array(3, 6), 'live', 'Rick'));
结果查询将是:
SELECT * FROM some_table WHERE id IN (3,6) AND status = 'live' AND author = 'Rick'
使用绑定的第二个好处是值会自动转义,从而产生更安全的查询。 您不必记住手动转义数据; 引擎会自动为您完成。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.