Категории

Простое голосование на PHP с защитой от накруток

18.02.2026

✅ Финал: Всё работает! ДА (50%) НЕТ (50%) 🎉

В этой статье я покажу, как сделать простое голосование с двумя кнопками (ДА/НЕТ) на чистом PHP с защитой от повторных голосований.

📋 Что умеет этот скрипт

📁 Структура файлов

/var/www/site/public_html/
├── poll.php # обработчик голосования
├── get_poll_results.php # получение результатов
├── poll_data.json # файл с данными
└── includes/
    └── footer.php # или любой файл, куда вставите HTML

🔐 Безопасность

📊 Демо (как это выглядит)

📊 Опрос для читателей

Хотите чтобы я потратил время и подготовил для вас ISOшник с самописной системой управления IoT?

👍 Да, интересно
👎 Нет, фигня
100%
✅ Да: 1 ❌ Нет: 0 Всего: 1
✅ Спасибо за голос!

🎉 Итог: Всё работает, код безопасен, можно использовать на любом сайте с PHP. Первый тестовый голос показал 100% "ЗА"!

Пользуйтесь, дорабатывайте, делитесь! Если есть вопросы - пишите в комментарии.

🔧 Файл poll.php

<?php
/**
 * Обработчик голосования
 * Путь: /var/www/site/public_html/poll.php
 */

// Путь к файлу с данными
$pollFile = __DIR__ . '/poll_data.json';

// Фильтр ботов
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (preg_match('/bot|crawl|spider|slurp|google|yandex/i', $userAgent)) {
    http_response_code(403);
    die('Bot detected');
}

// Получаем IP и создаем уникальный ID посетителя
$ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
$visitorId = md5($ip . ($_SERVER['HTTP_USER_AGENT'] ?? ''));

// Получаем голос
$input = json_decode(file_get_contents('php://input'), true);
$vote = $input['vote'] ?? '';

// Проверяем валидность
if (!in_array($vote, ['yes', 'no'])) {
    http_response_code(400);
    echo json_encode(['error' => 'Invalid vote']);
    exit;
}

// Читаем данные
if (!file_exists($pollFile)) {
    $data = ['yes' => 0, 'no' => 0, 'voters' => []];
} else {
    $content = file_get_contents($pollFile);
    $data = json_decode($content, true) ?? ['yes' => 0, 'no' => 0, 'voters' => []];
}

// Проверяем, не голосовал ли уже
if (in_array($visitorId, $data['voters'])) {
    http_response_code(403);
    echo json_encode(['error' => 'Вы уже голосовали']);
    exit;
}

// Обновляем счетчик
$data[$vote] = $data[$vote] + 1;
$data['voters'][] = $visitorId;

// Сохраняем
file_put_contents($pollFile, json_encode($data, JSON_PRETTY_PRINT));

// Отправляем результат
header('Content-Type: application/json');
echo json_encode([
    'yes' => $data['yes'],
    'no' => $data['no']
]);

📊 Файл get_poll_results.php

<?php
/**
 * Получение результатов голосования
 * Путь: /var/www/site/public_html/get_poll_results.php
 */

$pollFile = __DIR__ . '/poll_data.json';

$yes = 0;
$no = 0;

if (file_exists($pollFile)) {
    $content = file_get_contents($pollFile);
    $data = json_decode($content, true);
    if ($data) {
        $yes = $data['yes'] ?? 0;
        $no = $data['no'] ?? 0;
    }
}

header('Content-Type: application/json');
echo json_encode(['yes' => $yes, 'no' => $no]);

📦 Файл poll_data.json

{
    "yes": 0,
    "no": 0,
    "voters": []
}

⚠️ Важно: После создания файла дайте права на запись:
chmod 666 /var/www/site/public_html/poll_data.json
chown www-data:www-data /var/www/site/public_html/poll_data.json

🎨 HTML + JavaScript (вставьте в footer.php)

<!-- Опрос для читателей -->
<div class="poll-container">
    <h3>📊 Опрос для читателей</h3>
    <p>Хотите чтобы я потратил время и подготовил для вас ISOшник с самописной системой управления IoT?</p>
    
    <div class="poll-buttons">
        <button onclick="vote('yes')" class="vote-btn yes-btn" id="vote-yes">👍 Да, интересно</button>
        <button onclick="vote('no')" class="vote-btn no-btn" id="vote-no">👎 Нет, фигня</button>
    </div>
    
    <div class="poll-results" id="pollResults" style="display: none;">
        <div class="progress-bar-container">
            <div class="progress-bar" id="yesProgress" style="width: 0%"></div>
        </div>
        <div class="results-text">
            <span>✅ Да: <span id="yesCount">0</span></span>
            <span>❌ Нет: <span id="noCount">0</span></span>
            <span id="totalVotes">Всего: 0</span>
        </div>
    </div>
    
    <div id="voteMessage" class="vote-message"></div>
