We’d like to be able to make our poll data available to the public. We can do this by reading our file and collating the data. When you have lots of similar data, you will often store this data in a container called an array. For example, if you want to keep a list of colors, you might have an array that contains “red”, “blue”, “green”, “cyan”, “yellow”, and “magenta”. The simplest arrays are just lists of values.
Arrays can also be more complex. For example, you might have a list of employee identification numbers and employee names and phone numbers. If so, you could take the employee identification number and use that to look up an employee’s name and the employee’s phone number.
What we’re going to do is first read the entire file into a simple array that lists every single line in the file. Then we’re going to ask PHP to count all of the similar entries. PHP will return that count in a slightly more complex array that corresponds each unique entry with the number of times that entry appears
If fourteen people have voted for the Tin Man and three people have voted for Neo, the simple array will contain fourteen tinmans and three neos. The count will contain “tinman” and correspond that to “14”, and one “neo” that corresponds to “3”.
Add the following code to the bottom of your web page, after the form but before the </body>:
<?
//get all of our votes
$votes = file($filename);
$votecounts = array_count_values($votes);
FOREACH ($votecounts as $choice => $count):
echo "<p>$choice received $count vote(s).</p>\n";
ENDFOREACH;
?>
First, we read our file using the “file” command. It takes every line of the file and places each line as an entry in the array $votes. We then use the function array_count_values() to count the items in the $votes array. That result is stored in $votecounts.
The foreach structure is a lot like the if structure. Like if, everything between the FOREACH and the ENDFOREACH are performed while the foreach is valid. Unlike the if, however, the lines enclosed by a foreach block can, and usually are, performed more than once. PHP goes through those lines once for every entry in the array. If there are five items in the array, PHP will use those lines five times. In between the parentheses, we give foreach the array and the name of the container(s) that should contain the current array item each time we go through the “loop”.
In the $votecounts array, each item is composed of two parts: the choice, and the count of how many times that choice was chosen. So when we say “$votecounts as $choice => $count”, we’re telling PHP to give us the first part of the array’s item in the container $choice and the second part in the container $count. If there were only one part (such as the $votes array), we would use something like “$votes as $choice”.
The above code works. It accepts votes and it shows the results of the votes. But the results look pretty ugly. We’re showing the viewer our internal representation of the votes, not the human representation. Instead of displaying “The White Rabbit” for example, we’re displaying “rabbit”. We need to create an array that stores our internal representation and corresponds it to our English representation.
Go to the top of your poll page and, as the first line directly after the “<?”, add one line:
$pollchoices = array(
"rabbit"=>"The White Rabbit",
"scarecrow"=>"The Scarecrow",
"tinman"=>"The Tin Man",
"neo"=>"Neo",
);
This is an array that corresponds our internal code (such as “rabbit”) with the full name of that choice (such as “The White Rabbit”). The left side of each item=>item pair is the “index” of that pair. It can be used to look up the right side of the pair.
Change the inside of our foreach to:
$name = $pollchoices[$choice];
echo "<p>$name received $count vote(s).</p>\n";
You will probably end up seeing something like:
received 1 vote(s).
received 2 vote(s).
received 2 vote(s).
Which is not exactly what we want. The problem is that each of the lines in our file contains a carriage return. When file() reads the lines into our array, it does not delete the carriage returns. Our array of real names does not include the carriage returns, and we probably don’t want it to. So we need to get rid of the carriage returns. There is a command called “trim()” that does this for us. Change the “$name =” line to:
$name = $pollchoices[trim($choice)];
You should now see votes with real names:
The Tin Man received 1 vote(s).
Neo received 2 vote(s).
The White Rabbit received 2 vote(s).
Let’s say we want to add a new choice. We currently need to do this in two places. Once in our form, and once in our $pollchoices array. Now that we have $pollchoices, we can use that to create our form’s option lines. Replace all of the option lines with:
<option value="">Choose:</option>
<?
FOREACH ($pollchoices as $code => $name):
echo "<option value=\"$code\">$name</option>\n";
ENDFOREACH;
?>
Those backslashes (“\”) are very important. So far, whenever we’ve told PHP to display some text, we’ve surrounded that text with double quotes. But here, one of the things we want PHP to display are double quotes! If you simply put the quotes there along, PHP would get confused and think we wanted to end the text. By “backquoting” the double quotes, PHP knows that we want those quotes displayed, and that they do not end the string of text. (You can also backslash backslashes if you need to display a backslash.)
Finally, go ahead and add “"lion"=>"The Cowardly Lion"” to the list of items in $pollchoices.
Remember what we said about trusting data from outside of our PHP code? We’re doing it again. They’re choosing a selection and we’re writing that selection to a file. Right? Not at all. They are sending us text, and we are writing whatever they send us to a file. If they want to write things to our file other than what is in that list, our form tells them exactly how to do it. That’s not good.
Now that we are constructing our selections via a list, we know exactly what options they are authorized to choose. We can check to make sure that the text they sent us is one of the options in our list. Let’s add another “if” under the if that sets the “$choice” container:
IF ($choice = $_POST["imagine"]):
//make sure that they sent us a valid choice
IF ($pollchoices[$choice]):
//yes, this choice exists
//let’s append this to a file
IF ($choicefile = fopen($filename, "a")):
fwrite($choicefile, "$choice\n");
fclose($choicefile);
ELSE:
echo "<p><b>Unable to append to file choices.</b></p>\n";
ENDIF;
ENDIF;
ENDIF;
You can sort arrays as well, using “asort()” and “arsort()”. The first sorts in ascending order, the second in descending (reverse) order. Why not show our results in order from the most popular on down? In front of your foreach where you display the poll’s results, add:
arsort($votecounts);
Our display will now automatically adjust itself according to who is winning the poll.