Link to home
Start Free TrialLog in
Avatar of thenelson
thenelson

asked on

foreach($_POST as $key => $value) not getting every $key

Reference (additional problem found to this question): https://www.experts-exchange.com/questions/28295433/Safari-posting-url-code.html

I have a form which has several fields with the same name. It is posting the following data:
"...Diagnoses=HA&Diagnoses=Migraine&Diagnoses=TBI&Diagnoses=TMJ..."
I am using the following code in the php script which only appends the last Diagnoses field ("TMJ") to the string  $postData:
foreach($_POST as $key => $value) {
    $postData .= $key.'='.$value."\r\n";
}

Open in new window


I would like to continue using fields with the same name because I would like to append those fields together on one line like this:
foreach($_POST as $key => $value) 
{
    If ($key == $peviousKey)
	{
		echo "we are here<br>";
		$postData .= ", ".$value;
	}
	else
	{
		$postData .= "\r\n".$key.'='.$value;
	}
	$peviousKey = $key;
}

Open in new window

var_dump($_POST); displays only the last Diagnosis field ("TMJ") in the array.

I am thinking of renaming the duplicate fields to:
Diagnosis01
Diagnosis02
Diagnosis03
etc
and then use substr ($key, 0, -2) to compare with the $peviousKey and append the "duplicate" $keys together.

Any other ideas on how to handle this.?  TIA
Avatar of Chris Stanyon
Chris Stanyon
Flag of United Kingdom of Great Britain and Northern Ireland image

If you want to name form fields the same then you need to name them as an array.

<input type="text" name="diagnoses[]" />
<input type="text" name="diagnoses[]" />
<input type="text" name="diagnoses[]" />
<input type="text" name="diagnoses[]" />

Open in new window

Then your POST array will contain a key of diagnoses, that is an array:

foreach ($_POST['diagnoses'] as $diag):
    echo $diag;
endforeach;

Open in new window

As a follow on, if you want all the diagnoses in one string, then use the PHP implode function:

$diagnoses = implode(",", $_POST['diagnoses']);

Open in new window

That will give you string with each of the diagnoses, separated by a comma.
Avatar of thenelson
thenelson

ASKER

Chris,
Thanks for your response.

So as in your example, I could use name="diagnoses[]" for each field. I wouldn't need to use:
name="diagnoses[0]"; name="diagnoses[1]"; etc?

Would foreach($_POST as $key => $value) pick up each item in the subarray or would I have to include foreach ($_POST['diagnoses'] as $diag) also?

Using your suggestion, how would I build a string like this:
...
PreviousField=some text
Diagnoses=HA, Migraine, TMJ
PostField=some other text
...

I am thinking of renaming the duplicate fields to: "Diagnoses01,"; "Diagnoses02,"; etc. where the comma (or colon, semicolon, period, etc) at the end communicates the separator to use and then use this code to build my output:
foreach($_POST as $key => $value) 
{
    If (substr ($key, 0, -3) == $peviousKey)
	{
		$postData .= substr ($key, 0, -1).$value;
	}
	else
	{
		$postData .= "\r\n".$key.'='.$value;
	}
	$peviousKey = substr ($key, 0, -3);
}
//remove first "\r\n"
$postData = substr ($postData,2);

Open in new window

Do you see a problem with this?
ASKER CERTIFIED SOLUTION
Avatar of Chris Stanyon
Chris Stanyon
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>There's absolutely no reason to rename your fields
I am going to have to rename the Diagnoses fields to Diagnoses[]. Yes?

The code you suggested will work with multiple subarrays?  It will keep the different subarrays separate? For example:
<input type="text" name="diagnoses[]" />
<input type="text" name="diagnoses[]" />
<input type="text" name="diagnoses[]" />
<input type="text" name="diagnoses[]" />
<input type="text" name="symptom[]" />
<input type="text" name="symptom[]" />
<input type="text" name="surgery[]" />
<input type="text" name="surgery[]" />

Open in new window

>There's absolutely no reason to rename your fields - this will create problems later on.
I like your solution better than mine but out of curiosity, what problems could I expect later on. I realize I would need to be careful to avoid field names that are the same except the last three characters but anything other than that?

I didn't know that php had an alternate if:-endif;    foreach:-endforeach; syntax as opposed to using curly braces. Good to know.
OK. You're not renaming your fields - your just turning them into arrays by adding the square brackets - they'll still be called diagnoses, but instead of being a single value, it will be passed as an array.

In the sample HTML you've provided, your POST array will contains 3 keys - diagnoses, symptom, surgery and each of these keys will be an array containing the values.

