Ошибка Codeigniter при экранировании значений при передаче массива в запрос с помощью «in» в предложении where - PullRequest
7 голосов
/ 02 июня 2011

У меня есть функция ниже в моей модели для проекта 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 ]

5 голосов
/ 02 июня 2011

Вас может заинтересовать использование класса CI Active Record :

Помимо простоты, основным преимуществом использования функций 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 () ThisФункция экранирует данные, передаваемые ей, независимо от типа.

Вот выдержка из исходного кода драйвера 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;
}

Похоже, он игнорирует массивы.

В любом случае, надеюсь, вы найдете решение, которое работает для вас.Мой голос за активную запись.

3 голосов
/ 02 июня 2011

То, что вы хотите сделать, это экранировать отдельные значения в массиве. Таким образом, вы можете сначала использовать 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')
1 голос
/ 05 сентября 2012

Вот решение, которое я использую для этого, с 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));

Это не красиво, но, кажется, делает свое дело!

1 голос
/ 02 июня 2011

Вы можете попробовать что-то вроде этого:

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

* Отказ от ответственности: я не могу получить доступ к своему серверу PHP / MySQL прямо сейчас, поэтому я не проверял это. Может потребоваться некоторая модификация и / или настройка.

0 голосов
/ 09 апреля 2015

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'

Вторым преимуществом использования связываний является то, что значения автоматически экранируются, что делает более безопасные запросы.Вам не нужно помнить, чтобы вручную экранировать данные;двигатель делает это автоматически для вас.

0 голосов
/ 03 февраля 2015

Чтобы связать их, вы можете сделать следующее:

$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 );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...