Передать необязательный SQL в подготовленный оператор - PullRequest
0 голосов
/ 30 сентября 2019

Я пытаюсь вставить какой-то необязательный SQL в подготовленный оператор с параметром $and:

public function loadInfoAndStatus($property_id, $property_item_type_id, $and, $returnArray = false)
{
    if (!isset($property_id) || empty($property_id)
     || !isset($property_item_type_id) || empty($property_item_type_id)
     || !isset($and) || empty($and)) {
        error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
        return false;
    }

    $sql = "   SELECT pi.status, pi.info, pi.property_item_id "
           . " FROM ". self::TABLE ." pi "
           . " JOIN countries c ON c.country_id = pi.country_id "
           . " WHERE pi.property_id = ? "
           . "     AND property_item_type_id = ? "
           .       $this->con->real_escape_string($and)     // <--- here
           . " ORDER BY pi.status "
           . " DESC LIMIT 0,1";

    $err = "";
    if (!$stmt = $this->con->prepare($sql)) {
        $err .= "Prepare failed: (" . $this->con->errno . ") " . $this->con->error;
    }

    ...

Но если я вызываю функцию, например,

$row2 = Main::getModel("Property/Item")->loadInfoAndStatus(
    $id
    , $property_item_type_id
    , " AND c.iso = 'DE' "
    , true
);

Подсказка: $ и может быть одним из:

" AND c.iso <> 'DE' AND c.european <> 1 "
" AND c.iso <> 'DE' AND c.european = 1 "
" AND c.iso = 'DE' "

Затем я получаю «Подготовка не удалась», но сообщения об ошибке нет.

Результирующий SQL:

SELECT pi.status, pi.info, pi.property_item_id  FROM property_item pi  JOIN countries c ON c.country_id = pi.country_id  WHERE pi.property_id = ?      AND property_item_type_id = ?  AND c.iso = \'DE\'  ORDER BY pi.status  DESC LIMIT 0,1

Это работает, если я не использую real_escape_string

Нужно ли создавать новые функции для каждого нового sql, или есть другой способ?

Ответы [ 2 ]

1 голос
/ 30 сентября 2019

Я решил проблему с помощью метода белого списка:

public function loadInfoAndStatus($property_id, $property_item_type_id, $and = "", $returnArray = false)
{
    if (empty($property_id) || empty($property_item_type_id) || empty($and)) {
        error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
        return false;
    }

    if (!$this->isSqlInWhitelist($and, array(
        "AND c.iso = 'DE'"
        ,"AND c.iso <> 'DE' AND c.european = 1"
        ,"AND c.iso <> 'DE' AND c.european <> 1"
    ))) {
        error_log(get_class() . " - " . __FUNCTION__ ." : sql is not in whitelist.");
        return false;
    }

    $sql = "   SELECT pi.status, pi.info, pi.property_item_id "
           . " FROM ". self::TABLE ." pi "
           . " JOIN countries c ON c.country_id = pi.country_id "
           . " WHERE pi.property_id = ? "
           . "     AND property_item_type_id = ? "
           .       $and
           . " ORDER BY pi.status "
           . " DESC LIMIT 0,1";

    $stmt = $this->con->prepare($sql);
    ...

...

protected function isSqlInWhitelist($sql, $whitelist)
{
    if (!empty($sql)) {
        if (!in_array(trim($sql), $whitelist)) { return false; }
    }
    return true;
}
1 голос
/ 30 сентября 2019

Вы должны перечислить все возможные варианты в вашей функции.

Это сложная задача, но вы должны понять, , что это единственный способ.

public function loadInfoAndStatus($property_id, $property_item_type_id, $iso = null, $european = null, $returnArray = false)
{
    if (empty($property_id) || empty($property_item_type_id)) {
        error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
        return false;
    }
    $parameters = [$property_id, $property_item_type_id];
    $sql = "   SELECT pi.status, pi.info, pi.property_item_id "
           . " FROM ". self::TABLE ." pi "
           . " JOIN countries c ON c.country_id = pi.country_id "
           . " WHERE pi.property_id = ? "
           . "     AND property_item_type_id = ? ";

    if ($iso) {
        $sql .= " AND c.iso <> ? ";
        $parameters[] = $iso;
    }
    if ($european === true) {
        $sql .= " AND c.european == 1 ";
    } elseif ($european === false) {
        $sql .= " AND c.european <> 1 ";
    }

    $sql .= " ORDER BY pi.status ";
    $sql .= " DESC LIMIT 0,1";

    $stmt = $this->con->prepare($sql);
    $stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
    $stmt->execute();

Я также удалил некоторый код культа груза из вашего метода, на случай, если вас заинтересует, почему

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...