Prevent user from voting twice with cookie

Hi there, im hoping someone can help me with this. What i’m trying to do is prevent users from voting twice with cookies, ive tried to set several myself but none of them have worked. Any help would be amazing. Thanks.


 <?php
include 'functions.php';
// Connect to MySQL
$pdo = pdo_connect_mysql();



// If the GET request "id" exists (poll id)...
if (isset($_GET['id'])) {
    // MySQL query that selects the poll records by the GET request "id"
    $stmt = $pdo->prepare('SELECT * FROM polls WHERE id = ?');
    $stmt->execute([$_GET['id']]);

    // Fetch the record
    $poll = $stmt->fetch(PDO::FETCH_ASSOC);
    // Check if the poll record exists with the id specified
    if ($poll) {
        // MySQL query that selects all the poll answers
        $stmt = $pdo->prepare('SELECT * FROM poll_answers WHERE poll_id = ?');
        $stmt->execute([$_GET['id']]);
        // Fetch all the poll anwsers
        $poll_answers = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // If the user clicked the "Vote" button...
        if (isset($_POST['poll_answer'])) {
            // Update and increase the vote for the answer the user voted for
            $stmt = $pdo->prepare('UPDATE poll_answers SET votes = votes  +1  WHERE id = ?');
            $stmt->execute([$_POST['poll_answer']]);

           
            // Redirect user to the result page
            header ('Location: result.php?id=' . $_GET['id']);
            exit;
        }
    } else {
        die ('Poll with that ID does not exist.');
    }
} else {
    die ('No poll ID specified.');
}


?>

<?=template_header('Poll Vote')?>

<div class="content poll-vote">
    <h2><?=$poll['title']?></h2>
    <p><?=$poll['des']?></p>
    <form action="vote.php?id=<?=$_GET['id']?>" onSubmit="disable()"  method="post">
        <?php for ($i = 0; $i < count($poll_answers); $i++): ?>
        <label>
            <input type="radio" name="poll_answer" value="<?=$poll_answers[$i]['id']?>"<?=$i == 0 ? ' checked' : ''?>>
            <?=$poll_answers[$i]['title']?>
        </label>
        <?php endfor; ?>
        <div>
            <input type="submit" name="submit" value="Vote">
            <a href="result.php?id=<?=$poll['id']?>">View Result</a>
        </div>
    </form>
</div>

<?=template_footer()?>

I don’t see any of the cookie code in there. Maybe best to show the code you’ve tried.

Do you want to block / allow the poll in its entirety, or on a per-answer basis? That is, could the user answer five out of ten poll questions, then come back later and answer the other five, or is it all or nothing?

On a quick scan, I can’t see where the user is identified, or are you not concerned with that? What happens if the user clears their cookies, is it OK then for them to vote again?

Hi there, this is the code i have tried to make it work with. it did set but im not exactly sure how to make it so the user only votes once, and to asnwer your question. its an all or nothing thing. just one vote then you’re done. Also no, it does not bother me if they refresh their cookies, they can revote again if they do that. This has no real use case. Thankyou.

$cookie_name = “id”;
$cookie_value =“id”;
setcookie($cookie_name, $cookie_value time() + (86400 * 30), “/”); // 86400 = 1 day
?>

> <?php
> if(!isset($_COOKIE[$id])) {
>   echo "Cookie named '" . $id . "' is not set!";
> } else {
>   echo "Cookie '" . $cookie_name . "' is set!<br>";
>   echo "Value is: " . $_COOKIE[$id];
> }

Those aren’t the same, though - you set a cookie with the name “id”, but you check for the cookie $id, i.e. your poll ID.

Presuming that’s a typo and you are checking the same as you are setting, all you need do is expand your check code so that if the cookie is set, you don’t display the poll, and if it is not, you do. Once you successfully submit the poll, then you set the cookie for that poll id.

if (submitting the form) { 
  store the poll results
  set the cookie 
  display a thank-you, or the next poll, or whatever
  }
else {
  if (cookie is set) { 
    don't display the poll
  } 
  else {
    display the poll
    }
}

Hi there, Could you tell me what i need to replace? do i need to change the name “id”? im very new to cookies so its a little confusing. also i need to add this code after my original code? Thank you again.

if (submitting the form) {
store the poll results
set the cookie
display a thank-you, or the next poll, or whatever
}
else {
if (cookie is set) {
don’t display the poll
}
else {
display the poll
}
}

The above is pseudo-code, intended to show you the layout of your own actual code.

All I was saying above is that when you set the cookie in the code you posted, you use this code:

$cookie_name = “id”;
$cookie_value =“id”;
setcookie($cookie_name, $cookie_value time() + (86400 * 30), “/”); // 86400 = 1 day

which, presuming it doesn’t have the missing comma after $cookie_value in your actual code, creates a cookie with the name “id”.

This code, however

