1

EDIT:

Thank you so much for your answers, you really amaze me with so much wisdom :)

I am trying to relay on TuteC's code a bit changed, but can't figure how to make it work properly:

$valor = $_POST['valor'];

$post_vars = array('iphone3g1', 'iphone3g2', 'nome', 'iphone41', 'postal', 'apelido');
foreach($post_vars as $var) {
    $$var = "'" . mysql_real_escape_string($_POST[$var]). "', ";
}

$sql = "INSERT INTO clientes (iphone3g1, iphone3g2, nome, iphone41, postal, apelido, valor) VALUES ($$var '$valor')";
$query= mysql_query($sql);

I know there's a bit of cheating on the code, i would need to use substring so the $$var wouldn't output a "," at the end where i need the values, instead i tried to insert a variable that is a value ($valor = $_POST['valor'];) What is going wrong?

And for the others who tried to help me, thank you very much, i am learning so much with you here at stackoverflow.

I have a form with several field values, when trying to write a php file that reads those values it came out a mostruosity:

$codigounico= md5(uniqid(rand()));
$modelo=$_POST['selectName'];
$serial=$_POST['serial'];
$nif=$_POST['nif'];
$iphone3g1=$_POST['iphone3g1'];
$iphone3g2=$_POST['iphone3g2'];
$iphone3g3=$_POST['iphone3g3'];
$iphone3g4=$_POST['iphone3g4'];
$iphone3gs1=$_POST['iphone3gs1'];
$iphone3gs2=$_POST['iphone3gs2'];
$iphone3gs3=$_POST['iphone3gs3'];
$iphone3gs4=$_POST['iphone3gs4'];
$iphone41=$_POST['iphone41'];
$iphone42=$_POST['iphone42'];
$iphone43=$_POST['iphone43'];
$iphone44=$_POST['iphone44'];
$total=$_POST['total'];
$valor=$_POST['valor'];
$nome=$_POST['nome'];
$apelido=$_POST['apelido'];
$postal=$_POST['postal'];
$morada=$_POST['morada'];
$notas=$_POST['notas'];

$sql="INSERT INTO clientes (postal, morada, nome, apelido, name, serial, iphone3g1, iphone3g2, iphone3g3, iphone3g4, total, valor, iphone3gs1, iphone3gs2, iphone3gs3, iphone3gs4, iphone41, iphone42, iphone43, iphone44, nif, codigounico, Notas)VALUES('$postal', '$morada', '$nome', '$apelido', '$modelo', '$serial', '$iphone3g1', '$iphone3g2', '$iphone3g3', '$iphone3g4', '$total', '$valor', '$iphone3gs1', '$iphone3gs2', '$iphone3gs3', '$iphone3gs4', '$iphone41', '$iphone42', '$iphone43', '$iphone44', '$nif', '$codigounico', '$notas')";
$result=mysql_query($sql);

This is a very dificult code to maintain,

can I make my life easier?

Termininja
  • 6,620
  • 12
  • 48
  • 49
