Создание CGI - сценариев на языке Perl

Мы начинаем цикл уроков, посвященных созданию собственных CGI - сценариев. Наши занятия не являются абсолютно полным и всеобъемлющим курсом языка Perl. В первую очередь мы рассмотрим только те аспекты, которые позволят создавать относительно несложные, но полезные и эффективные сценарии.

Будут рассмотрены следующие темы:
  1. Основные принципы ввода-вывода и передача информации серверу. В качестве примера будет разобран простейший счетчик посещений.
  2. Получение информации от сервера. Счетчик совершенствуется.
  3. Безопасность. Опасность простейшего счетчика и как с ней бороться.
  4. Прием и декодирование информации из форм. Сценарий гостевой книги.
  5. Время и дата. Гостевая книга удаляет старые сообщения.
  6. Строковые команды, сравнения. Простейшие базы данных.
  7. Различные хитрости. По настоящему интересные сценарии.

Вот примерный круг вопросов. Не каждый из этих пунктов будет строго соответствовать одному уроку. Для некоторых может понадобиться и больше. Однако, новый материал будет появляться строго раз в неделю, по средам.

А пока, небольшое вводное занятие на тему

"Почему именно Perl, или как стать крутым Unix - программистом, ничего не понимая в этой системе"

Каждому человеку свойственно гордиться талантами, которыми он обладает. А еще больше - теми, которых у него нет. Причем, по моему опыту это особенно характерно для компьютерного мира. Каждый, кто умеет печатать на клавиатуре двумя пальцами, хочет считаться крутым программистом. Соответственно, человек прочитавший книгу "Освой самостоятельно программирование за 1 день" хочет считаться экспертом по всем операционным системам. Эта особенность компьютерщиков и привела к появлению языка Perl. Главным достоинством этого языка является его умение эффективно спрятать большинство особенностей операционной системы Unix, вызывающих у непосвященного шок. Учитывая, что большинство серверов Internet работают именно под Unix, а большинство авторов этих серверов - под Windows, язык способный преодалеть барьер, является большим подспорьем.

Рассмотрим основные особенности языка Perl.

Однако, в каждой бочке меда- по ложечке дегтя. Поскольку Perl - интерпретируемый язык, программы созданные на нем значительно менее эффективны, чем программы на языке С. Потеря эффективности окажется практически незаметной для простых программ, но будет весьма существенной для сложных. Таким образом, Perl максимально подходит для решения простых задач, таких как создание счетчиков, гостевых книг и поиска в простых базах данных.

Еще одна ложечка дегтя связана с тем, что Perl ведет свое происхождение из мира Unix. А следовательно - он сохраняет некоторые особенности этой системы. Они не служат значительным препятствием, но учитывать их придется. Учитывая, что для DOS/Windows существует большое количество компиляторов С/C++ уровня "для чайников", для серверов работающих под управлением Windows, более подходящим выбором будет все-тка С.

Ненсмотря на эти ложки дегтя, в качестве языка для разработки сценариев, Perl предпочтительнее чем Ява. Так, он не устапает Яве по скорости исполнения, однако значительно превосходит ее по простоте.

Таким образом, язык Perl является удобным средством, позволяющим быстро разрабатывать программы, решающие 90% задач, обычно возлагаемых на CGI - сценарии. Особенно в случае когда необходимо управлять сервером, работающим под Unix, а изучать тонкости этой системы - не хочется. Именно на такую аудиторию и будут рассчитаны наши уроки.

CGI-сценарии на языке Perl.

Занятие второе. Файловый ввод-вывод. Простейший счетчик посещений.

Практически ни один сценарий не может обойтись без операций ввода - вывода. В настоящем занятии будут рассмотрены основные принципы осуществления этой операции на языке Perl.

При работе с Perl, надо учитывать, что он ведет свое происхождение из Unix, и несет отчетливый отпечаток ее идеологии. Одним из примеров подход к операциям файлового ввода-вывода как к операциям с потоками.

Занятие состоит из следующих частей:

Функция open

Начнем с начала - создание и открытие файлов.

И создание и открытие файлов осуществляется одной и той же функцией open. В простейшем случае ее вызов выглядит так:

open("Имя потока", "имя файла");

Имя потока - это имя потока, который должен быть связан с файлом. В программе для обращения к файлу мы должны использовать именно его.

