Send a small option identifier rather than trusting arbitrary option text from the browser. Validate the choice, verify the poll exists, and insert the vote with a prepared statement.
<?php
$opt = trim((string) ($_POST['opt'] ?? ''));
$qstId = filter_input(INPUT_POST, 'qst_id', FILTER_VALIDATE_INT);
if ($opt === '' || $qstId === false || $qstId === null) {
http_response_code(400);
exit('Please select one option and submit the form again.');
}
$pollStmt = $dbo->prepare(
'SELECT opt1, opt2, opt3, opt4 FROM plus_poll WHERE qst_id = :qst_id'
);
$pollStmt->execute([':qst_id' => $qstId]);
$poll = $pollStmt->fetch(PDO::FETCH_ASSOC);
$allowed = $poll ? [$poll['opt1'], $poll['opt2'], $poll['opt3'], $poll['opt4']] : [];
if (!in_array($opt, $allowed, true)) {
http_response_code(400);
exit('Invalid poll option.');
}
?>
For a production poll, combine this with CSRF validation and your chosen duplicate-vote policy. When calculating results, prefer aggregate queries such as COUNT(*) ... GROUP BY instead of fetching rows only to count them in PHP.
<?php
session_start();
$sid = session_id();
require '../include/z_db1.php';
$opt = trim((string) ($_POST['opt'] ?? ''));
$qstId = filter_input(INPUT_POST, 'qst_id', FILTER_VALIDATE_INT);
if ($opt === '' || $qstId === false || $qstId === null) {
http_response_code(400);
exit('Please select one option and submit the form again.');
}
$pollStmt = $dbo->prepare(
'SELECT opt1, opt2, opt3, opt4 FROM plus_poll WHERE qst_id = :qst_id'
);
$pollStmt->execute([':qst_id' => $qstId]);
$poll = $pollStmt->fetch(PDO::FETCH_ASSOC);
if (!$poll || !in_array($opt, [$poll['opt1'], $poll['opt2'], $poll['opt3'], $poll['opt4']], true)) {
http_response_code(400);
exit('Invalid poll option.');
}
$duplicateStmt = $dbo->prepare(
'SELECT 1 FROM plus_poll_ans WHERE s_id = :sid AND qst_id = :qst_id LIMIT 1'
);
$duplicateStmt->execute([':sid' => $sid, ':qst_id' => $qstId]);
if ($duplicateStmt->fetchColumn()) {
exit('A vote from this session has already been recorded.');
}
$insertStmt = $dbo->prepare(
'INSERT INTO plus_poll_ans (s_id, qst_id, opt) VALUES (:sid, :qst_id, :opt)'
);
$insertStmt->execute([':sid' => $sid, ':qst_id' => $qstId, ':opt' => $opt]);
header('Location: view_poll_result.php', true, 303);
exit;
?>
We will move to display result page to show the result to the visitor. As we have inserted the selected data to another table, we will collect all the records for which the question id = the current question id. This will display in selecting the result of current poll only. To display past poll results we can change the ID to different values. Let us set the question id to 1 for the first poll result to display. To calculate the percentage of the options selected we will also find out the total number of answers submitted. We will use number format function of PHP to display two decimal places for the figures in percentage. Then we calculate the totals with COUNT(*) and group the choices with GROUP BY.<?php
require '../include/z_db1.php';
$qstId = 1;
$questionStmt = $dbo->prepare('SELECT qst FROM plus_poll WHERE qst_id = :qst_id');
$questionStmt->execute([':qst_id' => $qstId]);
$question = $questionStmt->fetchColumn();
$totalStmt = $dbo->prepare('SELECT COUNT(*) FROM plus_poll_ans WHERE qst_id = :qst_id');
$totalStmt->execute([':qst_id' => $qstId]);
$total = (int) $totalStmt->fetchColumn();
$resultStmt = $dbo->prepare(
'SELECT opt, COUNT(*) AS votes FROM plus_poll_ans WHERE qst_id = :qst_id GROUP BY opt ORDER BY votes DESC, opt'
);
$resultStmt->execute([':qst_id' => $qstId]);
$results = $resultStmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($results as $row) {
$votes = (int) $row['votes'];
$percent = $total > 0 ? ($votes / $total) * 100 : 0;
echo htmlspecialchars((string) $row['opt'], ENT_QUOTES, 'UTF-8') . ': '
. number_format($percent, 2) . "%<br>";
}
?>
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.