PHP: весёлый тест о шахматах :)
Смысл публикации данного шыдевра даже не в самом тесте, хотя он весёлый и подойдёт для детей-шахматистов, а в ещё одном способе быстрой организации онлайн-тестирования ("только написать текстовый файл, а скрипт чтоб был готовый"), на этот раз на PHP, так как на яваскрипте подобного уже много.
Работа скрипта сводится к тому, чтоб разобрать текстовый файл с помощью трёх функций:
read_test ($filename) //читает тест из файла $filename и формирует из него массив show_test ($test) //показывает тест из массива $test resume_test ($test) //выводит резюме по данным $_POST и массива $test
"Тест о шахматах: в шутку и всерьёз" в работе
Технические замечания и листинги:
Кодировка файла данных и файлов скрипта - Юникод (UTF-8). В норме, конечно, пользователь не знает имя файла на сервере, которое передаётся функции read_test
вместо data.txt
, так что дополнительная защита данных не обязательна. Существенно и то, что начисляемые за ответы баллы не передаются через HTTP-соединение, присутствуя только в массиве $test
. В исходнике страницы можно увидеть лишь то, что при выборе первой радиокнопки на сервер передаётся значение 1, второй - 2 и т.д.
Так как разбор данных очень простой, не нужно цифр 0-9
в тексте вопросов теста, цифры означают только номера вопросов, а буквы со скобками - варианты ответов.
Если максимум пяти вариантов ответа мало, исправьте в index.php
строку А-Д
в регулярном выражении на А-К
, например.
Вот простейший пример файла теста:
1. Что родилось в лесу? А) Ёлочка 2 Б) Медведь 1 В) Батут 0 Г) Таможенный союз 1 2. Куда уходит детство? А) Неведомо куда 1 Б) Туда или сюда 0 В) В чужие поезда 1 Г) В какие города 2 RESULTS 1. 0-1 балл. Очень плохо :) 2. 2-3 балла. Ответы так себе 3. 4-4 балла. Вы - умный чувак, всё правильно
И массив $test
для него:
Array ( [1] => Array ( [number] => 1 [question] => Что родилось в лесу? [variants] => 4 [text] => Array ( [1] => Ёлочка [2] => Медведь [3] => Батут [4] => Таможенный союз ) [ball] => Array ( [1] => 2 [2] => 1 [3] => 0 [4] => 1 ) ) [2] => Array ( [number] => 2 [question] => Куда уходит детство? [variants] => 4 [text] => Array ( [1] => Неведомо куда [2] => Туда или сюда [3] => В чужие поезда [4] => В какие города ) [ball] => Array ( [1] => 1 [2] => 0 [3] => 1 [4] => 2 ) ) [cnt] => 2 [res] => Array ( [1] => Array ( [result] => 0-1 балл. Очень плохо :) [lo] => 0 [hi] => 1 ) [2] => Array ( [result] => 2-3 балла. Ответы так себе [lo] => 2 [hi] => 3 ) [3] => Array ( [result] => 4-4 балла. Вы - умный чувак, всё правильно [lo] => 4 [hi] => 4 ) ) [rescnt] => 3 )
Во вложенную папку img
можно дополнительно положить картинки в формате PNG, номера которых совпадают с номерами вопросов, например, 5.png
для пятого. "Нечётные" картинки будут выводиться слева, "чётные" - справа. Аналогичную картинку для пятого варианта вывода следует назвать r5.png
и поместить в ту же папку.
При пропуске вопросов приложение скажет, какие вопросы не отвечены и даст ответить повторно, не удалив уже выбранные варианты.
Стандартный элемент HTML <input type="radio">
не даёт возможности выбирать радиокнопку щелчком по сопровождающему тексту, а только по маленькому неудобному кружочку. В коде с помощью мини-скрипта script.js
реализован выбор радиокнопки щелчком по тексту.
Скрипту не нужны внешние библиотеки вроде jquery.js
.
Файл index.php
<!DOCTYPE html> <html dir="ltr" lang="ru-RU"> <head> <meta charset="utf-8"> <title>Весёлый тест о шахматах</title> <link rel="stylesheet" href="style.css"> </head> <body><div id="content"> <h1>Тест о шахматах: в шутку и всерьёз :)</h1> <?php function read_test ($filename) { //читает тест из файла $filename и формирует из него массив $data = file_get_contents($filename); list ($questions,$results) = explode ("RESULTS",$data); $questions = preg_split ("/\d{1,2}\.\ /msiu", $questions); $cnt = 1; $test = array (); foreach ($questions as $q) { $q = trim($q); if (empty($q)) continue; list ($question,$variants) = preg_split ("/\r?\n+/msiu", $q); $question = trim($question); $parts = preg_match_all ("/([А-Д]{1}\)\ )(.*)/iu",$q,$matches); if (empty($question) or $parts===false or count($matches)<3) { echo '<div class="error">Ошибка теста в вопросе:<br><i>'.$q."</i></div>\n"; exit; } $test[$cnt]['number']=$cnt; $test[$cnt]['question']=$question; $test[$cnt]['variants']=count($matches[2]); for ($i=1; $i<=$test[$cnt]['variants']; $i++) { $variant_string = trim($matches[2][$i-1]); $variant = preg_split ("/\ \d{1}/iu", $variant_string); $ball = array (); if (count($variant)!=2 or !preg_match("/\ \d{1}/iu",$variant_string,$ball)) { echo '<div class="error">Ошибка теста в строке:<br><i>'.$variant_string."</i></div>\n"; exit; } $test[$cnt]['text'][$i]=$variant[0]; $test[$cnt]['ball'][$i]=trim(''.$ball[0]); } $cnt++; } $test['cnt']=$cnt-1; $results = preg_split ("/\d+\.\ /msiu", $results); $rescnt = 1; foreach ($results as $r) { $r = trim($r); if (empty($r)) continue; $parts = preg_match_all ("/(\d+\-\d+)(\ +.*)/iu",$r,$matches); if ($parts===false or count($matches)<3) { echo '<div class="error">Ошибка теста в варианте вывода:<br><i>'.$r."</i></div>\n"; exit; } $test['res'][$rescnt]['result']=$r; list ($lo,$hi) = preg_split ("/\-/msiu", trim($matches[1][0])); $test['res'][$rescnt]['lo']=$lo; $test['res'][$rescnt]['hi']=$hi; //не проверяем $lo<=$hi и т.п. $rescnt++; } $test['rescnt']=$rescnt-1; return $test; } function show_test ($test) { //показывает тест из массива $test echo '<script type="text/javascript" src="script.js"></script> <noscript> <div class="error">Для нормальной работы приложения нужен включённый Javascript!</div> </noscript>'."\n"; echo '<form method="post" name="f1">'."\n"; for ($cnt=1; $cnt<=$test['cnt']; $cnt++) { echo '<div class="question">'.$test[$cnt]['number'].'. '.$test[$cnt]['question']."\n"; $filename = 'img/'.$cnt.'.png'; $class = $cnt%2==0 ? 'odd_img' : 'even_img'; if (file_exists($filename)) echo '<img src="'.$filename.'" class="'.$class.'" />'."\n"; $letter = 'A'; for ($i=1; $i<=$test[$cnt]['variants']; $i++) { $text = $test[$cnt]['text'][$i]; $ball = $test[$cnt]['ball'][$i]; if (!empty($_POST['q'.$cnt])) $checker = intval($_POST['q'.$cnt]); else $checker = -1; echo '<div class="answer">'. '<input name="q'.$cnt.'" value="'.$i.'" type="radio"'.($checker==$i?' checked':'').'> '. '<span onclick="select_radio(\'q'.$cnt.'\','.($i-1).');">'.($letter++).'. '.$text.'</span>'. '</div>'."\n"; } echo '</div>'."\n"; } echo '<input type="hidden" name="cnt" value="'.$test['cnt'].'">'. '<input type="submit" name="action" value="Результаты >>">'. '</form>'."\n"; } function resume_test ($test) { //выводит резюме по данным $_POST и массива $test $cnt = intval($_POST['cnt']); if ($cnt!=$test['cnt']) { echo '<div class="error">Неверное значение количества вопросов: <i>'.$cnt."</i></div>\n"; exit; } $error_list = array (); $balls = 0; for ($i=1; $i<=$cnt; $i++) { if (!isset($_POST['q'.$i])) { $error_list[] = $i; continue; } else $balls += $test[$i]['ball'][intval($_POST['q'.$i])]; } $errors = count($error_list); if ($errors>0) { echo '<div class="error">Вы не ответили на вопросы c номерами: <i>'; for ($i=0; $i<$errors; $i++) echo $error_list[$i].' '; echo '</i>. Пожалуйста, дополните свой выбор :)</div>'."\n"; show_test($test); } else { $found = false; for ($rescnt=1; $rescnt<=$test['rescnt']; $rescnt++) { if ($balls>=$test['res'][$rescnt]['lo'] and $balls<=$test['res'][$rescnt]['hi']) { echo '<div class="result">'; $filename = 'img/r'.$test['res'][$rescnt]['lo'].'.png'; $class = $test['res'][$rescnt]['lo']%2==0 ? 'odd_img' : 'even_img'; if (file_exists($filename)) echo '<img src="'.$filename.'" class="'.$class.'" />'."\n"; echo 'Ваш результат: <b>'.$balls.'</b>.<p>Ваш вывод: '.$test['res'][$rescnt]['result'].'</p></div>'."\n"; $found = true; break; } } if (!$found) { echo '<div class="error">Не найдено ответа для вашего количества баллов <b>'. $balls.'</b>. Обратитесь к разработчику :(</div>'."\n"; } echo '<div class="question"><a href="'.$_SERVER['HTTP_REFERER'].'">Попробовать ещё раз!</a></div>'."\n"; } } $test = read_test("data.txt"); // print_r($test); if (!empty($_POST['action'])) resume_test ($test); else show_test($test); //print_r($_POST); ?> </div></body></html>
Файл style.css
* { padding: 0; margin: 0; } h1 { font-size: 140%; } input { background-color: white; border: 1px solid black; padding: 4px; } #content { width: 90%; margin: 10px auto; } .even_img { float: left; padding-left: 10px; padding-bottom: 10px; } .odd_img { float: right; padding-left: 10px; padding-bottom: 10px; } .question { padding: 10px; font-size: 120%; } .answer { overflow: auto; } .error { width: 90%; margin: 10px auto; padding: 10px; font-size: 120%; border: 2px solid red;} .result { width: 90%; margin: 10px auto; padding: 10px; font-size: 120%; border: 2px solid green; }
Файл script.js
function select_radio (myname, mynumber) { var btn = document.f1.elements[''+myname]; for (var i=0; i<btn.length; i++) btn[i].checked = false; btn[mynumber].checked = true; }
На самом деле, показанный код писался в учебных целях и далеко не совершенен, например, script.js можно успешно заменить тегом <label>, а файл читать сразу в массив функцией file и т.д.
27.10.2016, 11:25 [4334 просмотра]