</div>

<!-- Стили для тёмной темы -->
<style>
.poll-container {
    max-width: 500px;
    margin: 20px auto;
    padding: 25px;
    background: #1a1a1a;
    border: 1px solid #333;
    border-radius: 12px;
    color: #e0e0e0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.poll-container h3 {
    margin: 0 0 15px 0;
    color: #fff;
    font-size: 1.3rem;
}

.poll-container p {
    color: #b0b0b0;
    line-height: 1.5;
    margin: 15px 0;
}

.poll-buttons {
    display: flex;
    gap: 12px;
    margin: 25px 0;
}

.vote-btn {
    flex: 1;
    padding: 14px 20px;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    cursor: pointer;
    transition: all 0.2s;
    color: white;
    font-weight: 500;
}

.yes-btn {
    background: linear-gradient(135deg, #2e7d32, #1b5e20);
}

.yes-btn:hover:not(:disabled) {
    background: linear-gradient(135deg, #388e3c, #2e7d32);
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(46, 125, 50, 0.3);
}

.no-btn {
    background: linear-gradient(135deg, #c62828, #b71c1c);
}

.no-btn:hover:not(:disabled) {
    background: linear-gradient(135deg, #d32f2f, #c62828);
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(198, 40, 40, 0.3);
}

.vote-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

.progress-bar-container {
    width: 100%;
    height: 30px;
    background: #333;
    border-radius: 6px;
    overflow: hidden;
    margin: 10px 0;
}

.progress-bar {
    height: 100%;
    background: linear-gradient(90deg, #4caf50, #81c784);
    text-align: center;
    line-height: 30px;
    color: white;
    transition: width 0.3s;
}

.results-text {
    display: flex;
    justify-content: space-between;
    margin: 15px 0;
    color: #ccc;
}

.vote-message {
    margin-top: 20px;
    padding: 12px;
    border-radius: 8px;
    text-align: center;
    font-weight: 500;
}
</style>

<!-- Скрипт для голосования -->
<script>
// Проверка голосовал ли пользователь
function checkVoted() {
    return localStorage.getItem('hasVoted') === 'true';
}

// Загрузка результатов
function loadResults() {
    fetch('/get_poll_results.php')
        .then(response => response.json())
        .then(data => {
            updateResults(data.yes, data.no);
            if (checkVoted()) {
                showResults();
            }
        })
        .catch(() => console.log('Не удалось загрузить результаты'));
}

// Отправка голоса
function vote(choice) {
    if (checkVoted()) {
        document.getElementById('voteMessage').innerHTML = '❌ Вы уже голосовали';
        showResults();
        return;
    }
    
    document.getElementById('vote-yes').disabled = true;
    document.getElementById('vote-no').disabled = true;
    document.getElementById('voteMessage').innerHTML = '⏳ Отправка...';
    
    fetch('/poll.php', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({vote: choice})
    })
    .then(response => response.json())
    .then(data => {
        if (data.error) throw new Error(data.error);
        
        localStorage.setItem('hasVoted', 'true');
        updateResults(data.yes, data.no);
        showResults();
        document.getElementById('voteMessage').innerHTML = '✅ Спасибо за голос!';
    })
    .catch(error => {
        document.getElementById('voteMessage').innerHTML = '❌ ' + error.message;
        document.getElementById('vote-yes').disabled = false;
        document.getElementById('vote-no').disabled = false;
    });
}

// Обновление интерфейса
function updateResults(yes, no) {
    const total = yes + no;
    document.getElementById('yesCount').textContent = yes;
    document.getElementById('noCount').textContent = no;
    document.getElementById('totalVotes').textContent = `Всего: ${total}`;
    
    if (total > 0) {
        const yesPercent = (yes / total * 100).toFixed(1);
        document.getElementById('yesProgress').style.width = yesPercent + '%';
        document.getElementById('yesProgress').innerHTML = yesPercent + '%';
    }
}

function showResults() {
    document.querySelector('.poll-buttons').style.display = 'none';
    document.getElementById('pollResults').style.display = 'block';
}

document.addEventListener('DOMContentLoaded', loadResults);
</script>

Комментарии

Пока нет комментариев. Будьте первым!

Оставить комментарий

← Назад к списку

Посетителей сегодня: 0
о блоге

© Digital Specialist | Не являемся сотрудниками Google, Яндекса и NASA