Глава 10. PHP: Система оценки материалов

* РНР: система оценки материалов

В этой главе будет разобран сценарий, позволяющий посетителям выставлять оценки размещенным на сайте материалам. Ранее не использованных команд в нем почти нет, так что просто следите за логикой программы.
Сначала подумаем. Что должен делать код выставления оценки? Ну вначале, наверное, принимать от посетителя значение оценки (скажем, из формы с элементами <select. . .>. . .<option>. . ..). Однако, если эта оценка является первой за всю историю существования статьи, то необходимо записать ее в файл. Если же ранее статья уже была оценена, то следует считать имеющееся значение из файла и вычислить среднее арифметическое всех оценок, включая только что добавленную. В самом деле, нам ведь нужна именно средняя оценка, не так ли? А затем это новое значение средней оценки необходимо в тот же файл и записать.
Среднюю оценку можно посчитать по очевидной формуле

(средний балл*количество оценок)+новодобавленная оценка /
количество оценок + 1

Кроме того, среднюю оценку желательно показать и посетителю. Простая вставка файла с оценкой в страницу со статьей с помощью команды include не является изящным решением, так как значение средней оценки может быть выражено и довольно длинной десятичной дробью. Следовательно, оценку желательно округлить хотя бы до сотых, а лучше - до десятых.
Далее. Что должно произойти, когда посетитель выставит статье оценку? Первое, что приходит в голову - это отправить его назад на статью, которую он оценивал. Ведь направляли же мы в сценарии в прошлой главе посетителя назад в гостевую книгу после отправки им в.нее сообщения? Однако посетитель уже ведь прочитал статью, - зачем ему ее выводить снова, тратя его время в Сети, а, значит, и деньги. Куда как лучше после выставления оценки отправлять посетителя на специальную страницу с благодарностью о выполненном им действии и каталогом статей или разделов сайта.
Желательна также и реализация "защиты от накруток", т. е. нельзя давать одному и тому же посетителю возможность голосовать несколько раз. Для этого можно использовать cookie, т. е. помечать с помощью cookie браузер посетителя при выставлении им оценки и впоследствии позволять выставлять оценку только при отсутствии такого cookie.

Комментарий:
Cookie - это файл в специальном формате, который присылается сервером браузеру посетителя сайта, расположенном на этом сервере. Браузер, если он поддерживает cookie (и эта поддержка в нем не отключена), помещает его в особое место и впоследствии отправляет назад на сервер при поступлении от него запроса. Иными словами, cookie позволяет серверу хранить свою информацию на компьютерах посетителей и считывать ее оттуда при необходимости. Подробнее о cookie читайте в гл. 8.

Бесспорно, использование cookie - не очень надежная подобная защита. Ничего не мешает посетителю удалять cookie перед каждой новой попыткой выставления оценки или использовать разные браузеры, да и на общедоступных компьютерах проголосовать можно будет лишь единожды независимо от числа их пользователей до тех пор, пока cookie не будет удален. Но хоть что-то...
Наверняка на сайте будет находиться немало статей, к которым стоит добавить форму выставления оценок. Для того чтобы не вставлять в каждый файл множество строк одинакового кода, стоит выполнить весь код выставления оценки в отдельном модуле и включать его с помощью команды include в статьи, оценка которых посетителями необходима.
Итак, код выставления оценок разместится в двух файлах, один из которых предназначен для вставки в страницы со статьями, а второй содержит в себе текст благодарности посетителю за оценку. Допустим, первый файл будет называться niz.php, а второй - otziv.php. Что ж, приступим к самому тексту кода.
В каждый файл со статьей, в то место, где должна располагаться форма для выбора оценки, следует вставить одну строчку кода:

<?php include ("niz.php"); ?>

* Файл niz.php

Код этого файла выводит информацию об уже выставленных оценках - их количество и средний балл, а также в том случае, если посетитель еще не выставлял свою оценку - форму для ее ввода.

<?php

Файлы со значениями среднего балла и количества оценок будут храниться в папке с именем cnt. Запишем для удобства ее имя в переменную:

$dirct="cnt";

