代码点火器在将数组传递给带有“”的查询时出现转义值问题;在“;在where子句中


Codeigniter issue with escaping values when passing array to query with "in" in the where clause

在代码点火器项目的模型中,我有下面的函数,变量$id是一个数组,例如,包含(1,2,3)。现在我正在重新访问它,我想我实际上并没有逃离我的数组$id。我想我得改一下线路$this->db->escape($id)$id = $this->db->escape($id)

如果我这样做,那么它会在数组中的每个元素周围加上单引号,并将其视为一个长字符串,如下所示:'(1,2,3)'

有人能确认我实际上没有逃离我的变量,并提出解决方案或让我知道这是否是代码点火器框架中的一个错误吗?

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活动记录类:

除了简单之外,使用Active Record功能的一个主要好处是,它允许您创建独立于数据库的应用程序,因为查询语法是由每个数据库适配器生成的。它还允许更安全的查询,因为系统会自动转义值。

重写后的查询如下所示(假设$id是一个数组):

$this->db->where_in('toy_id', $id)->get('toys');

旁白:我承认我有点困惑,因为看起来$ids是一个更合适的变量名,而在查询中使用它的方式,我认为它是一个字符串。。。

如果您不喜欢活动记录,您可能还会发现查询绑定很有用:

使用绑定的第二个好处是值会自动转义,从而生成更安全的查询。您不必记住手动转义数据;发动机自动为你做这件事。


EDIT:稍后回过头来看,这似乎就是您要做的。在这种情况下,请尝试替换:

$sql = "select * from toys t where t.toy_id in ($id)";

带有:

$sql = "select * from toys t where t.toy_id in (?)";

并将$id作为第二个参数传递给query(),但作为逗号分隔的字符串(如果$id确实是一个数组,则为implode(',', $id))。


否则,您可能需要使用$this->db->escape_str()

$this->db->escape_str()此函数对传递给它的数据进行转义,无论数据类型如何。

以下是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...

它循环遍历数组并转义它们的值。

$this->db->escape似乎确实不适用于数组。

$this->db->escape()此函数确定数据类型,以便只转义字符串数据。

以下是来源:

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;
}

看起来它忽略了数组。

无论如何,希望你能找到一个适合你的解决方案。我的投票支持"积极记录"。

您想要做的是转义数组中的各个值。因此,您可以先在数组上使用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')

你可以试试这样的东西:

$sql = 'select * from toys t where t.toy_id in ('.
  join(',',array_map(function($i) {
    return $this->db->escape($i);
  }, $id)).');';

*免责声明:我现在不在可以访问我的PHP/MySQL服务器的地方,所以我还没有验证这一点。可能需要进行一些修改和/或调整。

以下是我使用的解决方案,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) 在application/core:中创建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));

它不漂亮,但似乎很管用!

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'));

查询中的问号会自动替换为查询函数的第二个参数中数组中的>值。

绑定也适用于数组,数组将转换为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'

使用绑定的第二个好处是自动转义值,从而生成更安全的查询。您不必记住手动转义数据;发动机自动为你做这件事。

要绑定它们,可以执行以下操作:

$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 );