views:

980

answers:

5

In PHP, I want to insert into a database using data contained in a associative array of field/value pairs.

Example:

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

The resulting SQL insert should look as follows:

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

I have come up with the following PHP one-liner:

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

It separates the keys and values of the the associative array and implodes to generate a comma-separated string . The problem is that it does not escape or quote the values that were inserted into the database. To illustrate the danger, Imagine if $_fields contained the following:

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

The following SQL would be generated:

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

Luckily, multiple queries are not supported, nevertheless quoting and escaping are essential to prevent SQL injection vulnerabilities.

How do you write your PHP Mysql Inserts?

Note: PDO or mysqli prepared queries aren't currently an option for me because the codebase already uses mysql extensively - a change is planned but it'd take alot of resources to convert?

+3  A: 

Nothing wrong with that. I do the same.

But make sure you mysql_escape() and quote the values you stick in the query, otherwise you're looking at SQL injection vulnerability.

Alternately, you could use parametrized queries, in which case you can practically pass the array in itself, instead of building a query string.

Frank Farmer
+1 for parametrized queries. Link for reference: http://us.php.net/manual/en/pdo.prepared-statements.php
outis
A: 

The best practice is either to use an ORM (Doctrine 2.0), an ActiveRecord implementation (Doctrine 1.0, RedBean), or a TableGateway pattern implementation (Zend_Db_Table, Propel). These tools will make your life a lot easier, and handle a lot of the heavy lifting for you, and can help protect you from SQL injections.

Other than that, there's nothing inherently wrong with what you're doing, you just might want to abstract it away into a class or a function, so that you can repeat the functionality in different places.

blockhead
+1 for best practice
Peter Lindqvist
+3  A: 

The only thing i would change would be to use sprintf for readability purposes

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

and make sure the values are escaped.

Galen
automatically no, just use http://php.net/manual/en/function.mysql-real-escape-string.php on each item before insertion
Galen
Wow, this is fantastic. You have added quotes to the values as well as improved the readability. Thanks very much!
Tom
I'll suggest to run array_filter() before, in order to remove the empty values of the array, they are useless but can slower your query.
DaNieL
@DaNieL: but wouldn't array_filter() remove also the string "0"? This might not be a good idea.
Marco Demajo
@marco: yes it will, the string "0" is considerated false in boolean, so it will be removed by array_filter (without callback). Take care that, depending on the situation, this can be a problem.
DaNieL
A: 

Using the sprintf trick mentioned by Galen in a previous answer, I have come up with the following code:

$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);

It generates a escaped and quoted insert. It also copes independent of whether magic_quotes_gpc is on or off. The code could be nicer if I used new PHP v5.3.0 anonymous functions but I need it to run on older PHP installations.

This code is a bit longer that the original (and slower) but it is more secure.

Tom
This doesn't really deserve to be an answer. It should be added to the original question. As for security, your best bet these days is to use prepared queries (aka parametrized queries).
outis
I agree, but if I add it to the original question, the existing answers won't make so much sense? Should I add it anyway?
Tom
Either A) add most of it, except the code (since it's a potential answer) or B) add it in a section title "Update".
outis
@outis - I chose option A. Thanks for the advice. Now these comments don't make much sense - do we delete them?
Tom
A: 

I use this to retrieve the VALUES part of the INSERT. But it might be an absurd way to do things. Comments/suggestions are welcome.

   function arrayToSqlValues($array)
   {
      $sql = "";
      foreach($array as $val)
      {    
         //adding value
         if($val === NULL)
            $sql .= "NULL";
         else
            if($val === FALSE)
               $sql .= "FALSE";
            else
               $sql .= "'" . addslashes($val) . "'";

         $sql .= ", ";
      };

      return "VALUES(" . rtrim($sql, " ,") . ")";
   }
Marco Demajo