Fun with POST forms/prepared statements

I’ve been wrestling with this since last night and I’m finally ready to give up and see if someone can tell me what I’m doing wrong.

[php] // MySQL Connect
$link = new mysqli(“localhost”,“username”,“password”,“database”);
// Find total number of kittens
$count = mysqli_fetch_array(mysqli_query($link,'SELECT max(id) AS max FROM kittens'));

// Pick kittens
$kitten_one_id = mt_rand(1, $count['max']);
$kitten_two_id = mt_rand(1, $count['max']);

while($kitten_one_id == $kitten_two_id) {
	$kitten_two_id = mt_rand(1, $count['max']); // Prevent duplicates
}

// Get kittens from kitten IDs
$result = mysqli_query($link,"select * from kittens where kittens.id = $kitten_one_id UNION select * from kittens where kittens.id = $kitten_two_id");
if(mysqli_num_rows($result) == 2) {
$kitten_one = mysqli_fetch_array($result);
$kitten_two = mysqli_fetch_array($result);

echo "<table border='1' cellspacing='5' cellpadding='5'><tr><th scope='row'>Name:</th>";
echo "<td>".$kitten_one['name']."</td><td>".$kitten_two['name']."</td></tr><tr><th scope='row'>Album:</th><td>".$kitten_one['album']."</td><td>".$kitten_two['album'].""; 
echo "</td></tr><tr><th scope='row'>Summary:</th><td>".$kitten_one['summary']."</td><td>".$kitten_two['summary']."</td></tr><tr><th scope='row'>Quote(s):</th><td>".$kitten_one['quotes']."</td><td>".$kitten_two['quotes']."</td></tr>
<tr><th scope='row'>Elo Rating:</th><td>".$kitten_one['elo']."</td><td>".$kitten_two['elo']."</td></tr></table>";
echo '<div class="buttons">';
echo '<form method="post"><input name="0" type="submit" value="Vote 4 ', $kitten_one['name'], '"></form>';
echo '<form method="post"><input name="1" type="submit" value="Vote 4 ', $kitten_two['name'], '"></form>';
echo '</div>'; } else {
	die('Error in the matrix!');
}

$game = $link->prepare("INSERT INTO games (kitten_one, kitten_two, winner, ip) VALUES (?, ?, ?, ?)"); 
$game->bind_param('iiis', $kitten_one_id, $kitten_two_id, $winner, $ip);
if (isset($_POST['0']) || isset($_POST['1'])) {
    if (isset($_POST['0'])) $winner = $kitten_one_id; else $winner = $kitten_two_id;
    $ip = $_SERVER['REMOTE_ADDR'];
	$game->execute();
    $game->close(); 
    $link->close(); 
}[/php]

It’s not easy to describe it, but what basically happens when I first load the page is that it generates the ‘kittens’ as you would expect. Pushing one of the form buttons doesn’t vote for one of them, though, but rather one of the ‘kittens’ that immediately comes up on the screen after. In other words, it does everything as it ought to, but it uses the new kittens that are generated after the form is sent and the page is refreshed instead of the ones I thought the code was supplying.

Incidentally, this is my first foray into the world of prepared statements, as I’ve been working to convert mysql_ code from 2012 (actually, the script that someone here graciously provided the last time I was here) to something I can feel reasonably safe using today. I hope that this code is if nothing else, reasonably secure, but it’s quite possible there are still obvious holes for SQL injection or something.

Thanks for your time!

Web servers are stateless. When the server is done processing one request, all the resources used for that request are destroyed.

When you first request the page and it generates the random numbers, those numbers won’t exist after the end of the current request. The next request, when the form gets submitted, calculate new random numbers.

To add ‘state’ to what is a stateless process, you need to use session variables to remember the values. Add a session_start() statement near the top of your code, before you output anything to the browser. You would store the random numbers in session variables. In the form processing code, you would use the session variables.

The post method form processing code needs to be near the top of your code, above the start of the html document. Besides allowing you to do a header() redirect upon successful completion of the form processing code, this allows you to display any validation errors when you (re)display the form and allows any data that changed as a result of the form processing code to be displayed when the page is displayed.

Also, you shouldn’t be using a number as the form field names (not sure if this is a violation of a W3 standard), but form field names should be descriptive, so that the code is easily readable and understandably by anyone, You should be using an associate name for each form field, then test for that name in the form processing code.

Sponsor our Newsletter | Privacy Policy | Terms of Service