Создаём графический информер на PHP
Глядя на счётчик посещений mail.ru, или на какой-либо другой информер, вы наверняка задумывались о том, как бы сделать такой самому. Эта статья поможет вам осуществить ваши планы. Всё, что для этого понадобится – хостинг с поддержкой PHP и знание основ этого языка. Если же вы сомневаетесь в необходимости информера, значит, у вас, наверное, индекс цитирования и посещаемость - соответственно пяти- и семизначные числа. Потому что даже Яндекс уделяет таким вещам внимание. Ведь это, по сути, бесплатная реклама, которая при этом не злит посетителей!
Почему именно PHP? Он значительно проще Perlа, особенно в отладке, программы на этом языке менее капризные и практически не зависят от набора установленных дополнительных программ.
Функции PHP для работы с графикой находятся в отдельной библиотеке GD. Скорее всего, она входит в дистрибутив вашей версии языка, однако не помешает выполнить следующий сценарий, чтобы это проверить:
<?php if (function_exists(?imageline?)) print ?есть?; else print ?нет? ?>
function_exists возвращает true, если функция с заданным именем определена, в данном случае мы проверяем наличие одной из тех самых графических функций, которые нам понадобятся. Если сценарий показал, что библиотека отсутствует, вам нужно будет скачать её отсюда: http://www.boutell.com/gd/. Вернёмся к нашей теме. Я распишу процесс создания информера на двух примерах: счётчик загрузок программ и информер оценки программы в форуме (оба используются у меня на GetSoft.ru). Начнём с первого из них. Нам понадобится изображение-заготовка, на котором будем рисовать числа. Предполагается, что у нас уже есть картинка с размерами 88x31 пиксел (стандартный формат для таких вещей). У себя я использовал формат PNG, хотя вы можете выбрать какой-нибудь другой. Библиотека поддерживает работу с GIF, JPEG, PNG, BMP и ещё несколькими. Кстати, поддержка GIF и PNG в некоторых версиях может отсутствовать, поэтому рекомендуется перед выбором формата проверить, будет ли библиотека с ним работать. Используйте сценарий, показанный выше, заменив название функции на "imagecreatefrompng" или "imagecreatefromgif".
В нижней части этой картинки мы будем выводить числа. Создайте сценарий следующего содержания (у меня он называется counterimage.php):
<?php
$hImage = imagecreatefrompng(?image.png?);
header(?Content-type: image/png?);
imagepng($hImage);
?>
Первая строка загружает изображение в память, возвращая дескриптор, по которому с ним можно будет работать. Вторая посылает браузеру заголовок о том, что далее будет следовать PNG-рисунок. Третья строка преобразует изображение с указанным дескриптором в выбранный формат и посылает его клиенту. Между первой и третьей строками с загруженной картинкой можно делать всё, что угодно, в частности мы будем рисовать на ней цифры. Есть несколько вариантов использованных нами графических функций, отличающихся окончаниями, например, imagecreatefromjpeg или imagewbmp. Справку по ним вы можете найти в руководстве по PHP, задав поиск фразы "image functions".
Если у вас установлен и настроен вебсервер, вы можете уже сейчас проверить работоспособность этого сценария. Для этого поместите его в папку с документами сервера и наберите в браузере:
http://localhost/counterimage.php
Если всё сделано правильно, вы должны увидеть ваш рисунок. Если нет, то причин может быть множество. Проверьте, работает ли вообще сервер, набрав http://localhost/, проверьте, куда вы положили скрипт и рисунок (в нашем примере они должны лежать в одной папке). А вы вообще проверяли, установлена ли GD ?
Библиотека GD позволяет печатать текст на рисунке, однако, чтобы не зависеть от различных шрифтов, мы будем выводить цифры вручную, используя функции imagesetpixel и imageline.
int imagesetpixel (resource image, int x, int y, int color)
int imageline (resource image, int x1, int y1, int x2, int y2, int color)
Первым аргументом обеих функций должен быть дескриптор изображения (который был возвращён imagecreatefrompng), последним - цвет, которым будем рисовать. Способ его задания будет различным в зависимости от того, как цвет представляется в изображении. Если у нас 24-битный рисунок, то color интерпретируется так:
Синий + Зелёный * 256 + Красный * 256 * 256.
Если в изображении 8 бит, т.е. оно использует палитру, то color будет означаать индекс в ней. Если честно, мне не хотелось возиться с палитрой, поэтому все информеры у меня на сайте используют 24-битный PNG, хотя некоторые читатели могут обвинить меня в глупой растрате памяти и трафика.
Смысл промежуточных аргументов, думаю, понятен. Для imagesetpixel это координаты закрашиваемой точки (0, 0 означает верхний левый угол, если кто забыл . Параметры imageline указывают координаты точек, соединяемых линией. К счастью, ребята из Microsoft к библиотеке GD руки не прикладывали, поэтому линии рисуются нормально. (В Windows GDI последняя точка линии не закрашивается.)
Теперь нужно создать функцию, которая будет вырисовывать цифры. Функции в PHP объявляются в любом месте программы, но лучше не выкрутасничать, а сделать это сразу после "<?php". Вставьте туда следующий код:
function PrintChar(
$hImage,
$strChar,
$x, $y,
$nColor)
{
switch ($strChar)
{
case ?0?:
imageline($hImage, $x, $y + 1, $x, $y + 4, $nColor);
imageline($hImage, $x + 1, $y + 5, $x + 2, $y + 5, $nColor);
imageline($hImage, $x + 3, $y + 4, $x + 3, $y + 1, $nColor);
imageline($hImage, $x + 2, $y, $x + 1, $y, $nColor);
break;
case ?1?:
imageline($hImage, $x + 2, $y, $x + 2, $y + 5, $nColor);
imagesetpixel($hImage, $x + 1, $y + 1, $nColor);
break;
case ?2?:
imagesetpixel($hImage, $x, $y + 1, $nColor);
imageline($hImage, $x + 1, $y, $x + 2, $y, $nColor);
imagesetpixel($hImage, $x + 3, $y + 1, $nColor);
imageline($hImage, $x + 3, $y + 2, $x, $y + 5, $nColor);
imageline($hImage, $x + 1, $y + 5, $x + 3, $y + 5, $nColor);
break;
case ?3?:
imageline($hImage, $x, $y, $x + 3, $y, $nColor);
imagesetpixel($hImage, $x + 3, $y + 1, $nColor);
imageline($hImage, $x + 1, $y + 2, $x + 2, $y + 2, $nColor);
imageline($hImage, $x + 3, $y + 3, $x + 3, $y + 4, $nColor);
imageline($hImage, $x + 2, $y + 5, $x + 1, $y + 5, $nColor);
imagesetpixel($hImage, $x, $y + 4, $nColor);
break;
case ?4?:
imageline($hImage, $x + 2, $y + 4, $x, $y + 4, $nColor);
imageline($hImage, $x, $y + 3, $x + 3, $y, $nColor);
imageline($hImage, $x + 3, $y + 1, $x + 3, $y + 5, $nColor);
break;
case ?5?:
imageline($hImage, $x + 3, $y, $x, $y, $nColor);
imagesetpixel($hImage, $x, $y + 1, $nColor);
imageline($hImage, $x, $y + 2, $x + 2, $y + 2, $nColor);
imageline($hImage, $x + 3, $y + 3, $x + 3, $y + 4, $nColor);
imageline($hImage, $x + 2, $y + 5, $x, $y + 5, $nColor);
break;
case ?6?:
imageline($hImage, $x + 3, $y, $x + 1, $y, $nColor);
imageline($hImage, $x, $y + 1, $x, $y + 4, $nColor);
imageline($hImage, $x + 1, $y + 5, $x + 2, $y + 5, $nColor);
imageline($hImage, $x + 3, $y + 4, $x + 3, $y + 3, $nColor);
imageline($hImage, $x + 2, $y + 2, $x + 1, $y + 2, $nColor);
break;
case ?7?:
imagesetpixel($hImage, $x, $y + 1, $nColor);
imageline($hImage, $x, $y, $x + 3, $y, $nColor);
imagesetpixel($hImage, $x + 3, $y + 1, $nColor);
imageline($hImage, $x + 2, $y + 2, $x + 2, $y + 3, $nColor);
imageline($hImage, $x + 1, $y + 4, $x + 1, $y + 5, $nColor);
break;
case ?8?:
imagesetpixel($hImage, $x, $y + 1, $nColor);
imageline($hImage, $x + 1, $y, $x + 2, $y, $nColor);
imagesetpixel($hImage, $x + 3, $y + 1, $nColor);
imageline($hImage, $x + 2, $y + 2, $x + 1, $y + 2, $nColor);
imageline($hImage, $x, $y + 3, $x, $y + 4, $nColor);
imageline($hImage, $x + 1, $y + 5, $x + 2, $y + 5, $nColor);
imageline($hImage, $x + 3, $y + 4, $x + 3, $y + 3, $nColor);
break;
case ?9?:
imageline($hImage, $x, $y + 5, $x + 2, $y + 5, $nColor);
imageline($hImage, $x + 3, $y + 4, $x + 3, $y + 1, $nColor);
imageline($hImage, $x + 2, $y, $x + 1, $y, $nColor);
imageline($hImage, $x, $y + 1, $x, $y + 2, $nColor);
imageline($hImage, $x + 1, $y + 3, $x + 2, $y + 3, $nColor);
break;
}
return 5;
}
Эта функция у нас будет выводить одну цифру. Первый параметр - дескриптор изображения, второй - символ, который надо вывести (здесь будут поддерживаться только числовые). Затем координаты его верхней левой точки и цвет (в таком виде, в котором он будет передан в графические функции). Возвращаемое значение - количество пикселов, на которое позиция вывода сдвинулась вправо. Это даёт возможность в будущем сделать символы переменной ширины, но сейчас мы этим пользоваться не будем. Функция рисует цифры в соответствии со шрифтом, сочинённым мною в перерывах от работы:
Вы можете придумать любой другой шрифт, если вам не нравится GetSoft Sans Serif:) На этом рисунке крестики - начала координат для каждой цифры, жёлтые точки - начало рисования, синие линии - переходы мнимой кисти, красные - рисование. Читатели, интересующиеся содержанием, а не оформлением этой статьи, простят мне этот кривой рисунок в Paint.
Теперь осталось создать функцию, которая будет получать строку и для каждого символа в ней вызывать PrintChar:
function PrintLine(
$hImage,
$str,
$x, $y,
$nColor)
{
$nLength = strlen($str);
for ($i = 0; $i < $nLength; $i++)
$x += PrintChar($hImage, substr($str, $i, 1), $x, $y, $nColor);
}
Все пять аргументов этой функции совпадают с теми, что мы использовали в предыдущей. Разница лишь в том, что x и y здесь означают координаты начала вывода всей строки. Несколько слов об использованных стандартных функциях.
int strlen (string str)
string substr (string string, int start [, int length])
strlen принимает строку и возвращает её длину. substr вырезает фрагмент, принимая строку, его начало и длину (если последняя не указана, используется вся строка до конца). Таким образом, мы вызываем PrintChar для каждого символа в строке, при этом всякий раз сдвигая позицию вывода вправо путём приращения x (напомню, что эта наша функция возвращает ширину выведенного символа).
Остаётся только проверить работу всего кода, который мы написали. Для этого перед той строкой с вызовом imagepng вставьте:
PrintLine($hImage, "0123456789", 10, 22, 0);
Этот вызов должен напечатать одну за другой все цифры, возможные в нашем информере. Координаты (10, 22) подобраны методом тыка и скорее всего будут отличаться в вашем случае. Снова наберите в браузере http://localhost/counterimage.php, после чего вы должны увидеть такой рисунок:
Справа показан реальный работающий счётчик с моего сайта. Там кроме того, что мы сейчас сделали, ещё выводится знак "+", а также текст может печататься с выравниванием по правому краю. Чтобы это реализовать, нужно немного доработать PrintLine. Думаю, с этим вы справитесь самостоятельно. Теперь осталось только заставить наш сценарий выводить нужное число, например, количество загрузок, посетителей, подписчиков и т.д. Техническая сторона этого дела полностью определяется структурой конкретного сайта, так что расписать всё это я не смогу. Скажу лишь, что нужно заменить "0123456789" на нужную переменную.
Информация, которую будет показывать наш сценарий, скорее всего должна зависеть от какого-нибудь параметра, если только ваш информер не выдаёт фиксированные данные, например погоду у нас в Нижнем Новгороде (и только в нём). Для этого скрипту нужно передать один или несколько аргументов. Вызываться при этом он будет так:
http://localhost/counterimage.php?param1=value1¶m2=value2
В каждом сценарии есть предопределённый ассоциативный массив $_GET, в котором хранятся все переданные аргументы. Когда вам нужно будет получить их значения, сделать это можно будет следующим образом:
$a = $_GET{?param1?}; // $a = "value1"
Рассмотрим информер с другим способом подачи информации, который тоже применяется у меня на сайте. Он будет показывать оценку программы в форуме (количество звёзд от 1 до 5) или надпись "нет оценки". Отличие от предыдущего примера заключается в том, что изображения звёзд сохранены в отдельных файлах. Мы будем их копировать на исходное изображение. Ниже приведены рисунки, соответственно star0.gif, star1.gif, notgraded.gif, image.png. Их мы и будем комбинировать.
Рассмотрим одну новую функцию:
int imagecopy (resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h)
Первые два аргумента - дескрипторы двух изображений, соответственно приёмника и источника. Следующая пара аргументов - координаты, показывающие куда надо копировать фрагмент. Затем идут координаты копируемого куска на изображении-источнике, последние два параметра - его ширина и высота. Тут следует заметить, что наши GIF-картинки содержат прозрачные участки. К счастью, функция достаточно умна, чтобы корректно их обработать, а не копировать весь заданный прямоугольник, так что волноваться не следует. Используем следующий нехитрый код:
$hImage = imagecreatefrompng(?image.png?);
if ($nMark)
{
$hImageStar0 = imagecreatefromgif(?star0.gif?);
$hImageStar1 = imagecreatefromgif(?star1.gif?);
$x = 35;
for ($i = 0; $i < $nMark; $i++, $x += 10)
imagecopy($hImage, $hImageStar1, $x, 8, 0, 0, 10, 10);
for (; $i < 5; $i++, $x += 10)
imagecopy($hImage, $hImageStar0, $x, 8, 0, 0, 10, 10);
}
else
{
$hImageNotGraded = imagecreatefromgif(?notgraded.gif?);
imagecopy($hImage, $hImageNotGraded, 35, 8, 0, 0, 50, 10);
}
Тут предполагается, что переменная nMark содержит число от 0 до 5, причём 0 означает отсутствие оценки (по крайней мере одной звёздочки достойна каждая программа Первый цикл рисует нужное число жёлтых звёзд, а второй - оставшееся количество белых. Если же оценки нет, вместо всего этого мы копируем один соответствующий рисунок. В итоге будет что-то похожее на это:
В заключение приведу HTML-код, который нужно будет предоставить пользователям, чтобы они размещали его у себя.
<a href="http://getsoft.ru/"><img src="http://getsoft.ru/counterimage.php?id=777" width="88" height="31" border="0"></a>
Здесь нужно заменить getsoft.ru на ваш адрес, а также 777 на какой-нибудь реальный параметр. Вот, в общем-то и всё, дальше всё зависит только от вашей фантазии... Если есть идеи, о чём ещё можно написать, я с радостью их выслушаю. Всего хорошего, и пусть ваши информеры будут не менее популярными, чем у mail.ru