Артефакт

Урок ?17. Perl и CGI

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

На сегодняшнем уроке мы поговорим о программах, выполняющихся на стороне сервера: того самого сервера, на котором хранятся просматриваемые нами веб-страницы. Эти программы могут быть инициированы обращением к ним браузера посетителя: точно так же, как в случае запроса с именем HTML-файла, когда сервер возвращает гипертекст, в ответ на запрос с именем файла типа .cgi или .pl (расширения может не быть вовсе, всё зависит от того, как сконфигурирован сервер) сервер запускает CGI-приложение, которое представляет собой запрашиваемый файл. Результаты работы приложения снова передаются браузеру.

Вопреки распространённому среди новичков заблуждению, CGI (Common Gateway Interface) — это не язык, а лишь тип интерфейса, протокол обмена данными между сервером и программой. CGI-программа может быть написана практически на любом языке, компилятор (интерпретатор) которого может работать в той ОС, под которой запущен веб-сервер. Чаше всего CGI-приложения пишутся на языке Perl, именно о нём мы сегодня и поговорим.

Для того, чтобы поэкспериментировать с CGI-скриптами, предварительно нужно инсталлировать Perl и правильно настроить сервер. За дистрибутивом ActivePerl для Windows нужно идти на сайт компании Activestate. Нужный вам продукт распространяется бесплатно. Думаю, что с его поиском и инсталляцией проблем возникнуть не должно. Замечу лишь, что лучше всего инсталлировать ActivePerl в папку C:\Perl, а не туда, куда предлагает программа-инсталлятор.

Для работы с Perl вам потребуется специализированный редактор. Можно обойтись и Notepad, но в нём нет подсветки синтаксиса, невозможно организовать вывод диагностики в сам редактор и т. д. и т. п. Подходящих редакторов десятки, сам я пользуюсь Perlbuilder, пробную версию которого можно скачать с сервера Solutionsoft.

Настройка Apache

Теперь откроем файл httpd.conf.

1. В первую очередь нужно сказать серверу, в какой папке можно запускать скрипты: по умолчанию это делать запрещено. Найдите в файле httpd.conf директиву ScriptAlias. Её нужно переписать примерно так:

ScriptAlias /cgi-bin/ "D:/MyWeb/artefact.lib.ru/cgi/"
<Directory "D:/MyWeb/artefact.lib.ru/cgi"> 
    AllowOverride None
    Options ExecCGI
    Order allow,deny
    Allow from all
</Directory>

Понятно, что нужно прописать те пути, которые соответствуют вашему компьютеру. Обратите внимание, что для директории D:/MyWeb/artefact.lib.ru/cgi, в которой будут храниться наши скрипты, указана опция ExecCGI, которая разрешает Apache запускать в этой директории исполняемые программы.

2. Теперь надо определить, файлы какого типа Apache должен считать скриптами. Ищем в httpd.conf подстроку AddHandler. Переписываем её так:

AddHandler cgi-script .pl

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

На этом настройки Apache закончены. Перезапустите сервер. Осталось убедиться в том, что все описаные процедуры проделаны правильно.

В папке, предназначенной для хранения скриптов, создайте текстовый файл. Содержимое его должно быть таким:

#!C:/Perl/bin/Perl.exe
print "Content-type: text/html\n\n";
print "Hello world!";

Обратите внимание на первые две строки скрипта.

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

Вторая строка должна предварять любой вывод информации в браузер. Если её не будет, сервер также выдаст сообшение об ошибке: отличие от предыдущего случая в том, что код скрипта до операций вывода успеет выполниться.

Итак, переименум файл с кодом в test.pl. Теперь Напечатаем в адресной строке браузера следующее:

http://localhost/cgi-bin/test.pl