А сами эти файлы будут иметь имена, составляемые на основе имени файла со статьей. Сначала "вытащим" это имя из полного имени файла (пояснения к используемым функциям смотрите в предыдущей главе):

$nom=substr(basename($PHP_SELF), 0, -4);

...а сами файлы назовем на основе этого имени, получив имя файла с количеством оценок прибавлением к нему окончания "kol", а имя файла со средним баллом - окончания "est" (рис. 10.1):

$kolvooc="$nom"."kol"; $ocenka="$nom"."est";

Рис. 10.1. Файлы системы оценивания.
Слева - статьи, файлы со сценариями и папка с файлами оценок, справа -содержимое этой папки


Теперь выведем сведения об уже выставленных оценках.

echo ("Оценок этой статье - ");

Если файл со сведениями о количестве оценок существует (он создается при первом оценивании)...

if (file_exists("$dirct/$kolvooc")==True)
{

...то вставим его значение в документ.

include ("$dirct/$kolvooc");

Если же такового файла нет, т. е. документ ни разу не оценивался...

}
else
{

...выведем значение "0".

echo ( " 0 " ) ;
}

Если файл со средним баллом существует...

if (file_exists("$dirct/$ocenka")==True)
{

...то надо вывести его значение.

echo (". Средний балл - ");

Но просто включить содержимое файла на страницу нельзя - средний балл может быть и длинной десятичной дробью. Поэтому откроем файл для чтения командой f open (подробнее об этой команде смотрите в предыдущей главе)...

$hdl в fopen("$dirct/$ocenka", "r+");

...и считаем в переменную Ssred все содержимое этого файла.

$sred = fread($hdl, filesize("$dirct/$ocenka"));

Примечание:
Функция fread (дескриптор файла, длина считываемого фрагмента) считывает из открытого файла, для которого получен указанный в ее первом параметре дескриптор, столько байт, сколько указано в ее втором параметре (чтение начинается с местонахождения так называемого указателя файла — отметки, показывающей текущее место работы с файлом; указатель двигается при чтении или записи в файл, а также при использовании команды fseek).
Функция filesize (полный путь к файлу) возвращает размер указанного в ее параметре файла в байтах.

Файл можно закрыть...

fclose($hdl);

...а переменную $sred - округлить до десятых

$sred=round ($sred, 1);

Примечание:
Функция round (число, количество разрядов) округляет дробное число в ее первом параметре до количества разрядов, указанного в ее втором параметре. Скажем, round (число, 1) округлит число до десятых, round (число, 2) - до сотых и т. д.

В РНР до четвертой версии функция round могла округлять числа только до целых, поэтому в том случае, если вы располагаете только такой версией РНР, то команда округления до десятых должна выглядеть как

$sred=(round ($sred*10))/10; ...и вывести на страницу.
echo ("$sred.");
}

Имя cookie, в котором будет находиться информация о том, голосовал ли посетитель за данную статью или нет, тоже будет образовываться из имени файла статьи. Запишем это имя в переменную $haveestim...

$haveestim=$nom."haveest";

...и проверим, определена ли переменная с таким именем - т. е. установлен ли одноименный cookie или нет (рис. 10.2). Можно было бы также проверять, какое значение имеет данная переменная, но для нашего сценария это неважно - если переменная установлена и cookie определен, то посетитель уже голосовал за данную статью: ведь cookie с данным именем устанавливается ему в этом и только в этом случае.

if ($$haveestim=="")

Обратите внимание на имя проверяемой переменной - это имя само является значением переменной Shaveestim (подобную конструкцию допускают правила РНР, подробнее смотрите в гл. 3).

Рис. 10.2. Содержимое cookie - пометки о состоявшемся голосовании

Поскольку значения cookies доступны и через массив $HTTP_COOKIE_VARS[], то проверить наличие cookie можно и на основе анализа значений этого массива:

if ($HTTP_COOKIE_VARS[$haveestim]=="")

или в РНР версии 4.1 и выше

if ($_COOKIE[$haveestim]==" ")

