A Simple PHP Polling/Voting System
Create an IP-logging PHP Polling or Voting System in PHP 5.0
Introduction
This article will illustrate how to create a very simple, clean polling or voting system using PHP 5.0 and the DOMDocument object model. This allows the polling system to read and write polling results to XML, instead of say, a MySQL database like many conventional polling systems.
Background
I run a local music website, where, each month, I wanted to have my members vote on their favorite local band. The problem began with the fact that Joomla's default polling application logged IPs in cookies. So, all a person had to do was delete their cookies, and they could vote again. This was a very unfair system, and allowed for stuffing of the ballot boxes. So, I began combing the web in search of a third party component that could log IPs, and keep people from voting more than once a day.
After looking around for quite a while on the internet, I just couldn't find any solid component that would allow me to have 1) more than 10 voting items, and 2) log IPs to a database or a file to ensure people couldn't vote more than once a day. Those ones I could find that did what I needed were either incredibly slow, required an email sign-up (read: add me to a spam list), or, had all sorts of popup ads associated with their component.
Much chagrinned, I opted to knock the rust off of my PHP skills, and write my own. Now, those of you who have read my last articles realize that I'm a Microsoft guy, specializing in ASP.NET and C#. NOT PHP! But, I do have some experience with the language (and yes, I really do enjoy the simplicity/power of PHP), so, when reading this, please keep in mind that I am by no means a subject matter expert on PHP!
This poll is simple to deploy and configure, and hopefully will provide some level of use to you folks out there who just want to get a secure poll up and running in a short period of time!
Getting Started
Fortunately enough, all you need to run this polling system is PHP 5.0. This is a requirement, since the DOMDocument object is new to PHP 5. You do not need any sort of database system as this polling system utilizes simple file IO and XML.
If you want to skip the tutorial, I'll give you quickstart instructions below. If you want to know what's exactly going on in the program, keep reading!
Quick Installation
- Create a directory for the poll to reside. This can be your root web directory if so desired.
- Unzip the files included in the .zip file to the above directory.
- CHMOD the addresses.xml and results.xml to 777 (in the /xml directory)
- Open the results.xml file, and add your polling question, and an entry for each voting item.
- Set the vote counts in the results.xml file to zero (or, whatever you'd like to start at).
- Optional: Modify the poll.css file to fit your website's theme.
- Access the poll at http://www.yoursite.com/yourpolldirectory/poll.php
- That's it! Please note that there should be at least 1 address entry in the addresses.xml file (loopback of 127.0.0.1 is fine)
Once you access your poll, the poll.php screen will look something like the following:
After you vote, the results screen will look something like the following:
Overview
The PHP Polling system is simple. It has 4 php files, 2 xml files, and a stylesheet. Technically, the user will only see two of those pages. They are outlined below.
- Poll.php
- Calls loadpoll.php to display the poll options
- Results.php
- Calls savevote.php to display the poll results
Loadpoll.php handles the display of the poll. How it handles this is simple.
- Uses DOMDocument to load the results.xml file
- Loops through all of the pollitem nodes to get the entries for the poll
- Creates a radiobutton control for each item, and assigns the value to the particular entry
$doc = new DOMDocument();$doc->load("xml/results.xml");$root = $doc->getElementsByTagName("results")->item(0);$question = $root->getAttribute("question");$pollitems = $doc->getElementsByTagName("pollitem");foreach( $pollitems as $pollitem ){//Create a radio button...}
We are basically just using the methods available to us in the DOMDocument object to loop through the nodes then emitting some HTML to account for our radiobutton controls. The last bit is to set a piece of JavaScript on the onclick event of the radiobutton. What this does is populate a hidden input field's text with the value of the vote. In this way, we can have a simple JavaScript confirm() dialog to ensure we have a value set. If not, we won't submit a vote. Once we set that value, and click "vote", the value of the form (action="savevote.php") posts the hidden value to savevote.php. The JavaScript functions are shown below.
function setVote(voteName){document.getElementById("votefor").value = voteName;}function confirmSubmit(){if (document.getElementById("votefor").value == ""){var agree=confirm("Please select an entry before voting.");return false;}}
In PHP, POST and GET variables are loaded into the $_REQUEST global. So, in savevote.php we'll request the "votefor" value, which we've set in the poll.php page.
$votefor = $_REQUEST["votefor"];
Savevote.php is the largest file, and does the most work. The order of operations is outlined below:
- Request the value of "votefor"
- If we have a null value for $votefor, we just display the results.
- Get the IP of the submitter using the $_SERVER["REMOTE_ADDR"] global
- Load the addresses.xml file, and loop through to see if we have a matching IP address
- If we have a match, we check the date of the last vote
- If the date is today, we do not count the vote (user already voted)
- If it's not today, we add one to the vote count (in results.xml), and set the last voted date to today
- If we don't have a match, we add the new address node with the IP and today's date
- Now that we've counted the vote (or not, as the case may be), we display our results.
Getting Fancy
Displaying the results could have been simple. A single line item with "Entry: xx votes" would have more than sufficed. But, I wanted to get clever. I like the idea of displaying a line graph, with the percentage of the votes inside the line graph. Now, you don't have to do this, but if you're interested in how this is done, read on.
Displaying a Line Graph
The first thing we'll need for the graph is the total number of votes. In the code, I get it like so:
// Get max vote count$doc = new DOMDocument();$doc->load("xml/results.xml");$maxvotes = 0;$pollitems = $doc->getElementsByTagName("pollitem");foreach( $pollitems as $pollitem ){$votes = $pollitem->getElementsByTagName("votes");$vote = $votes->item(0)->nodeValue;$maxvotes = $maxvotes + $vote;}
Now that we have the max, we'll loop back through the results.xml file to calculate the percentages.
// Generate the results table$doc = new DOMDocument();$doc->load("xml/results.xml");$pollitems = $doc->getElementsByTagName("pollitem");foreach( $pollitems as $pollitem ){$entries = $pollitem->getElementsByTagName("entryname");$entry = $entries->item(0)->nodeValue;$votes = $pollitem->getElementsByTagName("votes");$vote = $votes->item(0)->nodeValue;$tempWidth = $vote / $maxvotes;$tempWidth = 300 * $tempWidth;$votepct = round(($vote / $maxvotes) * 100);echo "<tr><td width=\"30%\" class=\"polls\">$entry</td>";echo "<td width=\"50%\" class=\"resultbar\"><div class=\"bar\"style=\"background-color: ";getRandomColor();echo "; width: $tempWidth px;\">$votepct%</div></td><td width=\"20%\"($vote votes)</td></tr>";}
The way I did this simply was to create a table with three columns. The first column will hold the entry name, the second column will have the line graph with the percentage inside, and the last column will hold the total number of votes.
Creating the Graph
All I did to create the graph was to create a DIV object for each entry. I went on the basis that the max width of the DIV would be 300px. You can set this to whatever you like, just make sure it'll fit in the table and the page! So, to get the width of what the DIV should be, we'll just take the number of votes ($vote), divide it by the maximum count of votes ($maxvote), then multiply that percent value by 300. So, let's say we had 30 votes out of 100, 30/100 = .3, .3 * 300 = 90, the DIV would need to be 90 pixels wide. Then, to get the percentage (to put in the middle of the DIV), we just take $vote/$maxvote, round the value, and multiply by 100. Using the above example, this would give us 30%.
Coloring the Graph
Now, when we create each DIV, we know the width to make each one, and the %age value to put in the middle of the DIV. I thought it would be cool to create separate colors for each div, so I wrote a getRandomColor() function, and set the DIV's background-color style to whatever was returned by that function.
// Returns a random RGB color (used to color the vote bars)function getRandomColor(){$r = rand(128,255);$g = rand(128,255);$b = rand(128,255);$color = dechex($r) . dechex($g) . dechex($b);echo "$color";}
All we're doing now is getting a random value for the Red ($r), Green ($g) and Blue ($b) values using the PHP rand() function, then converting the decimal value to hex using the dechex() function. This will return us a color value formatted to hexadecimal, which the background-color will like.
Conclusion
So, in a couple of quick files, you can have a customized polling system on your own PHP 5.0 enabled web server. I was able to get this to execute in both IIS 5.1 and Apache without any problems. The nice part is that you don't need a database to house the logged IPs. In my experience, this system runs extremely fast using the DOMDocument object in PHP 5.
Comments
Post a Comment