Если вы всё сделали правильно, то в окне браузера появится надпись «Hello world!», в противном случае вы увидите сообщение об ошибке. Для того, чтобы узнать, что именно не понравилось серверу, посмотрите в файл error.log, который находится папке logs: туда пишется расшифровка ошибок сервера. Чтобы ошибки отображались в браузере (вместо обычного незначащего сообщения Internal server error), используйте с своих скриптах директиву

use CGI::Carp qw (fatalsToBrowser);

Учтите, что удалённый сервер, на котором вы собираетесь хранить свои скрипты, наверняка сконфигурирован иначе, нежели ваш личный компьютер. В Unix-системах путь к интерпретатору Perl обычно выглядит так:

#!/usr/bin/perl

Соответственно измените первые строки своих скриптов перед тем, как загружать их на удалённый сервер. Кроме этого вам может потребоваться изменить права для загружаемых файлов: делается это командой chmod 755. Эта команда существует только в Unix/Linux системах, под Windows ничего подобного делать не нужно. Любой приличный FTP-клиент (например — LeapFTP), с помощью которого вы получаете доступ на удалённый сервер, поддерживает команду chmod.

Язык Perl

Язык Perl одновременно очень прост — и довольно сложен. Прост потому, что элементарные задачи с его помощью решаются в одно действие, как в школьном Бэйсике.

$a= 2;
$b = 2;
$c = $a * $b;
print $c;

Не стоит пугаться «лишних» символов: в Perl принято предварять имена переменных префиксами, знак доллара означает, что мы имеем дело со скаляром, т. е. числом или строкой символов. Есть ещё массивы, имена которых начинаются с символа «@», и ассоциативные массивы (начинаются с «%»). Заметьте, что отдельные элементы этих массивов являются скалярами и соответственно записываются через «$»: например, @lines, но $lines[4] (пятый элемент массива @lines, нумерация начинается с нуля).

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

Если придерживаться некоего «среднего» уровня программирования, то Perl скорее всё-таки прост. В нём есть знакомые любому программисту директивы while, if, for, read, print и т. д. Есть в нём и удивительный инструмент, который называется Regular Expressions (регулярные выражения): с его помощью вы можете проделать со строками символов всё, что угодно. Правда, выглядит это жутковато для неподготовленного глаза:

s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;

или

