В этой статье я покажу, как сделать простое голосование с двумя кнопками (ДА/НЕТ) на чистом PHP с защитой от повторных голосований.
Хотите чтобы я потратил время и подготовил для вас ISOшник с самописной системой управления IoT?
Пользуйтесь, дорабатывайте, делитесь! Если есть вопросы - пишите в комментарии.
<?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']
]);
<?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]);
{
"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
<!-- Опрос для читателей -->
<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>
Комментарии
Пока нет комментариев. Будьте первым!