Создание викторины с помощью 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']); // не передаём исходные названия
}
// Клиент получает только хэши
// При проверке сравниваем хэш выбранного варианта с правильным хэшем
Сложность: увеличивается нагрузка на сервер. Альтернатива – использовать токены сессии.

Создание викторины на PHP - comments

En
Php quiz (php)