MySQLi: запрос VS готовят

Есть что-то, чего я совсем не понимаю, это prepare и query в mysqli.

Этот метод использует mysqli::query для обработки запроса, и было известно о отсутствии безопасности:

public function fetch_assoc($query)
    {
        $result = parent::query($query);
        //$result = self::preparedStatement($query);
        if($result) 
        {
            return $result->fetch_assoc();
        } 
        else
        {
            # call the get_error function
            return self::get_error();
            # or:
            # return $this->get_error();
        }
    }

это тот, с помощью метода prepare-bind-execute, который, как я полагаю, имеет лучшую безопасность,

public function fetch_assoc_stmt($sql,$types = null,$params = null)
    {
        # create a prepared statement
        $stmt = parent::prepare($sql);

        # bind parameters for markers
        # but this is not dynamic enough...
        //$stmt->bind_param("s", $parameter);

        if($types&&$params)
        {
            $bind_names[] = $types;
            for ($i=0; $i<count($params);$i++) 
            {
                $bind_name = 'bind' . $i;
                $$bind_name = $params[$i];
                $bind_names[] = &$$bind_name;
            }
            $return = call_user_func_array(array($stmt,'bind_param'),$bind_names);
        }

        # execute query 
        $stmt->execute();

        # these lines of code below return one dimentional array, similar to mysqli::fetch_assoc()
        $meta = $stmt->result_metadata(); 

        while ($field = $meta->fetch_field()) { 
            $var = $field->name; 
            $$var = null; 
            $parameters[$field->name] = &$$var; 
        }

        call_user_func_array(array($stmt, 'bind_result'), $parameters); 

        while($stmt->fetch()) 
        { 
            return $parameters;  
        }

        # close statement
        $stmt->close();
    }

Однако оба этих метода возвращают один и тот же результат,

$mysqli = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);

$sql = "
SELECT *
FROM root_contacts_cfm
ORDER BY cnt_id DESC
";
print_r($mysqli->fetch_assoc_stmt($sql));

print_r($mysqli->fetch_assoc($sql));

они печатают это:

Array
(
    [cnt_id] => 2
    [cnt_email1] => [email protected]
    [cnt_email2] => 
    [cnt_fullname] => Lau T
    [cnt_firstname] => Thiam
    [cnt_lastname] => Lau
    [cnt_organisation] => 
    [cnt_website] => 
    [cnt_biography] => 
    [cnt_gender] => 
    [cnt_birthday] => 
    [cnt_address] => 
    [cnt_postcode] => 
    [cnt_telephone] => 
    [cnt_note] => 
    [cnt_key] => 
    [cat_id] => 
    [tcc_id] => 
    [cnt_suspended] => 0
    [cnt_created] => 2011-02-04 00:00:00
    [cnt_updated] => 2011-02-04 13:54:36
)
Array
(
    [cnt_id] => 2
    [cnt_email1] => [email protected]
    [cnt_email2] => 
    [cnt_fullname] => Lau T
    [cnt_firstname] => Thiam
    [cnt_lastname] => Lau
    [cnt_organisation] => 
    [cnt_website] => 
    [cnt_biography] => 
    [cnt_gender] => 
    [cnt_birthday] => 
    [cnt_address] => 
    [cnt_postcode] => 
    [cnt_telephone] => 
    [cnt_note] => 
    [cnt_key] => 
    [cat_id] => 
    [tcc_id] => 
    [cnt_suspended] => 0
    [cnt_created] => 2011-02-04 00:00:00
    [cnt_updated] => 2011-02-04 13:54:36
)

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

Итак, мой вопрос заключается в том, что использование prepare лучше, чем query, почему fetch_assoc вообще существует? Разве мы не должны просто забывать об этом или не должны ли php.net осуждать это? То же самое для fetch_all - зачем нам это делать в первую очередь!??

Спасибо.

Ответ 1

Подготовленные утверждения предпочтительнее простых SQL-запросов, когда вы используете параметры для динамического генерации запроса. В вашем примере ваш SQL не содержит переменных, поэтому использование простого запроса или подготовленного оператора функционально эквивалентно.

Если вы должны изменить значения параметров, например, в предложении WHERE, то подготовленные операторы предоставят вам дополнительную безопасность:

...
WHERE col1 = ? AND col2 = ?

Но когда ваш запрос прост и исправлен, для $mysqli->query($sql) может потребоваться меньше кода для использования fetch_assoc(). Использование прямых запросов, а не готовых заявлений, не является повсеместно плохой практикой, так как некоторые могут вам верить. Когда ваш запрос требует параметризации или когда один и тот же запрос необходимо скомпилировать и выполнить повторно, вы получите выгоду от подготовленного оператора.

Ответ 2

Извините, что это не ответ, но я недостаточно хорош, чтобы оставить комментарий.

Похоже, что в вашей второй функции есть ошибка. Ваш код не будет работать правильно для запросов, которые возвращают более одной строки. Не должно быть этого утверждения:

while($stmt->fetch()) {

    //to dereference
    $row_copy = $parameters;
    $return_array[] = $row_copy;

}

И тогда функция должна заканчиваться:

return $return_array;