Комментарий:
Помните, что доступность данных cookie зависит от настроек в файле php.ini (рис.10.3) - если в файле php.ini установлен в on параметр register_globals, то содержимое cookie доступно в сценарии в переменной с тем же именем, что и cookie, а если ephp.ini установлен в on параметр trackjvars, то содержимое cookie доступно в сценарии в одноименном с этим cookie элементе массива SHTTPCOOKIEVARSfJ (с РНР 4.1 -и $_СООК1Е[]).

Использовать массивы $HTTP_COOKIE_VARS[] и $_СООК1Е[] лучше с точки зрения безопасности. Если в сценарии используются одноименные cookie переменные, а не элементы этих массивов, то в том случае, если cookie не установлен, злоумышленник все равно может передать сценарию значение такой переменной, попросту указав его в адресной строке браузера. В указанные же массивы попадают исключительно полученные с cookie данные. Однако в рассматриваемом в данной главе сценарии это несущественно. 118

Рис. 10.3. Файл php.ini. Вышеупомянутые настройки

Если cookie не установлен - т. е. посетитель ранее не голосовал за данную статью...

{

то выведем ему форму для голосования

. ?>
<form method="post" action="otziv.php">

Передадим в скрытом поле формы имя файла со статьей без расширения - для определения на основе него сценарием-обработчиком имен файлов со сведениями о количестве оценок и среднем балле, а также полное имя, вместе с путем, файла со статьей - для вывода ссылки "Назад" на странице со сценарием-обработчиком. В принципе можно было бы передавать через форму только полное имя файла со статьей, а "чистое" имя файла статьи определять в сценарии-обработчике точно так же, как и в сценарии из niz.php - на основе функции basename, но для сокращения длины кода в обработчике воспользуемся передачей его через форму.

<input name="nom" type="hidden" value="<?php echo $nom; ?>">
<input type=hidden name=nazad value=<?php echo ($PHP_SELF); ?>>

Выведем форму ввода оценки:
Поставьте оценку статье:

<SELECT NAME=ocen>
<OPTION VALUE=5>5 (Отлично)
«DPTION VALUE=4>4 (Хорошо)
<OPTION VALUE=3>3 (Удовлетворительно)
<OPTION VALUE=2>2 (Плохо)
<OPTION VALOE=1>1 (Очень плохо)
</SELECT>

В результате в сценарий-обработчик будет передана переменная Socen (и одноименные элементы массивов $HTTP_POST_VARS, $_POST при соответствующих версиях РНР и настройках в php.ini) со значением, равным параметру value выбранного пользователем пункта выпадающего списка.
Выведем кнопку отправки формы (рис. 10.4).

Рис. 10.4. Система оценивания в действии...

<input name="submit" type="submit" value="Послать оценку"></form>
<?php

А если посетитель уже голосовал за данную статью - т. е. cookie с соответствующим именем у него установлен...

}
else {

...то сообщим ему об этом (рис. 10.5).

echo ("Вы уже голосовали за эту статью!");

Рис. 10.5. ...но голосование уже состоялось

Вот и все.

* Файл otziv.php

Код в этом файле рассчитывает новый средний балл статьи на основе переданной через форму оценки посетителя и текущего среднего балла, записывает значения среднего балла и количества оценок в соответствующие файлы, а также помечает браузер посетителя cookie для недопущения повторного голосования того же самого посетителя.

<?php

Поскольку изменять значения среднего балла и количества оценок имеет смысл лишь в том случае, если посетитель еще не голосовал за статью, то вновь составим имя cookie, служащего "пометкой" свершившегося голосования...

$namecook=$nom."haveest";

...и весь дальнейший код выполним только в том случае, если такого cookie установлено не было.

if ($$namecook=="")

Примечание:
Вместо этой строки можно использовать строку
i f ($HTTP_COOKIE_VARS[$haveestim]=="") или в PHP версии 4.1 и выше — строку
if ($_COOKIE[$haveestim]=="") как уже указывалось выше.

Комментарий:
Казалось бы — а зачем, собственно, проверять на этой странице, установлен ли cookie? Ведь форма для выставления оценки на предыдущей странице может появиться на ней только в том случае, если cookie отсутствует, не так ли? Но все дело в том, что посетитель, желающий сделать "накрутку" статьи, проголосовав за нее множество раз, вполне может сохранить на своем компьютере локальную копию статьи с формой для выставления оценки, и уже с нее осуществлять голосование. Ясно, что отображение формы на локальной копии страницы от наличия или отсутствия cookie не зависит, — поэтому и приходится осуществлять проверку еще и здесь.

Итак, если посетитель еще не голосовал за данную статью...

{

...установим cookie, говорящий, что такое голосование наконец совершилось. Время жизни cookie установим в месяц - пожалуй, хватит. (Пояснение по функции установки cookie смотрите в гл. 8).

SetCookie("$namecook","1",time()+2 592000);

Запишем в переменные имена директории с файлами оценок и самих этих файлов:

$dirct="cnt";
$kolvooc="$nom"."kol";
$ocenka="$nom"."est";

Если файлы оценок не существуют (т. е. выставляемая оценка - вообще первая по счету)...

if ((file_exists("$dirct/$kolvooc")!=True)||
(file_exists("$dirct/$name2")!=True)) {

...то запишем в файл со сведениями о количестве оценивших число 1 (так ведь и есть, не правда ли?)...

$hdll = fopen("$dirct/$kolvooc", "a+"); fwrite($hdll,l); fclose($hdll);

...а в файл со сведениями о среднем балле - выставленную посетителем оценку (она ведь и есть "среднее" от самой себя):

$hdl2 = fopen("$dirct/$ocenka", "a+");
fwrite($hdl2,$ocen);
fclose($hdl2);

Если же файлы со сведениями об оценке уже существуют...

else

...то считаем для начала содержимое файла с количеством оценок в переменную

kvo...
$hdll = fopen("$dirct/$kolvooc", "r+");
$kvo ¦ fread($hdll, filesize("cnt/$kolvooc"));

...а затем увеличим значение этой переменной на 1 - что и будет новым количеством оценок, с учетом последней выставленной:

$kvo++;

Теперь нам надо вернуть точку считывания (так называемый "указатель") в начало файла - для того, чтобы записать в файл новое значение количества оценок. Ведь в результате проведения операции чтения количества оценок из файла точка считывания - указатель - переместилась в его конец. Для совершения данной операции воспользуемся командой rewind:

rewind($hdll);

Примечание:
Команда rewind (дескриптор открытого файла) перемещает точку считывания и записи данных в файл (т. е. указатель файла), в начало этого файла. Если вы записываете что-либо в файл после считывания из него данных, то вам необходимо перед записью воспользоваться этой командой.
Следует помнить, что если файл был открыт командой fopen с параметром а или a+, то независимо от положения указателя запись новых данных командой fwrite будет осуществляться вконец файла.

Запишем новое значение количества оценок в предназначенный для хранения этой величины файл...

fwrite($hdll,$kvo);

...и закроем его.

fclose($hdll);

Теперь разберемся со средним баллом. Откроем файл, где хранится его значение...

$hdl2 = fopen("$dirct/$ocenka", "r+");

...запишем это значение в переменную...

$sred= fread($hdl2, filesize("cnt/$ocenka"));

...и рассчитаем новую величину среднего балла - на основе его старого значения, а также информации о количестве оценок и новой оценки.

$sred=($sred*($kvo-l)+$ocen)/$kvo;

Теперь запишем эти сведения в предназначенный для них файл -точно так же, как и парой абзацев выше.

rewind($hdl2); fwrite($hdl2,$sred); fclose($hdl2);

Собственно, и все.
Можно выводить информацию посетителю об итоговом результате (рис. 10.6) или краткую благодарность.

echo ("Благодарим вас за оценку!");

Рис. 10.6. После успешного голосования

Рис.10.7. После попытки повторного голосования

Если же посетитель уже голосовал за данную статью...

}
else {

...то сообщим ему об этом (рис. 10.7) - и ничего делать не станем, echo ("Вы уже голосовали за эту статью!");

}
Сценарий закончен. ?>

Остальной текст страницы - на ваше усмотрение. Разместите на ней каталог разделов сайта, список статей или просто красиво оформите. Если же пожелаете поставить на ней ссылку на оцениваемую статью - то просто разместите в нужном месте выводящий эту ссылку код:

<?php echo ("<a href=$nazad>Ha3afl</a>"); ?>

Переменная Snazad была передана через форму, помните?

Комментарий:
В данном сценарии для упрощения восприятия значения переменных, передаваемых через форму, брались из одноименных переменных в сценарии-обработчике - так как на безопасность работы программы это здесь не влияет: даже если злоумышленник подставит значение переменной cookie в адресную строку, то он только потеряет возможность проголосовать, и ничего больше. Если вы желаете сделать код лучше соответствующим правилам РНР - замените их на одноименные элементы массива $HTTP_POST_VARS[] (в РНР версии до 4.1) или SPOSTfJ (в РНР версии 4.1 и старше).

Как всегда, сценарий можно совершенствовать до бесконечности. Можно, например, совместить его с разбиравшемся в предыдущей главе сценарием гостевой книги - тогда посетители наряду с оценкой статьи могут оставить и свой отзыв на нее. Можно увеличить количество возможных оценок, которые посетители могут выставлять статьям -использовать 10-балльную или иную систему. Все в вашей власти - творите...

* Текст сценария

Для большей наглядности ниже приводится текст сценария целиком, без разрывов.

Вставка в файлы со статьями:

<?php include ("niz.php"); ?>

Файл niz.php

<?php
$dirct="cnt";
$nom=substr(basename($PHP_SELF), 0, -4); $kolvooc="$nom"."kol"; $ocenka="$nom"."est"; echo ("Оценок этой статье - "); if (file_exists("$dirct/$kolvooc")==True) {
include ("$dirct/$kolvooc"); }
else {
echo (" 0") ; }
if (file_exists("$dirct/$ocenka")==True) {
echo (". Средний балл - "); $hdl = fopen("$dirct/$ocenka", "r+"); $sred = fread($hdl, filesize("$dirct/$ocenka")); fclose($hdl); $sred=round ($sred, 1); echo ("$sred."); }
$haveestim=$nom."haveest"; if ($$haveestim=="")
<form method="post" action="otziv.php">
<input name="nom" type="hidden" value="<?php echo $nom; ?>">
<input type=hidden name=nazad value=<?php echo ($PHP_SELF); ?>>
Поставьте оценку статье:
<SELECT NAME=ocen>
<OPTION VALUE=5>5 (Отлично)
<OPTION VALUE=4>4 (Хорошо)
<OPTION VALUE=3>3 (Удовлетворительно)
<OPTION VALUE=2>2 (Плохо)
<OPTION VALUE=1>1 (Очень плохо)
</SELECT>
<input name="submit" type="submit" value="Пocлaть оценку"></form>
<?php
}
else {
echo ("Вы уже голосовали за*эту статью!"); } ?>

Файл otziv.php

<?php
$namecook=$nom."haveest"; if ($$namecook=="") {
SetCookie("$namecook","1",time()+2592000);
$dirct="cnt";
$kolvooc="$nom"."kol";
$ocenka="$nom"."est";
if ((file_exists("$dirct/$kolvooc")!=True) ||(file_exists("$dirct/$name2")!=True))
{
$hdll = fopen("$dirct/$kolvooc", "a+");
fwrite($hdll,1)j
128
fclose($hdll);
$hdl2 = fopen("$dirct/$ocenka", "a+");
fwrite($hdl2,$ocen);
fclose($hdl2);
else
$hdll = fopen("$dirct/$kolvooc", "r+");
$kvo = fread($hdll, filesize("cnt/$kolvooc"))
$kvo++;
rewind($hdll);
fwrite($hdll,$kvo); .
fclose($hdll);
$hdl2 = fopen("$dirct/$ocenka", "r+");
$sred= fread($hdl2, filesize("cnt/$ocenka"));
$sred=($sred*($kvo-l)+$ocen)/$kvo;
rewind($hdl2);
fwrite($hdl2,$sred);
fclose($hdl2);
echo ("Благодарим вас за оценку!"); else echo ("Вы уже голосовали за эту статью!");

Ниже в этом же файле:

<?php echo ("<a href=$nazad>Haзад</a>"); ?>

 



Опубликовал admin
31 Июл, Понедельник 2006г.



Программирование для чайников.