When I say it will cause problems later on, there are a couple of things to bear in mind. The more complicated the code, the more likely there is for a typo. In your first post you mentioned calling your fields Diagnoses01, Diagnoses02 etc, and then in the post above you mention about the last 3 characters being the same - I'm guessing you meant the last 2 characters, but already inconsistency is creeping in.

The code you've posted assumes that all the diagnosis fields will be kept together, so you are relying on the $previousKey value to group them. What if you need to re-design your form and you need something like:

<input type="text" name="diagnoses[]" />
<input type="text" name="symptom[]" />
<input type="text" name="diagnoses[]" />
<input type="text" name="symptom[]" />
<input type="text" name="diagnoses[]" />
<input type="text" name="symptom[]" />

Your code won't work.

There are plenty of other examples of why you shouldn't do it your way, but it comes down to this: it's just not considered best-practice!

As for the alternative PHP control blocks - I find it easier to use these when you have lots of nested blocks - if statements inside of while loops inside of foreach loops etc - it's very easy to lose track of the brackets and your code ends up with 7 closing brackets and no indication of what they're closing!!
ChrisStanyon has given you excellent advice here.  

I would just like to add a security note.  Never iterate over the $_POST array (or any external data, for that matter).  Your script should know what elements are expected in the request and should only process those elements.  This is a security issue that goes to the heart of "accept only known good values."  Since the $_POST array is created by PHP from external data, it is by definition tainted.  Therefore you cannot know that it contains good or bad values until you filter the external data.  An example of why this matters is in AntiPHPractice Number 18.

See http://www.laprbass.com/RAY_temp_thenelson.php

<?php // RAY_temp_thenelson.php
error_reporting(E_ALL);


// DEMONSTRATE HOW TO HANDLE EXTERNAL FORM DATA
// SEE http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28305093.html


// THESE ARE THE ACCEPTABLE COLORS
$colors = array
( 'red'
, 'green'
, 'blue'
)
;

// IF THERE IS A POST-METHOD REQUEST TO SET THE COLOR
if (!empty($_POST['color']))
{
    // CHECK THAT THE POST DATA MATCHES THE FORM DATA
    if (in_array($_POST['color'], $colors))
    {
        // NORMALIZE / FILTER THE EXTERNAL DATA HERE
        $color = strtolower($_POST['color']);
        $color = ucFirst($color);
        echo PHP_EOL . "THE COLOR IS $color";
    }
    else
    {
        trigger_error('SCRIPT UNDER ATTACK', E_USER_ERROR);
    }
}

// CREATE THE RADIO BUTTONS FOR THE COLORS
$radios = NULL;
foreach ($colors as $rgb)
{
    $radios .= '<input type="radio" name="color" value="' . $rgb . '" />' . "$rgb<br>";
}

// CREATE THE FORM USING HEREDOC SYNTAX
$form = <<<FORM
<form method="post">
$radios
<input type="submit" />
</form>
FORM;

echo $form;

Open in new window

Best regards, ~Ray
That works great!  Thanks!! I learned quite a few things.
Ray,

Thanks for your input.  I can see what you are saying: that anyone can post data to my php script and this could be dangerous. I can see how important this would be if the script acts on the data. In this case, the only thing I am doing is building a string and writing that to a text file which I then view. So I don't think anything bad would happen if someone sent me bogus data other than I would see garbage and not use it.

Do you agree?
Do you agree?
Let me put it this way: http://xkcd.com/292/
A followup:
Is there an easy way to not include empty array elements using
if (is_array($value)):
		$postData .= $key.'='.implode(', ', $value).".\r\n";

Open in new window


I am getting:
     1986, fusion, , , , , 1996, esi, , , , .
I would like to eliminate the multiple commas.
Sure, just replace contiguous commas with single commas.  Something like this.  You'll have to experiment with it some to get the regular expression exact for your work.

<?php 
$rgx = '#,,+#';
$dat = '1986, fusion,,,,,1996,esi,,,';
echo preg_replace($rgx, ',', $dat);

Open in new window

Sure - just run $value through array_filter();

if (is_array($value)):
    $value = array_filter($value);
    $postData .= $key . '=' . implode(', ', $value) . PHP_EOL;

Open in new window

You might also want to use the PHP_EOL constant instead of the \r\n - again, good practice :)
yep, that's what I did.
Thanks again
What I tried first was similar to Ray's suggestion. I used str_replace to remove the multiple commas. That worked.

Chris' suggestion works too and i believe is more elegant.

Thanks again.