Имя файла - название файла с которым мы хотим поработать.

Использование двойных кавычек с точки зрения Perl, означает, что заключенное в них выражение представляет собой строку.

Первое что обращает на себя внимание - отсутствие указания в явном виде на то, для какой именно цели файл открывается. Так, если файл уже существует, должен ли он быть очищен, или нет. Можно в этот файл записывать информацию, или он предназначен только для чтения ?

Естественно, при использовании функции open, все это на самом деле указывается, но в виде символов, добавляемых к имени файла спереди.

Всего используется три символа. Причем некоторые могут комбинироваться между собой.

Вот эти символы:

Знак

Функция

Совместим с

Несовместим с

<

Открытие для чтения. Если файла нет - возникает ошибка

<

> и <

>

Создание файла для записи. Чтение также возможно

> и +

<

+

Открытие для чтения - записи

> и <

нет

Возможны следующие комбинации:

Комбинация

Функция

<+

Открыть для чтения - записи. При отсутствии файла возникает ошибка

+>

Создать файл для чтения - записи. Если файл существует - его содержимое теряется

>>

Открыть или создать для дополнения

+>>

Открыть или создать для чтения - записи

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

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

Текст программы счетчика


#!/usr/bin/perl 
#Простейший счетчик. При каждом вызове увеличивает значение,
#хранящееся в файле с именем count.cnt на единицу и возвращает
#н возвращает новое значение.Авторы - Леонид Садофьев, Лариса Работнова
$counterfile="count.cnt"; #имя файла содержащего значение счетчика
unless (open(COUNTER,"+<$counterfile")) #если не удалось открыть файл 
  {
  $counter=0; #обнуляем значение счетчика
  }
else #если файл открыт успешно, 
  {
  $counter=<COUNTER>; #читаем значение счетчика
  }
close(COUNTER); #закрываем файл
$counter=$counter+1; #увеличиваем счетчик на единицу
open(COUNTER,"+>$counterfile"); #открываем файл со сбросом содержимого
print COUNTER $counter; #записываем новое значение счетчика
close (COUNTER); #закрываем файл
print "Content-type: text/html\n\n"; #вот так должен начинаться ответ
                                     #CGI - сценария. 
                                     #Пустая строка - обязательна
print <<EOF                          #Теперь можно генерировать HTML страницу
<html>
<head>
<title>Counter value</title>
</head>
<BODY>
<center>
<br>
 $counter
<br>
</body>
</html>


Счетчик может быть вызван через директиву включения типа

<!--#include virtual="cgi-bin/visitor.cgi"-->

или с помощью тэга HTML image src. Естественно, на вашем сервере счетчик может быть размещен в другом каталоге, а не только в cgi-bin

Несколько комментариев к тексту программы.

  1. Первая строка, оформленная как комментарий, является обязательной. Она указывает путь к интерпретатору Perl.
  2. Комментарии в языке Perl начинаются со знака '#'
  3. Ключевое слово unless не имеет аналогов в распространенных языках программирования, а жаль - оно реализует весьма полезную функцию 'Если не'.
  4. Первый раз функция open вызывается в режиме открытия существующего файла. Если она возвращает ошибку (то есть файл не существует), счетчик приобретает значение 0.
  5. В языке Perl, для текстовых строк, заключенных в двойные кавычки производится подстановка значений. Встретив имя переменной, интерпретатор подставит в это место ее значение.
  6. Обратите внимание на синтаксис операции чтения строки из файла.
  7. Знак $ в начале имени переменной указывает на то, что мы имеем дело со скалярной переменной, а например не с массивом.
  8. Второй раз функция open вызывается в режиме создания файла. Если файл не существует - он будет создан. Если уже существует, его длина будет обнулена.
  9. Ого ! Читали значение как строку, а работаем с ним как с числом ! Все правильно - Perl это позволяет. Понятие типа переменной в обычном смысле отсутствует. Благодаря этому, переменные не обязательно объявлять заранее, а можно сразу вводить в нужном месте.
  10. Весь вывод осуществляется с помощью функции print. Если при вызове указывается название потока, то вывод направляется в этот поток. Если название потока не указано - вывод направляется в стандартный поток вывода (STDOUT). Первый способ нужен для записи в файл, второй - для ответа серверу.
  11. CGI сценарий должен ответить серверу, причем придерживаясь определенных правил. Самый распространенный вариант ответа - генерация HTML страницы. Однако, прежде необходимо сформировать заголовок ответа, уведомив сервер, как именно мы собираемся отвечать. После заголовка обязательно должна следовать пустая строка.
  12. Обратите внимание, что можно указать и выводить в поток все что идет дальше до определенной метки. В нашем случае - выводить все до конца файла.
  13. При использовании данного сценария под Unix можно столкнуться с проблемой, которая по началу портила нам жизнь. В DOS/Windows конец строки обозначается двумя символами - \n и \r то есть - "новая строка" и "возврат каретки". В Unix используется только символ "новая строка". Если не удалить символ возврат каретки в конце каждой строки - интерпретатор Perl выдаст ошибку и сценарий не будет выполняться. Под DOS/Windows удалять символ не обязательно
  14. Для создания счетчика такого уровня не обязателен труд двух программистов. Просто этот сайт мы делаем вместе и пойди разберись, кто что писал.

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