s/(href=\".*?)\.+(\")/$1$2/igm;

На самом же деле, как говаривал незабвенный профессор Айсберг, всё очень и очень просто.

Естественно, я не буду писать руководство по программированию на Perl. Их и так слишком много, этих руководств. А вот несколько примеров решения самых распространённых задач, с которыми сталкивается веб-программист, нам не помешают.

Примеры CGI-скриптов

1. Выдача информации в браузер

Возьмём простейшую задачку: пусть скрипт что-нибудь посчитает и выдаст в браузер результат.

Замените содержимое файла test.pl на следующий код:

#!C:/Perl/bin/Perl.exe
# Присвоим переменной $a значение 2:
$a = 2;
# Присвоим переменной $b удвоенное значение $a:
$b = $a * 2;
# Отдадим браузеру тип ожидаемых данных:
print "Content-type: text/html\n\n";
# Выведем сами данные:
print qq~
<HTML>
    <HEAD>
       <TITLE>Пример динамически создаваемой страницы</TITLE></HEAD>
    <BODY>
        <H1>Результат:</H1>
        <p class=body>Значение переменной b = $b</p>
    </BODY>
</HTML>
~;

Вся информация, расположенная в блоке

print qq~
...
~;

будет передана браузеру as is, т. е. в неизменённом виде. Как видно из приведённого выше примера, организовывать вывод таким образом очень удобно, т. к. переменные (в данном случае $b) можно вставлять прямо в HTML-код, а в source code выведенной страницы в этом месте будут находиться их значения. Таких блоков в тексте программы может быть сколько угодно, их можно, скажем, сгруппировать в подпрограммы (ключевое слово sub) и снести в конец файла скрипта, чтобы не загромождать текст Perl-программы кодом HTML.

К слову, этот способ вывода HTML-кода удобен ещё и по другой причине.

Представьте себе, что вам нужно вывести в браузер строку типа «12375"ghr». Ну, мало ли чем приходится заниматься. ;-) Так вот, если написать это не думая как

print "12375"ghr";

мы получим ошибку, и программа выполняться не будет. Причина в том, что символ «"» будет воспринят как конец строковой константы (string terminator), и всё, что следует за ним, интерпретатор попытается воспринять как код Perl, результатом чего явится сообщение об ошибке. В других ситуациях — например, в случае с регулярными выражениями, синтаксис которых довольно сложен — в разряд специальных попадут «/», «?», «.», «\», «$», «^» и многие другие символы. Обычно эта проблемы решается использованием символа «\» (backslash):

print "12375\"ghr";

все символы, перед которыми стоит бэкслэш, будут интерпретироваться как обычные и не окажут никакого влияния на выполнение программы.

Пусть вам нужно отдать браузеру десятки строк HTML-кода. Если делать это построчно, то, во-первых, каждую строку придётся заключать в контейнер «print "...";», но это не единственная беда: HTML-код содержит массу символов, которые будут восприняты как специальные и которые придётся предварять знаком «\» (на жаргоне — «эскейпить»). Блочный же вывод (print qq~...~;) позволяет не менять код и сэкономить таким образом время для чего-нибудь поважнее.

2. Приём данных из формы

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

Способ первый: передача параметров при помощи URL.

До сих пор мы вызывали скрипт, направляя браузер по следующему адресу:

http://localhost/chi-bin/test.pl

Предположим, у нас есть два параметра. Перепишем URL следующим образом:

http://localhost/chi-bin/test.pl?a=2&b=4

Теперь наш скрипт сможет прочитать имена и значения передаваемых ему переменных, как именно — я опишу чуть позже. Обратите внимание: последовательность передаваемых скрипту данных открывается символом «?», а пары имя/значение отделяются друг от друга амперсендом («&»).

Недостаток способа: все передаваемые параметры в явном виде присутствуют в строке адреса браузера, что в ряде случаев может быть неудобно.

 

Способ второй: передача данных через HTML-форму.

Пусть веб-страница содержит форму примерно такого вида:

<form method=POST action="http://localhost/test.pl">
    <input type=hidden name="par1" value="56817">
    <input type=text name="par2" value="rtrtrt">
    <input type=submit value="Отправить данные">
</form>

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

http://localhost/test.pl?par1=56817&par2=rtrtrt

Способ чтения такой формы тот же самый, что и в случае передачи параметров через URL. О нём мы сейчас и поговорим.

Создадим страничку, содержащую ссылку

http://localhost/chi-bin/test.pl?a=2&b=4

Теперь снова перепишем test.pl:

#!C:/Perl/bin/Perl.exe
# Читаем входные параметры:
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
# Разбиваем параметры на пары:
@pairs = split(/&/, $buffer);
# В цикле обрабатываем каждую пару:
foreach $pair (@pairs) {
    ($name, $value) = split(/=/, $pair);
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    #
    $in{$name} = $value;
}
$с = $in{'a'} * $in{'b'};
# Отдадим браузеру тип ожидаемых данных:
print "Content-type: text/html\n\n";
# Выведем сами данные:
print qq~
<HTML>
    <HEAD>
        <TITLE>Пример динамически создаваемой страницы</TITLE>
    </HEAD>
    <BODY>
        <H1>Результат:</H1>
        <p class=body>Значение переменной с = $с</p>
    </BODY>
</HTML>
~;

Я не буду подробно разбирать эту часть кода, т. к., как уже упоминалось, обучение языку Perl выходит за рамки данного курса. скажу лишь, что результатом работы цикла foreach является ассоциативный массив (associative array или hash), содержащий имена и значения всех входных параметров. Достаточно понять, что каждое из этих значений теперь может быть получено вызовом элемента массива $in{'par_name'}, где par_name — имя нужного нам параметра. Запись $с = $in{'a'} * $in{'b'}; очень хорошо это иллюстрирует.

После вызова скрипта в окне браузера должно появиться значение переменной c как произведения значений переменных a и b, которые мы передали скрипту в строке URL. Если что-то не получилось — добро пожаловать в error.log. О синтаксических ошибках вам скажет сам Perlbuilder.

3. Работа с файлами

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

Создайте в той же папке, в которой находится файл test.pl, файл text.txt. Скопируйте туда какой-нибудь текст.

#!C:/Perl/bin/Perl.exe
# Задаём имя текстового файла:
$fname = "text.txt";
# Отдаём браузеру тип контента:
print "Content-type: text/html\n\n";
# Печатаем «шапку» HTML-файла:
print qq~
<HTML>
    <HEAD>
        <TITLE>Пример скрипта, читающего локальный файл</TITLE>
    </HEAD>
    <BODY>
        <H1>Содержимое файла:</H1>
~;
    # Открываем файл (или выдаём сообщение об ошибке):
    open(TFILE,$fname) or die "Нет такого файла";
    # Читаем файл построчно в массив:
    @LINES=<TFILE>;
    # Закрываем файл:
    close(TFILE);
    # Получаем количество элементов полученного массива:
    $LTFILE=@TFILE;
    # Запускаем цикл поэлементной обработки массива:
    for ($i=0;$i<$LTFILE;$i++) {
        # Печатаем каждую строку как HTML-параграф:
        print "<p>".$TFILE[$i]."<\/p>";
    }
# Печатаем окончание HTML-файла.
print qq~
    </BODY>
</HTML>
; 

Вот и всё. Заметьте, что слияние строк в Perl обозначается точкой (например, две текстовые переменные объединяются записью $a.$b).

Запустите скрипт, набрав его адрес в строке браузера. Если что-то не так — см. две последние фразы примера ?2.

4. Регулярные выражения

Как уже было сказано выше, регулярные выражения предназначены для обработки строк. Распознать regexp (сокращённое название Regular Expression) достаточно просто, обычно их предваряет знак «=~», а сами выражения начинаются с буквы (букв), задающей тип операции: m (match) — поиск подстроки, s (substitute) — замена подстроки, и tr (translate) — трансляция. Регулярные выражения трудночитаемы, поскольку строятся в основном из специальных символов и их последовательностей, но в их основе лежит простая логика, позволяющая творить настоящие чудеса. Вот пара простых примеров:

# Поиск первой кратчайшей подстроки,
# расположенной между подстроками «Van» и «ver».
# результат передаётся служебной переменной $1.
$a =~ m/Van(.*?)ver/i;
# Глобальная замена слова Toronto на слово Vancouver
# по всей строке.
$b =~ s/Toronto/Vancouver/ig;

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

# Одним движением руки убираем все пробелы в начале
# и конце строки. При отсутствии явно заданной
# области поиска операция производится над служебной
# переменной $_:
s/(^s+)|(\s+$)//igm;
# Убираем HTML, случайно попавший в обрабатываемую строку:
s/<.*?>(.*?)<.*?>/$1/igm;

Нравится? Сомневаюсь. Но ещё обязательно понравится. Кстати, все тексты в библиотеке обрабатывались при помощи регулярных выражений, а значит, какая-то польза от них всё-таки есть… ;-)

Урок 16. Домашний сервер 

Новости раздела

4 августа 2008 г.
Copy Editing: последняя часть

Ещё на сайте

Библиотека
Языки
Друзья
Канада
Авторский угол

Интернет

CPAN
Citforum
W3C.org
useit.com
Типомания
Code Charts
ру/ководство
Лаборатория dk
WebReference.com
Спецификация Perl
Заметки HTML-кодера
Анатомия Adobe Photoshop
The Apache Software Foundation


Рейтинг@Mail.ru

wordpress statistics

Рейтинг@Mail.ru