11

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?

2
  • 1
    I would just make sure everything has been validated before added them to the field and value arrays Commented Nov 16, 2009 at 19:10
  • Watch out: implode() converts NULL to empty string, which then becomes 0 in integer columns Commented Dec 6, 2012 at 13:25

7 Answers 7

22

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.

Sign up to request clarification or add additional context in comments.

5 Comments

automatically no, just use php.net/manual/en/function.mysql-real-escape-string.php on each item before insertion
Wow, this is fantastic. You have added quotes to the values as well as improved the readability. Thanks very much!
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: but wouldn't array_filter() remove also the string "0"? This might not be a good idea.
@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.
3

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.

1 Comment

+1 for parametrized queries. Link for reference: us.php.net/manual/en/pdo.prepared-statements.php
1

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.

Comments

0

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.

4 Comments

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).
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?
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 - I chose option A. Thanks for the advice. Now these comments don't make much sense - do we delete them?
0

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
            /*
            useless piece of code see comments
            if($val === FALSE)
               $sql .= "FALSE";
            else
            */
               $sql .= "'" . addslashes($val) . "'";

         $sql .= ", ";
      };

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

3 Comments

if $val === true, then this gives you '1'.
@TomHaigh: that's correct, it's not a bug. '1' in SQL query is TRUE.
in that case why have you done ` $sql .= "FALSE";` for false? Wouldn't it be better to be consistent?
0

There is a problem with NULL (in the accepted answer) values being converted to empty string "". So this is fix, NULL becomes NULL without quotes:

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

    return substr($s, 1);
}

Usage:

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

Comments

0

If you want to enhance your approach and add the possibility for input validation and sanitation, you might want to do this:

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).')' );
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.