Souza
  • 1,124
  • 5
  • 19
  • 44
  • 8
    Make sure you read about [SQL injection](http://en.wikipedia.org/wiki/SQL_injection) before taking this code any steps further like for example in production. – Darin Dimitrov Feb 27 '11 at 21:13
  • I am aware of SQL Injections, thank you for the waring, i am looking further to improve this part of the code, that is not very coder friendly. Thank you very much for the tip. – Souza Feb 27 '11 at 21:19

8 Answers8

4

To restrict which POST variables you "import", you can do something like:

$post_vars = array('iphone3g1', 'iphone3g2', '...');
foreach($post_vars as $var) {
    $$var = mysql_real_escape_string($_POST[$var]);
}

EDIT: Changed addslashes by mysql_real_escape_string (thanks @Czechnology).

TuteC
  • 4,342
  • 30
  • 40
  • @czechnology: In this case you can alter the statements of TuteC - but the main idea IS recommendable. – Robert Feb 27 '11 at 21:27
  • 2
    I'd recommend the mysql(i)_real_escape_string function instead of add_slashes for database insertion. – Czechnology Feb 27 '11 at 21:28
  • @Robert, you're right, I've overlooked the first array so I took it back and removed my first comment (before you posted yours). – Czechnology Feb 27 '11 at 21:29
  • 1
    There is a shorter way : `$escaped_data = array_map('mysql_real_escape_string', $_POST)`. And, by the way, the code in the answer won't work, the correct code is `foreach($post_vars as &$var)`. – Artefact2 Feb 27 '11 at 22:03
  • With escaped_data you don't filter only variables you are expecting. About the code not working, `$$var` also does the trick. – TuteC Feb 27 '11 at 22:07
  • @Artefact2 the goal is not to escape data but to get a query. – Your Common Sense Feb 27 '11 at 22:20
  • @TuteC, why isn't this working ? `$valor = $_POST['valor']; $post_vars = array('iphone3g1', 'iphone3g2', 'nome', 'iphone41', 'postal', 'apelido'); foreach($post_vars as $var) {    $$var = "'" . mysql_real_escape_string($_POST[$var]). "', "; } $sql = "INSERT INTO clientes (iphone3g1, iphone3g2, nome, iphone41, postal, apelido, valor) VALUES ($var '$valor')"; $query= mysql_query($sql);` – Souza Feb 28 '11 at 04:38
  • If you are tryin g to concatenate the strings in the loop, save it in an auxiliary variable, like so: `[...] $aux = ''; foreach($post_vars as $var) { $aux .= "'" . mysql_real_escape_string($_POST[$var]). "', "; } $sql = "INSERT INTO clientes (iphone3g1, iphone3g2, nome, iphone41, postal, apelido, valor) VALUES ($aux '$valor')"; [...]` – TuteC Feb 28 '11 at 19:07
  • TuteC, Thank You very much for your help, you're really saving my a$$. I have one little problem, arrays normaly only can handle 64 VARCHARS am i correct? – Souza Mar 01 '11 at 04:18
  • Actually, arrays have no limits, so the code is working flawlessly. Having problems with capitol letters inside the array, don't know why but i'll figure it out. Thank you very much and to the others who tried to help. – Souza Mar 02 '11 at 17:22
4

The issue I see is repetition of the same names four times over. This is how I would reduce it to two occurrences (you could drop it to one with more finagling).

$sql = 'INSERT INTO clientes (postal, morada, nome, apelido, name, serial, iphone3g1, iphone3g2, iphone3g3, iphone3g4, total, valor, iphone3gs1, iphone3gs2, iphone3gs3, iphone3gs4, iphone41, iphone42, iphone43, iphone44, nif, codigounico, Notas) VALUES(:postal, :morada, :nome, :apelido, :modelo, :serial, :iphone3g1, :iphone3g2, :iphone3g3, :iphone3g4, :total, :valor, :iphone3gs1, :iphone3gs2, :iphone3gs3, :iphone3gs4, :iphone41, :iphone42, :iphone43, :iphone44, :nif, :codigounico, :notas)';

preg_match_all('/:(\w+)/', $sql, $inputKeys);
$tokens = $inputKeys[0];
$values = array_map($inputKeys[1], function($k){
    return mysql_real_escape_string($_POST[$k]);
});
$sql = str_replace($tokens, $values, $sql);
$result = mysql_query($sql);

Depending on how you want to separate your logic, a reversed approach might be more useful, where you would specify the array of key names and iterate over that to generate the SQL string.

<?php

$inputKeys = array('postal', 'morada', 'nome', 'apelido', 'name', 'serial', 'iphone3g1', 'iphone3g2', 'iphone3g3', 'iphone3g4', 'total', 'valor', 'iphone3gs1', 'iphone3gs2', 'iphone3gs3', 'iphone3gs4', 'iphone41', 'iphone42', 'iphone43', 'iphone44', 'nif', 'codigounico', 'Notas');

$keyList = '(' . implode(',', $inputKeys) . ')';
$valueList = 'VALUES (';
foreach ($inputKeys as $k) {
    $valueList .= mysql_real_escape_string($_POST[$k]);
    $valueList .= ',';
}
$valueList = rtrim($valueList, ',');
$valueList .= ')';

$sql = 'INSERT INTO clientes '.$keyList.' '.$valueList;
$result = mysql_query($sql);

This approach drops the occurrences of the keys to one and will probably more naturally with your application.

erisco
  • 14,154
  • 2
  • 40
  • 45
  • Erisco, thank you very much for your collaboration/help. What i realized looking at your code is that, the keylist and valuelist must be the same, otherwise it wont write correctly at my clientes table, am i correct? – Souza Feb 27 '11 at 22:00
  • To shorten your code, I abused the fact that the column names in the database are the same as the key names in the POST data. If this is not the convention, then only my first solution will be useful. – erisco Feb 27 '11 at 22:12
  • there is not a single reason not to have the same names. – Your Common Sense Feb 27 '11 at 22:24
2

TuteC had a good aim but failed in details.

It makes me wonder, why noone has a ready made solution, but had to devise it on-the-fly. Nobody faced the same problem before?
And why most people trying to solve only part of the problem, getting variables only.

The goal is not to get variables.
The goal is to get a query. So, get yourself a query.

//quite handy way to define an array, saves you from typing zillion quotes
$fields = explode(" ","postal morada nome apelido name serial iphone3g1 iphone3g2 iphone3g3 iphone3g4 total valor iphone3gs1 iphone3gs2 iphone3gs3 iphone3gs4 iphone41 iphone42 iphone43 iphone44 nif codigounico Notas");

$sql    = "INSERT INTO clientes SET ";
foreach ($fields as $field) {
  if (isset($_POST[$field])) {
    $sql.= "`$field`='".mysql_real_escape_string($_POST[$field])."', ";
  }
}
$sql = substr($set, 0, -2); 

This code will create you a query without boring repeating the same field name many times.

But that's still not all improvements you can make.
A really neat thing is called a function.

function dbSet($fields) {
  $set    = '';
  foreach ($fields as $field) {
    if (isset($_POST[$field])) {
      $set.="`$field`='".mysql_real_escape_string($_POST[$field])."', ";
    }
  }
  return substr($set, 0, -2); 
}

put this function into your code library being included into all your scripts (you have one, don't you?) and then use it for both insert and update queries:

$_POST['codigounico'] = md5(uniqid(rand()));//a little hack to add custom field(s)
if ($action=="update") {
  $id  = intval($_POST['id']);
  $sql = "UPDATE $table SET ".dbSet($fields)." WHERE id = $id";
}
if ($action=="insert") {
  $sql = "INSERT $table SET ".dbSet($fields);
}

So, your code become extremely short and reliable and even reusable.
The only thing you have to change to handle another table is $fields array.

It seems your database is not well planned as it contains seemingly repetitive fields (iphone*). You have to normalize your database.

The same approach to use with prepared statements can be found in this my question: Insert/update helper function using PDO

Community
  • 1
  • 1
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
1

You could use a rather ugly part of PHP called variable variables, but it is generally considered a poor coding practice. You could include your database escaping at the same time. The code would look something like:

foreach($_POST as $key => $value){
    $$key = mysql_real_escape_string($value);
}

The variable variables manual section says they do not work with superglobals like $_PATH, but I think it may work in this case. I am not somewhere where I can test right now.

TheJubilex
  • 409
  • 3
  • 9
0
$set = array();
$keys = array('forename', 'surname', 'email');
foreach($keys as $val) {
  $safe_value = mysqli_escape_string($db, $_POST[$val]);
  array_push($set, "$val='$safe_value'");
}
$set_query = implode(',', $set);

Then make your MySQL query something like UPDATE table SET $set_query WHERE... or INSERT INTO table SET $set_query.


If you need to validate, trim, etc, do it before the above code like this:

$_POST["surname"] = trim($_POST["surname"];
rybo111
  • 12,240
  • 4
  • 61
  • 70
0

PHP: extract

Be careful though and make sure you clean the data before using it.

Shad
  • 15,134
  • 2
  • 22
  • 34
  • 2
    From php manual: `Do not use extract() on untrusted data, like user input (i.e. $_GET, $_FILES, etc.). If you do, for example if you want to run old code that relies on register_globals temporarily, make sure you use one of the non-overwriting extract_type values such as EXTR_SKIP and be aware that you should extract in the same order that's defined in variables_order within the php.ini.` – Czechnology Feb 27 '11 at 21:21
  • 1
    =( I said `Be careful`. Just because something is not recommended doesn't mean it can't be done responsibly. – Shad Feb 27 '11 at 21:45
-1

Actually, you could make your life easier by making your code a bit more complicated - escape the input before inserting into the database!

$sql = 
  "INSERT INTO clientes SET
    "postal = '" . mysql_real_escape_string($_POST['postal']) . "', ".
    "morada = '" . mysql_real_escape_string($_POST['morada']) . "', ".
    ...
Czechnology
  • 14,832
  • 10
  • 62
  • 88
  • Wasn't me downvoting :), i am thankful for your hint, more secure is better than more organized code – Souza Feb 27 '11 at 21:32
  • @Souza, it wasn't meant against you. ** I don't think longer code == unorganized code. If you use spaces or tabs to align corresponding things in the code I've presented, the code is easy to read and follow. And the next programmer who comes after you can easily see which variables are put where. It's also easier to make small adjustments (`intval`, `trim` etc) to such code compared to automated loops. – Czechnology Feb 27 '11 at 21:36
  • what about to make it BOTH more secure and better organized? – Your Common Sense Feb 27 '11 at 22:40
-1

First, I recommend you to create a key-value array like this:

$newClient = array(
  'codigounico' => md5(uniqid(rand())),
  'postal'      => $_POST['postal'],
  'modelo'      => $_POST['selectName'],
  ...
);

In this array key is the column name your MySQL table. In the code you've provided not every field is copied right from POST array (some are calculated, and some keys of the POST aren't equal with the tables column names), so you should use a flexible method. You should still specify all columns and values but only once so code is still maintainable and you won't have any security errors if someone sends you a broken POST. As for me it looks more like configuration than coding.

Then I recommend you to write a function similar to this:

function buildInsertQuery($tableName, $keyValue) {
  $result = '';
  if (!empty($keyValue)) {
    $delimiter = ', ';
    $columns = '';
    $values = '';
    foreach ($keyValue as $key => $value) {
      $columns .= $key . $delimiter;
      $values .= mysql_real_escape_string($value) . $delimiter;
    }
    $columns = substr($columns, 0, -length($delimiter));
    $values = substr($values, 0, -length($delimiter));
    $result = 'INSERT INTO `' . $tableName . '` (' . $columns . ') VALUES (' . $values . ')';
  }
  return $result;
}

And then you can simply build your query with just one function call:

$query = buildInsertQuery('clientes', $newClient);
Misha Karpenko
  • 2,168
  • 17
  • 18
  • the OP asked how to reduce his code, but your approach leaves ugliest part (variable assignment) intact. – Your Common Sense Feb 27 '11 at 22:22
  • Please have a look at Souza's code again. How would you handle the situation like this: $_POST['selectName'] should be inserted as 'modelo' column name? – Misha Karpenko Feb 27 '11 at 22:27
  • Also the OP didn't ask how to reduce his code but how to make it more maintainable. – Misha Karpenko Feb 27 '11 at 22:29
  • either manually rewrite $_POST element or give a proper name to the form input – Your Common Sense Feb 27 '11 at 22:29
  • And if someone changes manually his POST request the database query will fail if you somehow won't filter what column names are set. This means that you have to specify the list of columns. – Misha Karpenko Feb 27 '11 at 22:32
  • your code doesn't make his life easier. Your answer is **seemingly** sort but it's only because of trick to cut down $newClient array. If you bother to write it full, you will see that your code would be as ugly as initial one. While my answer is shorter and it covers WHOLE task. – Your Common Sense Feb 27 '11 at 22:33
  • there is a way to specify list of columns, more reliable one – Your Common Sense Feb 27 '11 at 22:34
  • First, I really doubt that exploding a string by spaces is a good way to specify the list. It is hard to find a specific parameter, to comment one parameter in your IDE by a hotkey etc. Second, what if user will need to add some specific validating logic for 5 or 10 of POST parameters? He will need to write "little hacks" for each of them. Hacks in order to make code shorter don't make it maintainable. – Misha Karpenko Feb 27 '11 at 22:46
  • LOL, explode is a least part of my code :) you can use whatever you wish. However, it's really handy, saving you from typing zillion quotes. Validation will produce another array, which you can use instead of $_POST - not a big deal. – Your Common Sense Feb 28 '11 at 01:59