Создание викторины с помощью PHP: примеры и инструкции
Основные подходы к созданию викторины на PHP
Как реализовать викторину с минимальными затратами и без базы данных?
Наиболее эффективное решение для небольших проектов – использование сессий PHP и массива с вопросами. Сессия хранит текущий номер вопроса и количество правильных ответов, массив содержит сами вопросы. Этот подход не требует внешних хранилищ и прост в развертывании.
Структура файла questions.php:
<?php
$questions = [
1 => [
'question' => 'Какая функция в PHP используется для подключения к MySQL?',
'options' => ['mysqli_connect', 'mysql_connect', 'PDO', 'mysql_query'],
'answer' => 0
],
2 => [
'question' => 'Что такое сессия в PHP?',
'options' => ['Файл на сервере', 'Переменная в скрипте', 'Механизм хранения данных между запросами', 'Куки'],
'answer' => 2
]
];Основной файл index.php:
<?php
session_start();
require_once 'questions.php';
// Инициализация состояния
if (!isset($_SESSION['current'])) $_SESSION['current'] = 0;
if (!isset($_SESSION['score'])) $_SESSION['score'] = 0;
// Обработка ответа
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$qid = (int)$_POST['qid'];
$selected = (int)$_POST['option'];
if (isset($questions[$qid]) && $selected === $questions[$qid]['answer']) {
$_SESSION['score']++;
}
$_SESSION['current']++;
// Перенаправление для предотвращения повторной отправки
header('Location: index.php');
exit;
}
$current = $_SESSION['current'];
$total = count($questions);
if ($current >= $total) {
echo "<h2>Викторина завершена!</h2>";
echo "<p>Правильных ответов: {$_SESSION['score']} из $total</p>";
session_destroy();
} else {
$q = $questions[$current + 1]; // вопросы нумерованы с 1
?>
<form method="post">
<h3><?= $q['question'] ?></h3>
<input type="hidden" name="qid" value="<?= $current + 1 ?>">
<?php foreach ($q['options'] as $i => $opt): ?>
<label><input type="radio" name="option" value="<?= $i ?>" required> <?= $opt ?></label><br>
<?php endforeach; ?>
<button type="submit">Ответить</button>
</form>
<?php
}Типичная проблема: повторная отправка формы при обновлении страницы. Решение – перенаправление после обработки POST (PRG-паттерн). В примере выше используется header('Location: index.php') после проверки ответа. Еще одна ошибка: неверное приведение типов для идентификатора вопроса – следует использовать (int). Если сессия не стартована, работа с $_SESSION вызовет ошибку, поэтому session_start() должен быть первым.
Как хранить вопросы в базе данных MySQL?
Для масштабируемых проектов удобно разместить вопросы в таблице БД. Это позволяет легко добавлять, редактировать и выбирать случайные вопросы.
-- Создание таблицы
CREATE TABLE questions (
id INT AUTO_INCREMENT PRIMARY KEY,
question TEXT NOT NULL,
option1 VARCHAR(255),
option2 VARCHAR(255),
option3 VARCHAR(255),
option4 VARCHAR(255),
correct INT NOT NULL -- индекс правильного варианта (0-3)
);
-- Вставка данных
INSERT INTO questions (question, option1, option2, option3, option4, correct) VALUES
('Какая функция в PHP используется для подключения к MySQL?', 'mysqli_connect', 'mysql_connect', 'PDO', 'mysql_query', 0);<?php
$pdo = new PDO('mysql:host=localhost;dbname=quiz', 'user', 'pass');
$stmt = $pdo->query('SELECT * FROM questions');
$questions = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Далее работа аналогична массиву, но ключи – id из БД
?>Сложности: необходимо настроить подключение к БД (учетные данные хранить в отдельном конфигурационном файле). При большом количестве одновременных пользователей сессия может мешать – рекомендуется хранить прогресс пользователя в отдельной таблице.
Как сделать викторину с JSON-файлом для вопросов?
JSON-файл удобен для хранения вопросов в переносимом формате, не требующем сервера БД.
// questions.json
[
{
"question": "Какая функция в PHP используется для подключения к MySQL?",
"options": ["mysqli_connect", "mysql_connect", "PDO", "mysql_query"],
"answer": 0
}
]<?php
$json = file_get_contents('questions.json');
$questions = json_decode($json, true);
// $questions – массив, каждому элементу можно добавить ключ (например, индекс)
?>Ошибка: если JSON некорректный, json_decode вернёт null. Следует проверять json_last_error(). Также файл должен быть доступен для чтения веб-серверу.
Как организовать случайный порядок вопросов?
Перемешивание вопросов перед началом викторины.
// Перед стартом викторины храним shuffle-порядок
if (!isset($_SESSION['order'])) {
$order = array_keys($questions);
shuffle($order);
$_SESSION['order'] = $order;
}
// В викторине используем $_SESSION['order'][$current] для получения id вопросаКак добавить таймер на викторину?
Использование JavaScript для отсчета времени и отправки формы автоматически.
<script>
let timeLeft = 30; // секунд на вопрос
const timer = setInterval(() => {
timeLeft--;
document.getElementById('timer').innerText = timeLeft;
if (timeLeft <= 0) {
clearInterval(timer);
document.querySelector('form').submit();
}
}, 1000);
</script>Расширенные примеры кода для викторины на PHP
Как реализовать викторину с AJAX-подгрузкой вопросов?
Использование fetch API и PHP-обработчика позволяет обновлять содержимое без перезагрузки страницы.
// frontend – index.html (или PHP-шаблон)
<div id="quiz"></div>
<script>
let current = 0;
let score = 0;
function loadQuestion() {
fetch('ajax_handler.php?action=get_question&id=' + (current+1))
.then(r => r.json())
.then(data => {
if (data.finished) {
document.getElementById('quiz').innerHTML = `<h3>Результат: ${score} из ${data.total}</h3>`;
return;
}
let html = `<h3>${data.question}</h3><form id="qform">`;
data.options.forEach((opt, i) => {
html += `<label><input type="radio" name="answer" value="${i}"> ${opt}</label><br>`;
});
html += `<button type="submit">Ответить</button></form>`;
document.getElementById('quiz').innerHTML = html;
document.getElementById('qform').addEventListener('submit', e => {
e.preventDefault();
const answer = document.querySelector('input[name="answer"]:checked')?.value;
if (answer !== undefined) {
fetch('ajax_handler.php?action=check_answer&qid=' + (current+1) + '&selected=' + answer)
.then(r => r.json())
.then(res => {
if (res.correct) score++;
current++;
loadQuestion();
});
}
});
});
}
loadQuestion();
</script>// ajax_handler.php
<?php
session_start();
require 'questions.php';
$action = $_GET['action'] ?? '';
if ($action === 'get_question') {
$id = (int)$_GET['id'];
if (!isset($_SESSION['order'])) {
$_SESSION['order'] = array_keys($questions);
shuffle($_SESSION['order']);
}
if ($id > count($_SESSION['order'])) {
echo json_encode(['finished' => true, 'total' => count($questions)]);
exit;
}
$qid = $_SESSION['order'][$id - 1];
$q = $questions[$qid];
echo json_encode([
'question' => $q['question'],
'options' => $q['options'],
'id' => $qid
]);
} elseif ($action === 'check_answer') {
$qid = (int)$_GET['qid'];
$selected = (int)$_GET['selected'];
if (isset($questions[$qid]) && $selected === $questions[$qid]['answer']) {
echo json_encode(['correct' => true]);
} else {
echo json_encode(['correct' => false]);
}
}
?>Проблема: при большом количестве одновременных запросов сессия может блокироваться. Решение – использовать session_write_close() после записи, если данные не нужны для чтения.
Как реализовать подсчет времени на всю викторину с сохранением на сервере?
// В начале викторины
if (!isset($_SESSION['start_time'])) {
$_SESSION['start_time'] = time();
$_SESSION['time_limit'] = 300; // 5 минут
}
// При каждом запросе проверяем
if (time() - $_SESSION['start_time'] > $_SESSION['time_limit']) {
// Автоматическое завершение
$current = count($questions);
}Результат: пользователь получает сообщение о превышении лимита времени.
Как обеспечить защиту от подбора ответов через инспекцию кода?
Не отправлять правильный ответ на клиент. Хранить ключ-хэш для каждого варианта.
// При создании вопроса генерируем хэш для каждого варианта
foreach ($questions as &$q) {
$q['hash_options'] = array_map(function($opt) { return md5($opt); }, $q['options']);
$q['hash_correct'] = $q['hash_options'][$q['answer']];
unset($q['options']); // не передаём исходные названия
}
// Клиент получает только хэши
// При проверке сравниваем хэш выбранного варианта с правильным хэшемСложность: увеличивается нагрузка на сервер. Альтернатива – использовать токены сессии.