Как написать хорошую вставку базы данных PHP, используя ассоциативный массив

В PHP я хочу вставить в базу данных данные, содержащиеся в ассоциативном массиве пар полей / значений.

Пример:

$_fields = array('field1'=>'value1','field2'=>'value2','field3'=>'value3'); 

Результирующая вставка SQL должна выглядеть следующим образом:

 INSERT INTO table (field1,field2,field3) VALUES ('value1','value2','value3'); 

Я придумал следующий однострочный PHP:

 mysql_query("INSERT INTO table (".implode(',',array_keys($_fields)).") VALUES (".implode(',',array_values($_fields)).")"); 

Он отделяет ключи и значения ассоциативного массива и implodes для генерации строки, разделенной запятыми. Проблема в том, что он не убегает или не цитирует значения, которые были вставлены в базу данных. Чтобы проиллюстрировать опасность, представьте, если $_fields содержали следующее:

 $_fields = array('field1'=>"naustyvalue); drop table members; --"); 

Будет создан следующий SQL:

 INSERT INTO table (field1) VALUES (naustyvalue); drop table members; --; 

К счастью, несколько запросов не поддерживаются , тем не менее, котировки и экранирование необходимы для предотвращения уязвимостей SQL-инъекций.

Как вы пишете свои вставки Mysql PHP?

Примечание: подготовленные запросы PDO или mysqli в настоящее время не являются для меня опцией, потому что база кода уже использует mysql экстенсивно – изменения запланированы, но для преобразования потребуется много ресурсов?

Единственное, что я бы изменил, было бы использовать sprintf для удобства чтения

 $sql = sprintf( 'INSERT INTO table (%s) VALUES ("%s")', implode(',',array_keys($_fields)), implode('","',array_values($_fields)) ); mysql_query($sql); 

и убедитесь, что значения экранированы.

В этом нет ничего плохого. Я делаю то же самое.

Но убедитесь, что вы mysql_escape() и укажите значения, которые вы вставляете в запрос, иначе вы смотрите на уязвимость SQL-инъекций.

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

Лучшей практикой является либо использование ORM (Doctrine 2.0), реализация ActiveRecord (Doctrine 1.0, RedBean), либо реализация шаблона TableGateway (Zend_Db_Table, Propel). Эти инструменты сделают вашу жизнь намного проще и справятся с большой тягой для вас и помогут защитить вас от инъекций SQL.

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

Используя трюк sprintf, упомянутый Галеном в предыдущем ответе , я придумал следующий код:

 $escapedfieldValues = array_map(create_function('$e', 'return mysql_real_escape_string(((get_magic_quotes_gpc()) ? stripslashes($e) : $e));'), array_values($_fields)); $sql = sprintf('INSERT INTO table (%s) VALUES ("%s")', implode(',',array_keys($_fields)), implode('"," ',$escapedfieldValues)); mysql_query($sql); 

Он генерирует экранированную и цитированную вставку. Он также управляется независимо от того, magic_quotes_gpc или выключено magic_quotes_gpc . Код может быть приятнее, если я использовал анонимные функции PHP v5.3.0, но мне нужно, чтобы он запускался на старых установках PHP.

Этот код немного дольше, чем оригинал (и медленнее), но он более безопасен.

Я использую это, чтобы получить часть VALUES INSERT. Но это может быть абсурдным способом. Комментарии / предложения приветствуются.

  function arrayToSqlValues($array) { $sql = ""; foreach($array as $val) { //adding value if($val === NULL) $sql .= "NULL"; else /* useless piece of code see comments if($val === FALSE) $sql .= "FALSE"; else */ $sql .= "'" . addslashes($val) . "'"; $sql .= ", "; }; return "VALUES(" . rtrim($sql, " ,") . ")"; } 

Существует проблема с значениями NULL (в принятом ответе), которые преобразуются в пустую строку "". Итак, это исправление, NULL становится NULL без кавычек:

 function implode_sql_values($vals) { $s = ''; foreach ($vals as $v) $s .= ','.(($v===NULL)?'NULL':'"'.mysql_real_escape_string($v).'"'); return substr($s, 1); } 

Применение:

 implode_sql_values(array_values( array('id'=>1, 'nick'=>'bla', 'fbid'=>NULL) )); // =='"1","bla",NULL' 

Если вы хотите улучшить свой подход и добавить возможность проверки ввода и санитарии, вы можете сделать это:

 function insertarray($table, $arr){ foreach($arr as $k => $v){ $col[] = sanitize($k); $val[] = "'".sanitize($v)."'"; } query('INSERT INTO '.sanitize($table).' ('.implode(', ', $col).') VALUES ('.implode(', ', $val).')' ); }