Чтобы усовершенствовать счетчик, устранив эти два основных дефекта, надо уметь получать и использовать в сценарии информацию от сервера. Этому и будет посвящено следующее занятие.

Предыдущие занятия:

Первое

CGI-сценарии на языке Perl.

Занятие третье. Получение и обработка данных

Совершенствуем счетчик - один сценарий на все страницы

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

Занятие состоит из следующих частей:

Вызов сценария с параметрами

В настоящее время существуют два способа вызова сценария с передачей ему параметров. Это методы Get и Post. Мы не будем подробно обсуждать достоинства и недостатки каждого из этих методов, а ограничимся только минимально необходимой информацией. Метод GET - это метод передачи принимаемый по умолчанию. Иными словами, если метод не указан явно, то сервер предполагает, что данные передаются по методу Get. В соответствии со спецификацией Standart CGI, при вызове сценария сервер заносит такие данные в переменную среды QUERY_STRING. Второй метод - POST - предназначен для передачи больших объемов данных и данные помещаются в стандартный входной поток сценария. Впрочем, при работе со счетчиком нам понадобится только метод GET,

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

cgi-bin/counter.cgi?mainpage

В данном случае вызывается сценарий с именем counter.cgi, расположенный в каталоге cgi-bin. В качестве параметра сценарию передается строка mainpage. Сам вызов осуществляется как правило через директивы включенияна стороне сервера - команду серверу, оформленную как комментарий HTML. Создавать форму для этого не требуется.

Хеши в языке Perl и доступ к переменным окружения

Хэши являются одной из самых удачных находок языка Perl и применяются в программах чрезвычайно широко. Более того, они почти не имеют аналогов в других языках программирования. Что же это такое ?

Хеш представляет собой набор пар ключ-значение. При этом, как ключ, так и значение, могут иметь любой тип - числовой, строковый и т.д. Ключ используется для доступа к значению. Вот несколько простых примеров работы с хэшем:

Действие

Синтаксис

Объявление переменной типа хэш

%MyHash;

Присвоение значения ключу. Если заданный ключ отсутствует - создается новая пара

$MyHash{'NewKey'}=$NewValue;

Изменение значения

$MyHash{'NewKey'}+=3;

Чтение значения

$Result=$MyHash{'NewKey'};

При обращении к значению, соответствующему неизвестному ключу возвращается значение undef.

Для доступа к переменным окружения в Perl поддерживается встроенный хэш с именем %ENV. Таким образом, доступ к переданным по методу GET данным осуществляется следующим образом:

$Data=$ENV{'QUERY_STRING'}; #Одинарные кавычки - строковая константа.

В нашем примере переменная $Data примет значение mainpage. Остается только использовать это значение для вычисления имени файла, содержащего значение счетчика для требуемой страницы.

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

Текст программы счетчика


#!/usr/bin/perl 
#Счетчик посещений. При каждом вызове увеличивает значение,
#хранящееся в файле с именем,вычисленным на основе передаваемого параметра на единицу и возвращает
#н возвращает новое значение.Авторы - Леонид Садофьев, Лариса Работнова
$counterfile; #переменная для хранения имени файла содержащего значение счетчика
$Data=$ENV{'QUERY_STRING'}; #теперь $Data содержит переданные сценарию данные
$counterfile="../counters/$Data.cnt"; #используем фиксированные имя каталога 
                                      #и расширение файла - безопасность превыше всего !
