简体   繁体   English

Codeigniter 问题与 escaping 值在 where 子句中使用“in”传递数组进行查询时

[英]Codeigniter issue with escaping values when passing array to query with “in” in the where clause

I have the function below in my model for a codeigniter project, and the variable $id is an array and for example, contains (1,2,3) .我在 codeigniter 项目的 model 中有下面的 function,变量$id是一个数组,例如,包含(1,2,3) Now that i'm revisiting it, I think that i'm not actually escaping my array $id .现在我正在重新审视它,我认为我实际上并不是 escaping 我的数组$id I think I would have to change the line $this->db->escape($id) to $id = $this->db->escape($id)我想我必须将行$this->db->escape($id)更改$id = $this->db->escape($id)

If I do that, then it puts single quotes around every element in the array and treats it as one long string like this: '(1,2,3)' .如果我这样做,那么它会在数组中的每个元素周围加上单引号,并将其视为一个长字符串,如下所示: '(1,2,3)'

Can someone confirm that I am not actually escaping my variable and either suggest a solution or let me know if this is a bug within the codeigniter framework?有人可以确认我实际上不是 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;
        }
    }

You may be interested in using the CI Active Record class:您可能对使用 CI活动记录class 感兴趣:

Beyond simplicity, a major benefit to using the Active Record features is that it allows you to create database independent applications, since the query syntax is generated by each database adapter.除了简单之外,使用 Active Record 功能的一个主要好处是它允许您创建独立于数据库的应用程序,因为查询语法是由每个数据库适配器生成的。 It also allows for safer queries, since the values are escaped automatically by the system .它还允许更安全的查询,因为系统会自动转义这些值。

Your rewritten query would look like this (assuming $id is an array):您重写的查询将如下所示(假设$id是一个数组):

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

Aside: I will admit I am a bit confused, as it looks like $ids would be a more appropriate variable name, and the way you are using it in the query, I would assume it is a string...旁白:我承认我有点困惑,因为看起来$ids将是一个更合适的变量名,而您在查询中使用它的方式,我会假设它是一个字符串......

If active record is not your thing, you may also find Query Bindings to be useful:如果您不喜欢活动记录,您可能还会发现查询绑定很有用:

The secondary benefit of using binds is that the values are automatically escaped , producing safer queries.使用绑定的第二个好处是值会自动转义,从而产生更安全的查询。 You don't have to remember to manually escape data;您不必记住手动转义数据; the engine does it automatically for you.引擎会自动为您完成。


EDIT : Looking back on this later, it looks like this is what you're trying to do.编辑:稍后回顾一下,看起来这就是你想要做的。 In that case, try replacing:在这种情况下,请尝试替换:

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

With:和:

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

And pass $id as the second argument to query() , but as a comma separated string ( implode(',', $id) if $id is indeed an array).并将$id作为第二个参数传递给query() ,但作为逗号分隔的字符串( implode(',', $id)如果$id确实是一个数组)。


Otherwise you may want to use $this->db->escape_str() .否则你可能想使用$this->db->escape_str()

$this->db->escape_str() This function escapes the data passed to it, regardless of type. $this->db->escape_str() 这个 function 对传递给它的数据进行转义,无论类型如何。

Here is an excerpt from the source code of the mysql driver to maybe put your mind at ease.以下是 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...

It loops through arrays and escapes their values.它循环通过 arrays 并转义它们的值。

It does indeed seem that $this->db->escape is not going to work for arrays.看来$this->db->escape escape 确实不适用于 arrays。

$this->db->escape() This function determines the data type so that it can escape only string data. $this->db->escape() 这个 function 确定数据类型,以便它只能转义字符串数据。

Here is the source:这是来源:

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

Looks like it ignores arrays.看起来它忽略了 arrays。

Anyways, hope you find a solution that works for you.无论如何,希望您找到适合您的解决方案。 My vote is for Active Record.我投票支持 Active Record。

What you want to do is escape the individual values in the array.您想要做的是转义数组中的各个值。 So you can use array_map on the array first.所以你可以先在数组上使用array_map

$id = array_map('some_escape_function', $id);

See: http://php.net/manual/en/function.array-map.php参见: http://php.net/manual/en/function.array-map.php

Then you can do:然后你可以这样做:

$in = join(",",$id);

Your SQL would then be:您的 SQL 将是:

WHERE t.toy_id in ($in)

Which gives you:这给了你:

WHERE t.toy_id in ('1','2','3')

Here's the solution I'm using for this, with CI 2.1.2:这是我使用 CI 2.1.2 的解决方案:

1) Copy /system/database/DB.php to application/database/DB.php, and around line 123, make it look like: 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) Create MY_Loader.php in application/core: 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) Create application/database/MY_DB_active_rec.php: 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;
  }

}

Then you just pass in an array of values:然后你只需传入一个值数组:

$in_data = array(1, 2, 3);
$this->db->query('SELECT * FROM table WHERE id IN(?)', array($in_data));

It's not pretty, but it seems to do the trick!它不漂亮,但它似乎可以解决问题!

You could try something like this:你可以尝试这样的事情:

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

*Disclaimer: I'm not where I can access my PHP/MySQL server right now, so I haven't validated this. *免责声明:我现在无法访问我的 PHP/MySQL 服务器,所以我没有对此进行验证。 Some modification and/or tweakage may be necessary.可能需要进行一些修改和/或调整。

To bind them you can do the following:要绑定它们,您可以执行以下操作:

$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 now automaticly escapes array values: Code Igniter v3 现在自动转义数组值:

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

Query Bindings查询绑定

Bindings enable you to simplify your query syntax by letting the system put the queries together for you.绑定使您能够通过让系统为您将查询放在一起来简化查询语法。 Consider the following example:考虑以下示例:

$sql = "SELECT * FROM some_table WHERE id =? AND status =? AND author =?"; $this->db->query($sql, array(3, 'live', 'Rick'));

The question marks in the query are automatically replaced with the >values in the array in the second parameter of the query function.查询中的问号会自动替换为查询 function 的第二个参数中的数组中的 > 值。

Binding also work with arrays, which will be transformed to IN sets:绑定也适用于 arrays,它将转换为 IN 集:

$sql = "SELECT * FROM some_table WHERE id IN? AND status =? AND author =?"; $this->db->query($sql, array(array(3, 6), 'live', 'Rick'));

The resulting query will be:结果查询将是:

SELECT * FROM some_table WHERE id IN (3,6) AND status = 'live' AND author = 'Rick'

The secondary benefit of using binds is that the values are automatically escaped, producing safer queries.使用绑定的第二个好处是值会自动转义,从而产生更安全的查询。 You don't have to remember to manually escape data;您不必记住手动转义数据; the engine does it automatically for you.引擎会自动为您完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM