?
Solved

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

Posted on 2013-11-27
15
Medium Priority
?
1,724 Views
Last Modified: 2013-11-27
Reference (additional problem found to this question): http://www.experts-exchange.com/Software/Internet_Email/Web_Browsers/Q_28295433.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
0
Comment
Question by:thenelson
  • 7
  • 5
  • 3
15 Comments
 
LVL 45

Expert Comment

by:Chris Stanyon
ID: 39680611
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

0
 
LVL 45

Expert Comment

by:Chris Stanyon
ID: 39680628
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.
0
 
LVL 39

Author Comment

by:thenelson
ID: 39680719
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?
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
LVL 45

Accepted Solution

by:
Chris Stanyon earned 2000 total points
ID: 39680758
There's absolutely no reason to rename your fields - this will create problems later on. Naming your fields as diagnoses[] is the same as naming them diagnoses[0], diagnoses[1], diagnoses[2] etc - it's just easier to leave them out and the browser take care of the indexes.

To get your code to work, just add an extra check in to see if the $value is an array

foreach($_POST as $key => $value):
	if (is_array($value)):
		$postData .= $key.'='.implode(',', $value)."\r\n";
	else:
		$postData .= $key.'='.$value."\r\n";	
	endif;
endforeach;

Open in new window

If the value is an array (such as diagnoses) it will join them all together with the implode function.
0
 
LVL 39

Author Comment

by:thenelson
ID: 39680828
>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.
0
 
LVL 45

Expert Comment

by:Chris Stanyon
ID: 39680900
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!!
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 39680998
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
0
 
LVL 39

Author Closing Comment

by:thenelson
ID: 39681004
That works great!  Thanks!! I learned quite a few things.
0
 
LVL 39

Author Comment

by:thenelson
ID: 39681090
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?
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 39681168
Do you agree?
Let me put it this way: http://xkcd.com/292/
0
 
LVL 39

Author Comment

by:thenelson
ID: 39681248
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.
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 39681271
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

0
 
LVL 45

Expert Comment

by:Chris Stanyon
ID: 39681279
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 :)
0
 
LVL 39

Author Comment

by:thenelson
ID: 39681283
yep, that's what I did.
Thanks again
0
 
LVL 39

Author Comment

by:thenelson
ID: 39681306
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.
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Australian government abolished Visa 457 earlier this April and this article describes how this decision might affect Australian IT scene and IT experts.
When it comes to security, close monitoring is a must. According to WhiteHat Security annual report, a substantial number of all web applications are vulnerable always. Monitis offers a new product - fully-featured Website security monitoring and pr…
The viewer will learn the benefit of using external CSS files and the relationship between class and ID selectors. Create your external css file by saving it as style.css then set up your style tags: (CODE) Reference the nav tag and set your prop…
The viewer will the learn the benefit of plain text editors and code an HTML5 based template for use in further tutorials.
Suggested Courses
Course of the Month13 days, 10 hours left to enroll

750 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question