unless (open(COUNTER,"+<$counterfile")) #если не удалось открыть файл 
  {
  $counter=0; #обнуляем значение счетчика
  }
else #если файл открыт успешно, 
  {
  $counter=<COUNTER>; #читаем значение счетчика
  }
close(COUNTER); #закрываем файл
$counter=$counter+1; #увеличиваем счетчик на единицу
open(COUNTER,"+>$counterfile"); #открываем файл со сбросом содержимого
print COUNTER $counter; #записываем новое значение счетчика
close (COUNTER); #закрываем файл
print "Content-type: text/html\n\n"; #вот так должен начинаться ответ
                                     #CGI - сценария. 
                                     #Пустая строка - обязательна
print <<EOF                          #Теперь можно генерировать HTML страницу
<html>
<head>
<title>Counter value</title>
</head>
<BODY>
<center>
<br>
 $counter
<br>
</body>
</html>


Несколько комментариев к тексту программы.

  1. Первая строка, оформленная как комментарий, является обязательной. Она указывает путь к интерпретатору Perl.
  2. Строку в одинарных кавычках можно рассматривать как "жесткую" константу - внутри такой строки подстановки не выполняются. Если строка заключена в двойные кавычки - вместо имен переменных подставляются их значения
  3. Комментарии в языке Perl начинаются со знака '#'
  4. Для вычисления имени файла мы используем предположение, что все файлы, содержащие значения счетчиков хранятся в одном каталоге с именем counters и имеют расширение .cnt. Естественно это можно менять по вкусу. В нашем примере каталог counters находится на том же уровне иерархии, что и каталог cgi-bin. Не забудьте создать такой каталог у себя на сервере и разрешить доступ к нему. Или измените текст программы.
  5. Ключевое слово unless не имеет аналогов в распространенных языках программирования, а жаль - оно реализует весьма полезную функцию 'Если не'.
  6. Первый раз функция open вызывается в режиме открытия существующего файла. Если она возвращает ошибку (то есть файл не существует), счетчик приобретает значение 0.
  7. В языке Perl, для текстовых строк, заключенных в двойные кавычки производится подстановка значений. Встретив имя переменной, интерпретатор подставит в это место ее значение.
  8. Обратите внимание на синтаксис операции чтения строки из файла.
  9. Знак $ в начале имени переменной указывает на то, что мы имеем дело со скалярной переменной, а например не с массивом.
  10. Второй раз функция open вызывается в режиме создания файла. Если файл не существует - он будет создан. Если уже существует, его длина будет обнулена.
  11. Ого ! Читали значение как строку, а работаем с ним как с числом ! Все правильно - Perl это позволяет. Понятие типа переменной в обычном смысле отсутствует. Благодаря этому, переменные не обязательно объявлять заранее, а можно сразу вводить в нужном месте.
  12. Весь вывод осуществляется с помощью функции print. Если при вызове указывается название потока, то вывод направляется в этот поток. Если название потока не указано - вывод направляется в стандартный поток вывода (STDOUT). Первый способ нужен для записи в файл, второй - для ответа серверу.
  13. CGI сценарий должен ответить серверу, причем придерживаясь определенных правил. Самый распространенный вариант ответа - генерация HTML страницы. Однако, прежде необходимо сформировать заголовок ответа, уведомив сервер, как именно мы собираемся отвечать. После заголовка обязательно должна следовать пустая строка.
  14. Обратите внимание, что можно указать и выводить в поток все что идет дальше до определенной метки. В нашем случае - выводить все до конца файла.
  15. При использовании данного сценария под Unix можно столкнуться с проблемой, которая по началу портила нам жизнь. В DOS/Windows конец строки обозначается двумя символами - \n и \r то есть - "новая строка" и "возврат каретки". В Unix используется только символ "новая строка". Если не удалить символ возврат каретки в конце каждой строки - интерпретатор Perl выдаст ошибку и сценарий не будет выполняться. Под DOS/Windows удалять символ не обязательно
  16. Для создания счетчика такого уровня не обязателен труд двух программистов. Просто этот сайт мы делаем вместе и пойди разберись, кто что писал.

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

Предыдущие занятия:

  1. Первое
  2. Второе