if(!isset($_COOKIE[$id])) {

is looking at a cookie whose name is stored in the variable $id. In your other code, $id isn’t mentioned at all, so I’m not sure where that comes from. But if you don’t create $id and give it a value, you’re checking the contents of a cookie with a blank name, and I’m not sure that’s allowed.

I presumed that difference was a typo in your post because you said it was setting the cookie, and I presumed you knew that because your check was telling you it was set, which in turn means that you must have either typed incorrectly in your post, or set $id to contain the string “id” somewhere.

Well no i didnt say know that, because as i said im very knew to PHP. Could you show me some example code that would work for my code? thankyou.

Ah, sorry, I presumed when you said “it did set”, you meant that the cookie had set.

I can’t really do sample code without basically writing it for you. How about you have a go with it and post the results, and people will offer help based on that. Maybe start with the basic logic, convert my pseudo code into actual working code. Doesn’t have to update the database, just have a simple form with a submit button and a few echo statements in the various sections so you can follow the logic through the code and see where things fit. Once all that works, and you can see that your form isn’t displayed once the cookie has been set, then you can add the rest back in.

Hello, there. Well i guess you’re not going to for whatever reason, which is fine. I’m not a student or anything this is just for me to mess with, so me learning the code isn’t that much of a big deal to me right now… id just like a working solution after these hours. ive been at it for a few hours trying different soultions with no luck… And i thought i already did have a go at writing it lol, thats kinda why im here. thanks though.

It is actually a bit unusual to use cookies to store data such as answered poll id’s. Typically you would use sessions which would store data on the server. An automatically generated session id cookie would then tie a particular user to it’s $_SESSION data.

However, cookies do allow you to have a stateless application so lets go with it.

Start by using a ‘pretend’ database so as to bypass the PDO nonsense.

<?php declare(strict_types=1);
# polls.php
$polls = [
  1 => ['title' => 'Language', 'answers' => [1 => 'PHP', 2 => 'JS']],
  2 => ['title' => 'Slayers', 'answers' => [1 => 'Buffy', 2 => 'Faith', 3 => 'Kennedy']],
];

Now we list the available polls with a voting option.

<?php declare(strict_types=1);

# index.php
error_reporting(E_ALL);

// Get a answered poll
$pollAnsweredId = isset($_COOKIE['poll_answered_id']) ? (int)$_COOKIE['poll_answered_id'] : null;

require_once 'polls.php';

$html = <<<EOT
<h1>Select Polls</h1>
<table border="1">
<tr><td>Link</td><td>Title</td><td>Voted</td></tr>
EOT;

foreach($polls as $pollId => $poll) {
    $pollUrl = "/vote.php?poll_id={$pollId}";
    $voted = ($pollId === $pollAnsweredId) ? 'yes' : 'no';
    $html .= <<<EOT
<tr>
  <td><a href="{$pollUrl}">Vote</a></td>
  <td>{$poll['title']}</td>
  <td>{$voted}</td>
</tr>
EOT;
}
$html .= "</table>\n";
echo $html;

First thing we do is to get the value of any cookie named ‘poll_answered_id’. If so, we use this to set the ‘Voted’ column in the list of polls. Notice the use of more or less descriptive variable names. Just using ‘id’ can quickly become confusing.

And then the vote.php code:

<?php declare(strict_types=1);

# vote.php
error_reporting(E_ALL);

require_once 'polls.php';

// Get the current poll
$pollId = isset($_GET['poll_id']) ? (int)$_GET['poll_id'] : null;
if (!isset($polls[$pollId])) {
    die('Poll does not exist');
}
$poll = $polls[$pollId];

// Check posted
$pollAnswerId = isset($_POST['poll_answer_id']) ? (int)$_POST['poll_answer_id'] : null;
if ($pollAnswerId) {
    // Did the user already answer this poll?
    $pollAnsweredId = isset($_COOKIE['poll_answered_id']) ? (int)$_COOKIE['poll_answered_id'] : null;
    if ($pollAnsweredId === $pollId) {
        die('Already answered');
    }
    // Update the database counter using $pollId and $pollAnswerId

    // Set the cookie
    setcookie('poll_answered_id',(string)$pollId);

    // Back to listing
    header('Location: /index.php');
    exit;
}
// The form
$html = <<<EOT
<form action="/vote.php?poll_id={$pollId}" method="POST">
EOT;
foreach($poll['answers'] as $answerId => $answerTitle) {
    $html .= <<<EOT
<label>
    <input type="radio" name="poll_answer_id" value="$answerId" />
    {$answerTitle}
</label>
EOT;
}
$html .= <<<EOT
<input type="submit" name="submit" value="Vote"/>
</form>
EOT;
echo $html;

Basically we check to see if the user posted an answer. If they did then we check to see if they have already answered the poll using the cookie. If not then we update the database and set to poll_answered_id cookie.

If it is not a post then just send out the form. I might add that it is a bit unusual to add the id to the forms action method. Typically you would have it as a hidden input element.

In any event this might show you the cookie flow path. You can enhance it to allow tracking the answers of multiple polls if you like. And maybe switch to session data unless you really need to use cookies.

And one final bit of unsolicited advice: For questions with multiple moving parts, consider posting your code to a github repo and including a link to it in your question. It can be difficult to see what is going on with bits of code especially when the code does not appear to match the question.

Hello, thankyou for your detailed response, and yes i will do that in the future. thanks!

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.