Counter@PHP
После запуска сайта его требуется развивать и продвигать, а для того чтобы знать, в каком направлении двигаться, нам необходимо знать статистику посещаемости всего сайта, а также отдельных его разделов. И одним из основных инструментов для этого является счетчик просмотров той или иной странички. Такой счетчик обычно размещают в самом низу странички. Причем он может быть как обычным текстовым, так и графическим.
Графические кнопки-счетчики предлагают многие бесплатные сервисы, самые известные из которых - Rambler, Mail.Ru, SpyLog. Но сегодня мы попробуем написать подобный счетчик сами - двигаясь в направлении от простого к сложному.
Простой текстовый счетчик
Начнем с создания самого простого текстового счетчика, который будет обсчитывать только одну страничку – ту, на которой он будет размещен. Но перед тем как переходить непосредственно к программированию, давайте остановимся на его алгоритме. В данном случае он очень простой, но, сформулировав его словами, нам будет проще понять, как работает код. Итак, алгоритм работы счетчика будет следующий:
- Прочитать текущее содержимое счетчика;
- Увеличить текущее содержимое счетчика на единицу;
- Сохранить обновленное содержимое счетчика;
- Вывести на печать обновленное значение счетчика.
Теперь важный вопрос - где мы будем хранить значение счетчика? Т.к. страничка запрашивается разными людьми и с разных компьютеров, то хранить его мы можем только на сервере. В качестве хранилища может выступать либо файл, либо база данных. Работу с базой данных мы сейчас разбирать не будем, а остановимся на обычном текстовом файле, как наиболее подходящем варианте во многих случаях.
Я сразу приведу PHP-код, реализующий самый простой из всех возможных типов счетчика, а затем уже дам ряд комментариев.
<?
// Имя файла, в котором хранится счетчик
$file_counter = "counter.txt";
// Читаем текущее значение счетчика
if (file_exists($file_counter)) {
$fp = fopen($file_counter, "r");
$counter = fread($fp, filesize($file_counter));
fclose($fp);
} else {
$counter = 0;
}
// Увеличиваем счетчик на единицу
$counter++;
// Сохраняем обновленное значение счетчика
$fp = fopen($file_counter, "w");
fwrite($fp, $counter);
fclose($fp);
// Выводим значение счетчика на печать
echo $counter;
?>
Как видите, скрипт достаточно прозрачный и практически полностью следует сформулированному ранее алгоритму. При этом, для удобства дальнейшей поддержки кода, я в первой же строке определил переменную $file_counter, которую уже и использую. Постарайтесь, по возможности, всегда определять переменные для всех более-менее важных исходных данных и не используйте их напрямую.
Перед тем как читать значение счетчика из файла, я, при помощи функции file_exists(), проверяю - существует ли данный файл вообще? Если нет, то сразу же присваиваю счетчику нулевое значение. Если же файл существует, то читаем из него текущее значение счетчика. Для того чтобы прочитать содержимое файла, необходимо использовать следующую конструкцию:
<?
// Открываем файл для чтения
$fp = fopen($file_counter, "r");
// Читаем значение счетчика из файла
$counter = fread($fp, filesize($file_counter));
// Закрываем файл
fclose($fp);
?>
Для работы с файлами в PHP, как и во многих других языках программирования, предварительно необходимо открыть файл на чтение или запись. Для открытия файла используется функция fopen(), в качестве передаваемых ей параметров мы должны указать путь к файлу и способ доступа ("r" - для чтения, "w" - для записи). В случае успешного открытия файла мы получаем его дескриптор, который далее и используется при любых операциях с файлом.
На следующем шаге, используя функцию fread(), мы читаем содержимое файла. Т.к. у нас это очень простой счетчик, то все содержимое файла и является значением счетчика. Но для того чтобы прочитать содержимое файла, нам необходимо, кроме дескриптора файла, также указать и размер читаемых данных. В случае если файл небольшой, проще всего прочитать его в переменную целиком. А значит, нам необходимо предварительно узнать размер файла в байтах. Это делается при помощи функции filesize().
После того как мы закончили работать с файлом, его необходимо закрыть, чтобы освободить ресурсы операционной системы. Для закрытия файла достаточно вызвать функцию fclose(), указав дескриптор файла в качестве параметра. После того как файл будет закрыт, дескриптор становится неопределенным и его больше использовать нельзя.
Прочитав значение счетчика, его необходимо увеличить на единицу и сохранить обновленное значение в файле. При сохранении файла, точно так же, как и при открытии, необходимо вначале получить дескриптор файла, а уже затем делать с файлом какие-либо операции. В отличие от чтения, нам сейчас необходимо открыть файл на запись, указав режим доступа "w". При открытии файла в режиме записи он автоматически усекается до нулевой длины. Для сохранения строки в файле мы используем функцию fwrite(), передавая ей дескриптор файла и собственно значение переменной $counter в качестве параметров.
<?
// Открываем файл для записи
$fp = fopen($file_counter, "w");
// Сохраняем обновленное значение счетчика в файле
fwrite($fp, $counter);
// Закрываем файл
fclose($fp);
?>
Файл может отсутствовать при первом запуске скрипта, и в этом случае он будет создан автоматически - при первом же сохранении счетчика.
Количество просмотров за сегодня
Общий счетчик удобен для оценки интегральной посещаемости странички, но как узнать, сколько раз страничка была просмотрена сегодня? Давайте модифицируем счетчик таким образом, чтобы поддерживалось два счетчика - общий и количество просмотров за сегодня. При этом алгоритм работы такого счетчика будет более сложный:
- Прочитать текущее содержимое счетчиков;
- Увеличить текущее содержимое "общего счетчика" на единицу;
- Проверить, наступил ли уже завтрашний день? Если нет, то увеличить текущее содержимое "счетчика за сегодня" на единицу. Если да, то установить "счетчик за сегодня" равным единице;
- Сохранить обновленное содержимое счетчиков;
- Вывести на печать обновленное значение счетчиков.
После прочтения алгоритма у вас может возникнуть вопрос - а как узнать, что наступил уже завтрашний день? А сделаем мы это следующим образом - одновременно с увеличением текущего содержимого счетчика мы будем сохранять также и дату последнего обращения к страничке. Это позволит нам определить факт наступления завтра простым сравнением даты последнего обращения к страничке и текущей даты.
Работа с датами в PHP
Для работы с датами в PHP существует целый ряд специализированных функций. Нас сейчас интересует только одна, которая возвращает текущую дату, а именно функция date(). В качестве аргумента в функцию передается текстовая строка, определяющая формат получаемой даты. Типичный запрос выглядит следующим образом:
$current_date = date("Y-m-d H:i:s");
В результате выполнения этой строки переменной $current_date будет присвоена текстовая строка, определяющая текущую дату и время. Если мы выведем эту переменную на печать, то увидим что-то типа следующего:
2006-02-04 15:32:07
Обратите внимание, что ее формат соответствует заданному нами, только буквы были заменены соответствующими значениями. Ниже я привожу список наиболее часто используемых букв и что они обозначают. Учтите, что регистр имеет значение. Итак:
- d - день в виде 2-значного числа в формате с лидирующим нулем (01 - 31)
- j - день в виде числа (1-31)
- m - месяц в виде 2-значного числа в формате с лидирующим нулем (01 - 12)
- n - месяц в виде числа (1-12)
- y - год в формате двух последних цифр года (например, "04")
- Y - год в полном 4-значном формате (например, "2004")
- w - день недели (причем, 0 - это воскресенье, 1 - понедельник, 2 - вторник,.. 6 - суббота)
- H - час в 24-часовом формате в виде 2-значного числа с лидирующим нулем (00 - 23)
- G - час в 24-часовом формате без лидирующего нуля (0 - 23)
- i - минуты (00 - 59)
- s - секунды (00 - 59)
Полный список всех возможных кодов для форматирования даты Вы можете посмотреть в справочнике по функциям PHP
В отличие от ранее описанного простого счетчика, который хранит только одно значение, сейчас нам потребуется хранить целых три переменных. К общему счетчику посещений добавятся "счетчик за сегодня" и дата последнего просмотра странички. Вопрос - а как же сохранить эти переменные в файле? Ведь до сих пор мы сохраняли и читали только одно значение. Ответ будет следующим - нам необходимо упаковать несколько значений в одну строку, которую мы уже и будем сохранять в файле. А при чтении будем делать обратную операцию - распаковку строки.
Упаковка и распаковка данных
Для упаковки и распаковки переменных в строку в PHP существует пара удобных функций, предназначенных для этой цели. Они называются implode() и explode(). Первая служит для упаковки и в качестве исходных данных принимает массив (это значит, что предварительно нам придется преобразовать данные для упаковки в массив), а вторая распаковывает строку, возвращая результат в виде массива.
Предположим, что все необходимые параметры текущего значения счетчика нам уже известны, и тогда код для их упаковки в строку будет выглядеть следующим образом:
<?
// Определяем текущую дату
$current_date = date("Y-m-d");
// Формируем массив с параметрами счетчика
$counter = array(
"total" => 10,
"today" => 7,
"date" => $current_date
)
// Упаковываем данные из массива в строку
$string = implode(" | ", $counter);
?>
В примере я первой же командой рассчитал текущую дату, а далее сформировал вручную массив с параметрами счетчика. При этом значения счетчиков я взял произвольными (когда начнем писать работающий счетчик, то заменим их реальными значениями).
Обратите внимание, что в функции implode() в качестве первого параметра указывается т.н. разделитель - последовательность символов, которая будет разделять данные при "склеивании" их в одну строку. Я использовал последовательность символов " | " (пробел - вертикальная черта - пробел), т.к. она мне кажется наиболее удобной для чтения. В общем случае вы можете использовать любой другой символ или последовательность символов в качестве разделителя. Главное, чтобы он был уникальным и не встречался в данных, предназначенных для упаковки, т.к. в противном случае возникнут ошибки при распаковке, связанные с невозможностью корректно определить границы данных. В результате исполнения данного кода переменной $string будет присвоена следующая строка:
Именно ее вы сможете увидеть в файле "counter.txt", после того как запустите данный скрипт (мы его еще, правда, не написали, но к концу статьи он наверняка уже будет работать).
Массивы в PHP
На самом деле в PHP используются не обычные массивы, а так называемые ассоциативные (другое название таких массивов - хеши). В отличие от классического массива, где доступ к нужной переменной осуществляется при помощи цифрового индекса, причем индекс начинается с нуля или единицы, в ассоциативных массивах в качестве индекса может выступать любая строка или число. Эта строка будет называться ключом, и ей будет сопоставлено определенное значение. Такая организация очень удобна. Особенно при работе со связанными данными. Предположим, что нам необходимо сохранить некую информацию о пользователе: его имя, фамилию, электронный адрес и город. С использованием ассоциативного массива это можно сделать следующим образом:
$user = array(
"firstname" => "Вася",
"lastname" => "Петров",
"email" => "vasya@petrov.ru",
"city" => "Санкт-Петербург"
);
Для задания параметров массива я использовал ключевое слово array(), указав в качестве параметров пары ключ-значение, разделенные запятой. Индексный ключ от значения отделяется специальной конструкцией =>. Удобнее всего писать именно так, как показано выше, - по одной переменной на строчке со сдвигом вправо. Теперь индексу с именем "firstname" сопоставлено значение "Вася", а индексу с именем "lastname" сопоставлено значение "Петров" и т.д.
Для того чтобы обратиться к конкретной переменной внутри ассоциативного массива, необходимо после имени массива, в квадратных скобках, указать значение ключа:
echo $user["firstname"]; // Напечатает слово "Вася"
echo $user["email"]; // Напечатает электронный адрес "vasya@petrov.ru"
В отличие от классических массивов, индексы в ассоциативных массивах не обязаны быть уникальными. Другими словами, в массиве могут находиться несколько элементов с одинаковыми ключами и даже просто несколько копий одной и той же пары ключ-значение. Соответственно, и обрабатывать ассоциативные массивы как обычные нельзя, т.к. мы можем пропустить часть значений. Но о том, как обрабатывать ассоциативные массивы, мы поговорим чуть позже, когда в этом возникнет необходимость.
Теперь разберемся с вопросом распаковки параметров нашего счетчика из строки. Будем считать, что она уже считана и нам необходимо извлечь из нее значения счетчика и вывести их на печать. Код, отвечающий за эту часть, будет выглядеть следующим образом:
<?
// Строка, хранящая параметры счетчика
$string = "10 | 7 | 2006-02-04";
// Извлекаем параметры счетчика из строки во временный массив
$temp_counter = explode(" | ", $string);
// Формируем ассоциативный массив с параметрами счетчика
$counter = array(
"total" => $temp_counter[0],
"today" => $temp_counter[1],
"date" => $temp_counter[2]
);
// Выводим значения счетчиков на печать
echo "Количество просмотров страницы: ".$counter["total"]." (+".$counter["today"].")";
?>
Как видите, функция explode() по синтаксису совпадает с implode(), и первым параметром передается разделитель, а вторым - строка для распаковки. В результате мы получаем ассоциативный массив с необходимыми данными. Последней строкой этого кода на печать выводится значение счетчиков. Я сделал более сложный вывод счетчика, чем в предыдущем варианте скрипта, за счет комбинирования текста и переменных, чтобы повысить удобство восприятия. На страничке это будет выглядеть вот так:
Количество просмотров страницы: 10 (+7)
Увеличение значений счетчиков
Осталось разобраться с вопросом увеличения значений счетчиков. Как мы уже договорились ранее, в файле, кроме счетчика, хранится и дата последнего доступа, поэтому нам нужно лишь сравнить ее с текущей датой, чтобы принять решение об увеличении "счетчика за сегодня". Предположим, что строка с информацией о счетчике уже считана и распакована в ассоциативный массив, тогда код увеличения счетчиков будет выглядеть следующим образом:
<?
// Определяем текущую дату
$current_date = date("Y-m-d");
...
// Увеличиваем на единицу общий счетчик
$counter["total"]++;
// Проверяем, наступило ли завтра
if ($counter["date"] != $current_date) {
// Сбрасываем счетчик за сегодня
$counter["today"] = 0;
}
// Увеличиваем на единицу счетчик за сегодня
$counter["today"]++;
// Обновляем дату последнего просмотра
$counter["date"] = $current_date;
?>
В вышеприведенном коде я не стал увеличивать значение "счетчика за сегодня", а наоборот, я его обнуляю в том случае, если сегодня - это уже завтра. В конечном счете, получается абсолютно тот же результат. Просто не забывайте о том, что часто одно и то же действие можно реализовать разными способами. Обратите также внимание на то, что я сразу же обновил и дату последнего просмотра.
Ну вот, мы разобрали все сложные моменты нашего счетчика, и осталось только соединить все вместе, чтобы получить код счетчика, учитывающего и просмотры странички за сегодня.
Счетчик на несколько страниц
Как сделать простой счетчик, мы теперь знаем, но что если мы хотим обсчитывать сразу несколько страниц? Ведь интересно знать, какая страничка на сайте самая популярная, а какая наоборот. Счетчик, который мы только что написали, для этих целей не подходит, т.к. умеет обсчитывать только одну страницу. А значит, нам нужно модифицировать предыдущий скрипт таким образом, чтобы одним фрагментом кода можно было обсчитывать любое количество страничек.
Для того чтобы обсчитывать несколько страничек, нам необходимо для каждой из них завести свой отдельный счетчик. Будем все счетчики хранить в одном файле - по одной записи на каждую страницу. Большая часть работы нами уже проделана, и нужно только модифицировать часть кода, ответственную за загрузку информации из файла и за сохранение информации в файле. Но давайте сначала, как и в предыдущих случаях, сформулируем алгоритм работы нового счетчика:
- Прочитать файл в построчный массив, хранящий информацию о счетчиках;
- Найти нужную строку и прочитать содержимое счетчика;
- Увеличить значение счетчика (как общего, так и счетчика за сегодня)
- Обновить в построчном массиве содержимое строки, относящейся к текущей странице;
- Сохранить построчный массив, хранящий информацию о счетчиках;
- Вывести на печать значение счетчика, относящегося к текущей странице.
Вначале разберемся с функцией чтения информации из файла. До сих пор я использовал стандартную функцию чтения информации из файла fread(), но теперь нам больше подойдет функция file(), которая просто читает содержимое любого файла и сохраняет его в массиве - по одной строке в каждой ячейке. То есть именно то, что нам и нужно! В качестве параметра ей передается путь к файлу, который нужно прочитать. Никаких дополнительных действий по получению дескриптора файла, его открытию, закрытию и прочего делать не нужно. По этой причине PHP-код для чтения информации о счетчиках из файла будет исключительно компактным:
<?
// Имя файла, в котором хранится счетчик
$file_counter = "counter.txt";
// Читаем информацию о счетчиках в построчный массив
$counters = @file($file_counter);
?>
Обратите внимание, что, в отличие от предыдущих счетчиков, я уже не проверяю наличие в системе файла с данными, т.к. это делает сама функция file(). В случае, если файл отсутствует, данная функция просто вернет пустую строку и выведет на экран сообщение об ошибке. Нам, в данном случае, это сообщение не нужно, поэтому мы подавляем его при помощи символа "@", указанного перед названием функции. Кстати, этот прием можно использовать и для подавления печати любых других сообщений в вызываемых функциях.
В связи с тем, что нам теперь необходимо обсчитывать несколько страничек, в дополнение к уже имеющимся переменным (общий счетчик, счетчик за сегодня и дата последнего посещения) добавим еще одну, в которой будет храниться URL странички. Теперь для того, чтобы найти в массиве счетчиков нужный, необходимо просто просмотреть весь массив построчно и найти такую запись, у которой значение переменной $counter["url"] совпадает с URL текущей страницы.
<?
// Флаг нахождения нужной записи
$flag = 0;
// Проверяем, обсчитывалась ли эта страничка ранее
reset($counters);
while (list($key, $value) = each($counters)) {
// Распаковываем строку записи в массив с параметрами счетчика
$temp_counter = explode(" | ", trim($value));
$counter = array(
"url" => $temp_counter[0],
"total" => $temp_counter[1],
"today" => $temp_counter[2],
"date" => $temp_counter[3]
);
// Проверяем, совпадает ли адрес счетчика с адресом текущей страницы
if ($counter["url"] == $REQUEST_URI) {
// Если совпадает, значит, мы нашли нужный счетчик
$flag = 1;
break;
}
}
?>
Данный код требует некоторых пояснений. Во-первых, каким образом можно обойти все элементы массива? А во-вторых, как среди них найти нужный? Как я уже говорил ранее, в PHP используются ассоциативные массивы. При этом в качестве индекса в них может выступать любое число или строка. Более того, записи в ассоциативном массиве могут повторяться. Для того чтобы просмотреть все записи в ассоциативном массиве, используется следующий вариант цикла:
<?
reset($array);
while (list($key, $value) = each($array)) {
// Здесь делаем с элементом массива то, что нам нужно
}
?>
В первой строке вызывается функция reset(), которая позиционирует внутренний указатель ассоциативного массива на первый элемент. Это гарантирует, что мы начинаем обход массива с начала. Далее запускается цикл while(), в качестве параметра которого используется конструкция:
list($key, $value) = each($array)
Функция each() читает очередной элемент массива и затем автоматически увеличивает внутренний указатель массива таким образом, чтобы он показывал на следующий элемент. Как только все элементы массива будут исчерпаны, функция each() вернет false и цикл завершится.
Для присвоения значений сразу нескольким переменным используется конструкция list(). В данном случае функция each() возвращает два значения: ключ и собственно элемент массива. Причем именно в таком порядке. Эти значения мы и присваиваем переменным $key и $value соответственно. Другими словами, после этой операции, в переменной $key будет храниться номер строки, а в переменной $value - сама строка.
После того как строка была прочитана, мы, уже знакомым нам способом, распаковываем ее в массив, описывающий счетчик:
<?
// Распаковываем строку записи в массив с параметрами счетчика
$temp_counter = explode(" | ", trim($value));
$counter = array(
"url" => $temp_counter[0],
"total" => $temp_counter[1],
"today" => $temp_counter[2],
"date" => $temp_counter[3]
);
?>
Отметьте только, что, во-первых, перед тем как распаковывать строку, я удаляю символ конца строки "\r\n" при помощи функции trim(). Символы конца строк у нас появляются в момент сохранения нескольких счетчиков в одном файле. Ориентируясь по ним, мы впоследствии и можем разбить файл на отдельные строки. Но перед тем как распаковывать данные, нам необходимо концы строк удалить, а иначе переменная будет содержать лишние символы. Во-вторых, не забудьте, что у нас появилась новая переменная для каждого счетчика, и поэтому конструкция формирования массива несколько расширилась. При этом критично соблюдать порядок элементов массива, т.к. именно в таком порядке данные будут храниться в файле.
Нахождение нужного счетчика
Осталось разобраться с вопросом, а каким образом можно определить адрес текущей странички? Дело в том, что при любом запросе странички посылаются также и данные, описывающие эту страничку, а также ряд других системных переменных. Для того чтобы увидеть список этих переменных, в PHP существует очень полезная функция phpinfo(), которая при своем вызове выводит на печать табличку со всеми переменными окружения и их значениями. Вы можете создать простой файл с названием phpinfo.php, в котором написать вызов данной функции:
В данном случае нам необходимо знать, что адрес текущей странички хранится в глобальной переменной $REQUEST_URI. Именно ее мы и будем использовать при нахождении нужного счетчика. Данный фрагмент кода уже был приведен выше, но я его продублирую здесь:
<?
// Флаг нахождения нужной записи
$flag = 0;
...
// Проверяем, совпадает ли адрес счетчика с адресом текущей страницы
if ($counter["url"] == $REQUEST_URI) {
// Если совпадает, значит, мы нашли нужный счетчик
$flag = 1;
break;
}
...
?>
Далее я опускаю весь код, который увеличивает счетчики и обновляет дату последнего обращения к страничке, т.к. он полностью совпадает с тем, что мы разобрали ранее. Продолжим с того момента, когда нам нужно упаковать скорректированный счетчик обратно в строку и сохранить изменения в файле.
Итак, у нас есть массив, содержащий параметры счетчика, и следующим шагом нам необходимо его вернуть обратно в массив всех счетчиков. В случае если мы корректируем значение странички, которая ранее обсчитывалась, нам необходимо изменить именно ту строку, в которой находилось описание этой странички. Как же ее найти? Это несложно, т.к. ее номер после нахождения хранится в переменной $key. А следовательно, необходимый нам код будет выглядеть следующим образом:
<?
// Формируем обновленную строку в массиве счетчиков
$counters[$key] = implode(" | ", $counter);
?>
В случае же, если страница ранее не обсчитывалась, нам необходимо предварительно создать нужное описание счетчика. После чего добавить его к общему массиву счетчиков:
<?
// Формируем новый массив с параметрами счетчика
$counter = array(
"url" => $REQUEST_URI,
"total" => 1,
"today" => 1,
"date" => $current_date
);
// Формируем новую строку с описанием счетчика и добавляем ее в массив
$counters[] = implode(" | ", $counter);
?>
Осталось сохранить обновленные данные в файле. Сохранять нужно будет построчно все элементы общего массива счетчиков, т.к. измениться могла любая строка. Равно как могли добавиться и новые строки. И главное, нужно не забыть добавить в конце каждого сохраняемого элемента массива признак конца строки. Я об этом уже упоминал ранее. Полный код, ответственный за сохранение массива счетчиков, будет выглядеть следующим образом:
<?
// Открываем файл для записи
$fp = fopen($file_counter, "w");
// Сохраняем обновленный массив счетчиков
reset($counters);
while (list($key, $value) = each($counters)) {
fwrite($fp, trim($value)."\r\n");
}
// Закрываем файл
fclose($fp);
?>
Подключение счетчика к нескольким страницам
Ну что же, счетчик у нас написан, и осталось подключить его ко всем страничкам, которые мы хотим обсчитывать. Но как лучше всего это сделать? Не размещать же на каждой из них громоздкий код? Разумеется, нет. Достаточно преобразовать разобранный выше код в функцию, вынести ее в отдельный файл и подключить ко всем страничкам. Печать же значения счетчика можно осуществить, вызывая функцию, которую можно назвать, скажем, print_counter(). Таким образом, любая страничка, которую мы хотим обсчитывать, будет иметь следующую структуру:
<?
// Подключаем внешние скрипты
require("counter.php");
?>
<html>
<head>
<title>Обсчитываемая страничка</title>
</head>
<body>
<h1>Обсчитываемая страничка</h1>
<? print_counter(); ?>
</body>
</html>
Подключение внешних PHP-файлов делается при помощи функции require(), и это очень удобно. Пока у нас во внешнем файле находится только одна функция, но ведь их может быть и гораздо больше! Данный механизм позволяет создать удобную файловую структуру для поддержки сложных проектов.
Графический счетчик
Вот мы добрались и до написания графического счетчика. Принципы работы текстового счетчика вы уже знаете, графический же отличается от текстового только тем, что нам необходимо формировать не текстовую строку, а графический файл. В качестве основы я буду рассматривать простой текстовый счетчик, который мы написали вначале.
Перед тем как перейти непосредственно к кодированию графического счетчика, нам необходимо узнать некоторые основополагающие принципы работы с графикой в PHP. Первое, что нам необходимо понять, это механизм, при помощи которого браузер обрабатывает информацию.
При загрузке любой html-странички загружается как непосредственно html-код, так и ряд графических файлов, а также множество вспомогательных (таблицы стилей, функции на JavaScript). Для понимания принципов работы удобнее все эти загружаемые файлы воспринимать как потоки данных. И самое интересное то, что непосредственно в потоке ничего не сказано о том, информация какого типа в нем находится. Тип передаваемой информации задается при помощи MIME-типа, который передается в заголовке. Браузер анализирует MIME-тип, и если он содержит значение "image/gif", то браузер будет интерпретировать данные как графику, если "text/html", то обрабатывать как web-страничку и т.д. И при этом совершенно не важно, какое расширение будет у передаваемого файла.
Что такое MIME?
Если расшифровать эту аббревиатуру, то мы узнаем, что это "многоцелевые расширения электронной почты" (Multipurpose Internet Mail Extensions). Дело в том, что впервые данный способ использовался при передаче прикрепленных файлов в электронных письмах. И необходимо было каким-то образом указать формат передаваемых данных. Вот тогда и был изобретен MIME. Но впоследствии эта концепция была расширена на представление типов данных и в других областях. В частности, при отправке web-сервером информации браузеру.
Любое описание MIME-типа состоит из 2-х частей, разделенных слешем. Первая часть определяет группу, к которой относится файл, а вторая - конкретный формат. Для каждого MIME-типа определен свой обработчик, т.е. указатель на программу, которая умеет обрабатывать такие файлы. Наиболее популярные форматы обрабатываются самим браузером. Список некоторых основных MIME-типов я привожу ниже.
- text/plain - обычный текст в формате ASCII
- text/html - web-страничка
- text/xml - XML-данные
- image/gif - графика формата GIF
- image/jpeg - графика формата JPEG
- image/png - графика формата PNG
- application/zip - архивный файл формата ZIP
Любая информация, которая передается в браузер, предваряется служебным заголовком, в котором передается много важной информации, в том числе и тип передаваемых данных. По умолчанию, когда мы пишем PHP-код, интерпретатор языка выдает тип text/html, соответствующий HTML-страничке. Если же мы собираемся программно формировать графический файл, то нам необходимо уже самим выдать нужный тип, соответствующий тому типу графики, который мы будем формировать. Вот как это можно сделать:
<?
// Выдаем в браузер тип передаваемых данных
header("Content-type: image/png");
?>
Функция header() специально предназначена для вывода служебных заголовков. В данном случае мы выдали заголовок, определяющий, что далее будут передаваться графические данные формата PNG.
Графическая библиотека GD
Для того чтобы мы могли работать с графикой в PHP, необходимо, чтобы в PHP была включена поддержка графической библиотеки. Наиболее популярная графическая библиотека, которая входит в дистрибутив PHP, разработана Томасом Буттелом и называется GD. На данный момент актуальной является вторая версия библиотеки, которая называется GD2. Прежде чем приступать к работе с графикой в PHP, необходимо проверить, что данная библиотека подключена. Это можно сделать, вызвав ранее описанную функцию phpinfo(). Если данная библиотека присутствует в составе PHP, то вы увидите информационный блок с именем GD, который описывает конкретные настройки графической библиотеки. В этой статье я не буду подробно останавливаться на подключении и конфигурировании библиотеки, а буду считать, что она уже подключена.
Графическая библиотека содержит в себе множество функций, управляющих созданием и модификацией изображений. Но нам пока понадобится узнать всего несколько функций, которые пригодятся при формировании графического счетчика.
Нет никакого смысла рисовать графический счетчик полностью. Гораздо удобнее заранее нарисовать шаблон-картинку, задающий внешний вид счетчика, на котором уже и печатать текущее значение счетчика. Мы возьмем в качестве исходной картинки изображение размером 88x31, сохраненное в формате PNG.
Итак, первым делом нам необходимо загрузить в переменную изображение, которое хранится во внешнем файле. Попутно его нужно будет раскодировать из формата PNG. Это делается при помощи графической функции ImageCreateFromPNG():
<?
// Имя файла, в котором хранится графический шаблон счетчика
$file_image = "counter.png";
// Загружаем шаблон графического счетчика из файла
$im = @ImageCreateFromPNG($file_image);
?>
В качестве результата выполнения данной функции возвращается дескриптор на созданное в памяти изображение. Дальнейшее взаимодействие с изображением происходит только при помощи дескриптора. Он указывается в качестве первого параметра в большинстве графических функций. Точно таким же образом можно было бы загрузить изображение формата GIF или JPEG. Для этого необходимо было бы использовать функции ImageCreateFromGIF() или ImageCreateFromJPEG() соответственно.
Перед тем как что-нибудь рисовать на изображении, необходимо определить нужные цвета в палитре. Это делается при помощи функции ImageColorAllocate(). В качестве параметра ей передаются уже упомянутый ранее дескриптор изображения и составляющие цвета в формате RGB:
<?
// Определяем необходимые цвета в палитре
$background_color = ImageColorAllocate($im, 255, 255, 255);
$text_color = ImageColorAllocate($im, 0, 0, 0);
?>
Вот теперь мы можем приступить к рисованию. Или, в нашем частном случае, к печати текста на изображении. Это делается при помощи функции ImageString(). В качестве параметра этой функции передаются координаты надписи, номер шрифта, цвет и собственно текст, который необходимо напечатать. Координаты для размещения надписи нужно предварительно рассчитать:
<?
// Рассчитываем координаты надписи с учетом выравнивания вправо
$position_x = 84 - strlen($counter) * 5;
$position_y = 15;
?>
В данном случае, начало координат зависит от изображения-шаблона, а также от того факта, будет ли текст выровнен влево или вправо. В моем случае я выбрал текст, выровненный по правому краю, и поэтому рассчитывал координату x путем вычитания длины надписи от правого края. Т.к. в качестве надписи будут использоваться только цифры, то их ширина будет одинаковой и равна 5 пикселам. Я это знаю точно, потому что использую конкретный шрифт, встроенный в GD. Длину же строки в символах, можно узнать, воспользовавшись функцией strlen(). И вот, когда координаты уже известны, вызываем функцию ImageString():
<?
// Ранее прочитанное из файла значение счетчика
$counter = 14;
// Печатаем текстовую надпись на изображении
ImageString ($im, 1, $position_x, $position_y, $counter, $text_color);
?>
Цифра один, указанная в качестве второго параметра функции ImageString(), - это номер одного из пяти встроенных в графическую библиотеку шрифтов. Они отличаются лишь размером, и значение данного параметра может принимать значение от 1 до 5. Чем больше число, тем крупнее шрифт. Для шрифта с номером один ширина одного символа равна 5 пикселам. Этот факт я и использовал при расчете координат надписи.
Что же, мы уже почти все сделали! Осталось только выдать графические данные в браузер. Это можно сделать, используя функцию ImagePNG(). Но вы еще не забыли, что предварительно необходимо послать заголовок, определяющий тип передаваемых данных?
<?
// Выдаем в браузер тип передаваемых данных
header("Content-type: image/png");
// Выводим графические данные
ImagePNG($im);
// Удаляем изображение из памяти
ImageDestroy($im);
?>
После того как изображение было выдано в браузер и более нам не нужно, его необходимо удалить из памяти, используя функцию ImageDestroy(). Как и в случае с загрузкой, мы могли бы выдать в браузер изображение формата GIF или JPEG, используя функции ImageGIF() или ImageJPEG() соответственно.
Размещение счетчика на странице
Итак, графический счетчик написан, но его еще нужно разместить на страничке. Не забывайте, что в результате выполнения вышеописанного кода будет сформирован графический файл, но не страничка. Давайте вспомним, каким образом на страничке можно разместить изображение. Это делается при помощи тега <img>, который по сути является ссылкой на внешний файл изображения. В нашем случае внешнего файла не существует, и графический счетчик формируется динамически в момент запроса. Но способ подключения изображения к страничке остается тем же самым. Просто в атрибуте src тега <img> нам в качестве параметра необходимо будет указать адрес нашего графического скрипта.
Итак, нам необходимо создать отдельную HTML-страничку, в которой уже и сделать ссылку на графический счетчик. Соответствующий код странички может выглядеть следующим образом.
<html>
<head>
<title>Графический счетчик</title>
</head>
<body>
<h1>Графический счетчик</h1>
<img src="counter.php" width="88" height="31" alt="Графический счетчик">
</body>
</html>
Не замечаете ничего интересного? Ведь именно так подключаются абсолютно все графические счетчики, в том числе и предоставляемые такими грандами, как Rambler или Mail.Ru. Все дело в том, что графический счетчик может быть добавлен абсолютно на любую страничку, в том числе и статическую. А вот текстовый счетчик может быть поставлен только на динамическую страницу.
Архив с файлами к статье
cкачать архив с файлами к статье
Нажав на изображение дискеты, вы сможете скачать ZIP-архив с примерами счетчиков, описанных в статье. Ниже приведено краткое описание архива:
- counter_text/ - простой текстовый счетчик;
- counter_text_today/ - текстовый счетчик с учетом просмотров за сегодня;
- counter_text_multiple/ - текстовый счетчик для нескольких страниц;
- counter_graphics/ - графический счетчик;
- phpinfo.php - печать информации о версии PHP и системном окружении.
Автор: Михаил Мельников
Источник: www.cherry-design.spb.ru
|