在使用where子句中的“in”传递数组时,使用转义值的Codeigniter问题

时间:2011-06-02 02:23:24

标签: php codeigniter

我在我的模型中有一个codeigniter项目的函数,变量$id是一个数组,例如,包含(1,2,3)。现在我正在重新审视它,我认为我实际上并没有逃避我的数组$id。我想我必须改变这条线 $this->db->escape($id)$id = $this->db->escape($id)

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

有人可以确认我实际上没有转义我的变量并建议解决方案或让我知道这是否是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;
        }
    }

6 个答案:

答案 0 :(得分:5)

您可能有兴趣使用CI Active Record类:

  

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

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

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

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

如果您的活动记录不合适,您可能还会发现Query Bindings非常有用:

  

使用绑定的第二个好处是值会自动转义,从而产生更安全的查询。您不必记住手动转义数据;引擎会自动为您完成。


编辑:稍后回顾一下,看起来这就是你想要做的事情。在这种情况下,请尝试替换:

$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()无论类型如何,此函数都会转义传递给它的数据。

以下是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;
}

看起来它会忽略数组。

无论如何,希望您找到适合您的解决方案。我的投票是针对Active Record。

答案 1 :(得分:3)

您要做的是逃避数组中的各个值。所以你可以先在数组上使用 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')

答案 2 :(得分:1)

您可以尝试这样的事情:

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

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

答案 3 :(得分:1)

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

它不漂亮,但似乎可以做到这一点!

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

Code Igniter v3现在自动转义数组值:

http://www.codeigniter.com/userguide3/database/queries.html

  

查询绑定

     

通过让系统将查询放在一起,Bindings可以简化查询语法。请考虑以下示例:

     

$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'

     

使用绑定的第二个好处是值会自动转义,从而产生更安全的查询。您不必记住手动转义数据;引擎会自动为您完成。