Обработка на уеб-форми с Perl
Здравейте и от мен. Наскоро си закупих учебник PERL, и реших да напиша едно нещо като уроче на база прочетено от учебника. Надявам се да бъда полезен.

Perl е интерпретируем език, създаден от програмиста Лари Уол (Larry Wall) за обработка на големи текстови файлове. Името му е съкращение от Practical Extraction and Report Language (език за практически извличане на данни и съставяне на отчети). Макар и да е съкращение, прието е само първата буква да се изписва главна - все пак понякога и традицията си казва думата. С развитието на World Wide Web, Perl се оказва прекрасно средство за взаимодействие между клиентите и уеб-сървърите чрез CGI (Common Gateway Interface) скриптове. Чрез него лесно може да се получат данни, въведени от потребителя чрез HTML форма и да бъдат обработени и съхранени, да се генерира нов HTML документ на базата тези данни и/или да се изпрати E-mail, отново да базата на въвдените данни.

Въпреки че ASP и PHP, които безспорно превъзхождат Perl в множество отношения, все по-широко навлизат в уеб-програмирането, Perl все още намира голямо приложение при създаването на уеб-форми. Защо да учите PHP, ако искате просто да сложите една анкета на сайта си?

Както и за PHP, така и за Perl е необходима поддръжка от страна на сървъра. По-точно необходимо ви е разрешение да изпълнявате Perl скриптове. Може би една от основните причини Perl да се задържи толкова дълго след навлизането на PHP и ASP е именно, че практически всяка дистрибуция на Linux, под който са множество от сървърите, има вграден Perl интерпретатор.Друг не по-малко важен фактор е, че Perl е лесен, и в същото време мощен език за извличане и обработка на данни.

-Интерпретатор

Perl е интерпретируем език, което означава, че кода не се компилира, а остава в оригиналния си вид и се интерпретира от специална програма - интерпретатор. Именно затова първия ред на програмата трябва да съдържа местоположението на интерпретатора; в противен случай скрипта не може да бъде изпълнен. Първия ред започва със знака # и след това следва директорията, в която се намира програмата. Ако скрипта се изпълнява под UNIX-базирана операционна система (в това число влиза всяка една от версиите на Linux), вашия код ще има подобен вид:
#!/usr/bin/perl

За да разберете къде се намира интерпретатора е достатъчно да намишете which perl - тази команда връща директорияра, в която се намира указаната програма

-Коментари

Всеки ред, започващ със знака #, с изключения на първия, се приема като коментар и не се интерпретира. Коментарът може и да не е в началото на линията, може да се намира и след някакъв код; но всичко след знака # се игнорира

-Променливи

В Perl съществуват три типа променливи - скалари, масиви и асоциативни масиви. Скаларните променливи съдържат една стойност. Пред имената им се поставя знака $, който указва на интерпретатора, че това е скаларна променлива. Ето няколко примера:

$foo=1;
$name="Fred";
$pi=3.141592;

Както се вижда от примера, скаларните променливи могат да съдържат данни от всякакъв тип - число или текстов низ. Можете да сложите и цяло изречение. В Perl няма нужда променливите да бъдат декларирани предварително - просто ги слагате дам, където са ви необходими. Вие можете лесно да вкарате променлива в даден низ: просто пишете низа и на мястото на което е необходима променливата я всписвате, без да прекъсвате низа. Ето един пример:

#!/usr/bin/perl
print "Enter your name:";
$name=*STDIN*;
chomp($name);
print "Hello, $name! Welcome to our country.\n"

Ако променливата $name има стойност Jerry, програмата ще изведе следното съобщение:

Hello, Jerry! Welcome to our country

Тази програма има няколко елемента, на които трябва да обърнете внимание. Първия е, че всеки ред, с изключения на първия, завършва с точка и запетая (;). Фактически всеки ред, с някои изключения, трябва да завършва с този символ. Първия ред от скрипта се явява едно такова изключение.

Следващото нещо, което би трябвало да сте забелязали, е че на променливата $name се присвоява *STDIN*. Тази команда указва на интерпретатора да прочете данните от стандартно входно устройство (обикновено клавиатурата). Четенето приключва след натискането на клавиша return (Enter). Следва командата chomp. Тъй като при въвеждане на данни от клавиатурата,в променливата се записва и символа return, което води до преминаване на нов ред след изписване на променливата, вие трябва да отсраните излишния символ. Именно затова служи командата chomp - тя премахма символът retutn от подадения и параметър.
Последното нещо, на което би трябвало да сте обърнали внимание е, че параметърът на командата print завършва със символа \n. Самата команда print служи за отпечатване на данни на екрана. След нея следва интервал и след това в кавички и се задава информацията, която да отпечата. Символът \n е именно символът return, или по-точно неговото обозначение в Perl. Това кара програмата след като изведе указания текст да премине на нов ред.

Масивите съдържат списък със стойности. Пред имената им се поставя знака @, което указва на интерпретатора, че става дума за масив. Ето един пример:

@colors=("red","green","blue");

Първия елемент от масива има индекс 0, следователно за да се обърнате към него трябва да използвате $colors[0]. Както показва този пример, вие имате достъп до всеки един елемент на масива посредством скалар, носеш същото име с квадратни скоби след него, а стойността в квадратните скоби указва номера на елемента от масива.

Функции, свързани с масивите:

pop(@array) връща последния елемент от масива @array
shift(@array) връща първия елемент от масива @array
push(@array, value) прибавя елемента value в края на масива @array (ако value е низ, трябва да го сложите в кавички)
push(@array, @array2) прибавя всички елементи от масива @array2 в края на масива @array
sort(@array) връща елементите на масива @array подредени по азбучен ред
reverse(@array) връща елементите на масива @array подредени отзад напред
$#array връща индекса на последния елемент в масива @array
scalar(@array) връща броя на елементите в масива (стойността, която връща scalar е с единица поголяма от тази на $#, тъй като първия елемент в масива има индекс 0)
Асоциативните масиви са друг тип масиви, за всеки от елементите на които има асоциирана стойност. Пред имената им се слага знакът %, за да се укаже на интерпрататора, че това е асоциативен масив. Ето един пример:

%pages = ("fred, "http://www.cgi101.com/fred/",
"beth", "http://www.cgi101.com/beth/",
"john", "http://www.cgi101.com/john/" );

Същия масив може да дефинирате и като:

%pages = (fred =* "http://www.cgi101.com/fred/",
beth =* "http://www.cgi101.com/beth/",
john =* "http://www.cgi101.com/john/" );

По този начин променливата $pages{'fred'} има стойност "http://www.cgi101.com/fred/". Това е и начина за извлчане на асоциираната стойност на променлива - скаларна променлива със същото име като на асоциативния масив, след която следват фигурни скоби, в коио се вписва името на асоциираната променлива. Трябва да отбележим, че в нашия пример, fred, beth и john се явяват ключови променливи, а съответните адреси - техни асоциирани променливи.

- Условен оператор

Условния оператор има вида

if (expresion) {
statement;
statement;
...
}

където expresion е условието, а statement оператори, които да бъдат изпълнени. Всички оператори между началото на блока { и неговия край } ще бъдат изпълнени само ако условието е вярно. Условния оператор има две части, които не са задължителни и се слагат след края на блока. Първата е elsif и има вида:

elsif (expresion) {
statement;
statement;
...
}

Тази част от кода се изпълнява само ако условието на оператора if не е изпълнено и условието на elsif е изпълнено. Вие можете да имате толкова блока elsif, колкото са ви необходими, като всеки следващ се изпълнява само ако неговото условие е вярно и никое от условията на оператора if и принадлежащите му оператори elsif, стоящи преди текущия, не е изпълнено. Така кода

if ($x==1) {
print "x is equal to one. This is positive number\n"
}
elsif ($x*1) {
print "x is larger then one and not accepted\n"
elsif ($x*2) {
print "x is larger then two and not accepted\n"
}

никога няма да отпечата x is larger then two and not accepted, тъй като в момента, в който някое условие бъде изпълнено, след изпълнение на съответния блок програмата излиза от условния оператор без да проверява другите блокове.

Третата част стои след последния блок на elsif и се изпълнява само, ако нито едно условие не бъде изпълнено. Дефинира се по следния начин:

else {
statement;
statement;
...
}

Следния код:

if ($x==1) {
print "x is equal to one. This is positive number\n"
}
elsif ($x==2) {
print "x is equl to two. This is positive number\n"
}
elsif ($x*2) {
print "x is larger than two. X is positive number\n"
}
else {
print "x is qual to 0 or it is negative number\n"
}

Ще отпечата x is equal to one. This is positive number ако променливата $x е 1, x is equal to two. This is positive number, ако е 2, x is larger than two. X is positive number, ако е число, по-голямо от 2 или x is qual to 0 or it is negative number ако нито едно от горните условия не е изпълнено.

Съществува друг оператор, който се изпълнява във всички случаи, когато условието му не е изпълнено. Този оператор има вида

unless (expresion) {
statement;
statement;
...
}

При този оператор не съществува else - такава конструкция е недопустима. Ето един пример за изполването му:

unless ($destination eq $home) {
print "I'm not going home.\n";
}

Този ще се изпълнява винаги, когато $destination и $home не съвпадат.

-Цикли

В Perl съществуват три оператора за цикъл. Ще разгледаме всеки един от тях поотделно

Операторът for има следня синтаксис:

for (expresion1; expresion2; expresion3) {
statement;
statement;
...
}

Като expresion1 се изпълнява преди началото на цикъла и обикновено се използва за присвояване на начална стойност на променливата, следяща повторението на цикъла, expresion2 е условието, определящо дали блокът ще бъде изпълнен, цикълът се изпълнява само ако expresion2 е true, в противен случай се излиза от цикъла; expresion3 се изпълнява след всеки цикъл и обикновено се използва за промяна на стойноста на управляващата променлива. Например можете да имате следния цикъл: for ($i=1; $i*100; $i+=$n) {
statement
...
}

При който променливата $i започва със стойност 1, при всяко повторение и се прибавя стойността на променливата $n и цикълът се изпълнява докато $i е по-малко от 100.

Цикълът определен от операторът while се изпълнява докато е изпълнено неговото условие и има следния вид:

while (expresion) {
statement;
statement;
...
}
continue {
statement;
statement;
...
}

Частта continue се изпълнява след всеки цикъл и обикновено се използва за ново изчисление на стойността на expresion.

За разлика от тях, цикълът foreach се изпълнява толкова пъти, колкото са елементита в подаденяи му параметър. Синтаксиса му е

foreach $var (@list) {
statement;
statement;
...
}

На променливата $var и се присвоява подред всеки елемент от масива @list и се изпълнява блока. Ако в блока бъде променена стойността на $var, това се отразява и в масива, тъй като напрактика $var се явява указател към един от елементите на масива.

Функции join и split

join("arg1", arg2, arg2...);

Тази функция вкарва първия аргумент между всеки два от подадените и аргументи.

split(/arg/, $var); Тази функция разделя низа $var на отделни променливи в точките, указани от arg (arg е символа, който се смята за раделител на отделните променливи, например можете да отделите всички думи от един низ използвайки функцията split(/ /, $var); навсякъде, където има интервал се преминава към следващия елемент от масива). Тази функция не запазва променения низ и ако искате да извлечете отделните елементи трябва да я присовите на масив.

-Обработка на уеб-форми

Създаването на една потребителсака форма има 2 етапа - създаване на съмата форма, което се осъществява чрез HTML, и написването на CGI скрипта, който да я обработи. Когато потребителя попълни формата и натисне бутона Submit, информацията се изпраща на CGI скрипта за обработка. Има два метода за изпращане на информацията - GET и POST. Метода се задава като стойност на атрибута METHOD на тага *FORM* в HTML страницата.
Когато се използва метода GET, браузърът вписва въведените във формата данни в края на URL адреса, който се предава на HTTP-сървер. Методът е удобен за формиране на заявки към баззи данни, чието съдържание редовно се обновява. Приемайки информация от формата, CGI скрипта я записва в променливата QUERY_STRING. Ако във формата има прекалено много данни, то в подобна структура могат да възникнат проблеми и ще предпочетете използването на метода POST.

При този метод браузъра предава приетите данни така, сякаш са набрани от клавиатурата, т.е. чрез стандартното входно устройство (STDIN). Сервъра не салага символа EOF в края на данните, затова за правилното прочитане на данните от STDIN програмата трябва да използва променливата CONTENT_LENGTH. По-нататък CGI скрипта извлича параметрите от тази променлива по същия начин, както и от която и да е променлива, например ассоциативен масив. Метода POST е по-ефективен и надежден от GET, тъй като всяка операционна система налага различни ограничения на максималното количество данни, което може да се изпрати с помощта на GET. Например форми, използващи поле *TEXTAREA*, позволяват въвеждането на на голямо количество текст и задължително трябва да ползват POST.

Данните, които постъпват от формата, имат следния вид:

NAME1=VALUE1&NAME2=VALUE2&NAME3=VALUE3

NAME са имета на елементите от формата, определени от атрибута NAME, a VALUE - информацията, въведена от потребителя в съответното поле. Ако формата съдържа няколко елемента, то двойките NAME=VALUE се разделят със символа &.

Тъй като при използване на метода GET данните от формата постъпват като част от URL адреса, те не могат да съдържат интервали и други специални символи, използването на които URL на допуска, а също и символи, имащи в URL друго значение, като наклонена черта например, като тези ограничения се прехвърлят и върху метода POST. За изпълняването на условията, Web браузъра кодира информация, представена от потребителя. Всички интервали се заменят със символа +, вместо управляващите и някои други символи се слага шеснайсетичния им еквивалент. Ето и символите, които се кодират от браузъра:

Символ Код

\t (tab) %09
\n (return) %0AM
(space) +
/ %2F
~ %7E
: %3A
; %3B
@ %40
& %26

При ползване на метода GET, информацията се подава като част от URL-to и пристига в CGI скрипта като съдържание на QUERY_STRING, което е ключова променлива от асоциативния масив %ENV. Ако информацията е изпратена чрез метода POST, сървъра подава информацията като през стандартно входно устройство (*STDIN*). Вашият скрипт трябва да се съобразява с метода на подаване на информацията. След като запишете информацията в една скаларна променлива (използвайки функцията read при метода POST или съдържанието на QUERY_STRING при метода GET). Функцията read има следния вид:

read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});

където $buffer е името на променливата. Можете да зададете каквото си искате име; в нашите примери ще използваме това.

След като вече имате информацията, вие трябва да разделите отделните елементи от формата. Всеки елемент си има име NAME и съответната информация, вписана за този елемент от потребителя (избрания радиобутон, текста който е написал и т.н.) е VALUE. Както вече казахме, отделните двойки NAME-VALUE пристигат разделени със знака &; съответно трябва да използвате функцията split за да разделите елементите и да ги запишете в масив:

@pairs = split(/&/, $buffer);

Така вече имате масив с отделни двойки NAME VALUE. Следващата част е декодирането на информацията, т.е. замяната на всички символи, кодирани от браузъра с оригиналните символи. Първо се разделят елементите и съответните им стойности в отделни променливи, после плюсовете се превръщат в интервали, накрая чрез функцията pack() се декодират и останалите символи:

foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}

Операторът =~ проверява всеки знак от низа на който е приложен за символа, затворен между първите скоби и го заменя с втория символ. Чрез последния ред от кода, $FORM{$name} = $value; създаваме асоциативния масив %FORM, съдържащ ключови променливи - имената на елементите от формата, и асоциирани променливи - съответните стойности.

След като сте обработили информацията във формата, вие трябва да направите нещо с нея: да я запазите във файл, да я пратите по E-mail, да изведете нова HTML страница или всичките наведнъж. Това е и следващата част от вашия скрипт. Във всички случаи ще трябва да изведете някакъв документ, с който да уведомите потребителя, че процедурата е успешна и да му дадете възможност да продължи. Можете също да създадете скрипт, чрез който потребителя подава заявка на базза данни и скрипта му извежда съответната информация. За това прегледайте частта CGI: генериране на HTML.

-Използване на файлове

Едно от основните неща при CGI скриптовете е записването на файлове и тяхното четене. За тази цел се използва процедурата open:

open(handle, "filename");

където filename е името на файла, а handle - произволен индентификатор, който ще се използва за функциите, работещи с файла. Ако пред filename (но вътре в кавичките), стои знакът *, файлът се презаписва - т.е. цялата предишна информация се изтрива и може да бъде записана нова. Ако стои **, файлът се отваря и новата информация се записва в края му. Ако няма нито един от тези два знака, файлът се отваря само за четене и записът в него е забранен.

Може би ще се наложи да указвате пълния път до файлът, който отваряте, в противен случай сървърът може да създаде проблеми. Също така трябва да имате разрешение от съръвра, за отваряне и записване на файлове. Друг проблем е, че процедурата open не проверява дали файлът, който трябва да отвори, съществува. За това трябва да използвате следния вид на процедурата:

open(handle, "filename") or dienice("arg");

където dienice е предварително написана субпроцедура, изкарваща съобщение за грешка, а arg е съобщението за грешка. В противен случай програмата ви ще продължи да работи, дори ако файлът не е отворен. Ето и кода за субпроцедурата:

sub dienice {
my($msg) = @_;
print "*h2*Error*/h2*\n";
print $msg;
exit;
}

Тъй като скрипта може да бъде извикан от няколко потребителя едновременно, вие трябва да забраните достъпа до файла, с който работите, докато не приключите с него. В противен случай всички скриптове ще записват в него едновреммено, което не само ще обърка информацията, но може да доведе и до нейната загуба. Това става щрез процедурата flock(handle, 2), където handle е индентификатора на файла, който сте му дали при отварянето му. След като скрипта приключи изпълнението си, той ще разреши достъпа до файла. Тази процедура е ефективна, ако всички CGI скриптове ползващи този файл, ползват процедурата flock. Когато става въпрос за извикването на един и същи скрипт няколко пъти, всичките ще ползват едни и същи процедури, следователно няма да има проблеми. Ако същия файл се използва от друг скрипт, който не ползва процедурата flock, той може да пише и чете от файла едновреммено с вашия скрипт.

Тъй като скрипта може би ще трябва да изчака друго копие от скрипта да приключи записването си във файла, ще трябва да върнете позицията във файла чрез следната процедура:

seek(handle, offset, whence);

където, offset е броя линии след whence, където да започне записа. whence може да има следните стойности:
0 - началото на файла
1 - текущата позиция във файла
2 - края на файла

След като сте свършили работата си с файла, добре е да го затворите чрез процедурата close(handle);. След като скрипта приключи изпълнението си, той затваря всички отворени файлове.

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

$var =

Присвоява на скалара $var един ред от файла handle, докато

@var =

присвоява на масива @var всички редове от файла handle.

-Генериране на HTML код

Чрез вашия CGI скрипт вие можете да генерирате нова HTML страница, примерно за да му благодарите за учасието или да го уведомите че информацията е била изпратена успешно. Тъй като не отпечатвате текста на екрана, а в браузър, вие трябва да укажете че следващия текст ще е HTML код. За целта, преди да отпечатате каквото и да било, трябва да впишете

print "Content-type:text/html\n\n";

Ако не укажете по този начин че ще отпечатвате HTML документ, вие ще получите "Internal Server Error", когато се опитате да изпълните CGI скрипта.

Отпечатването става по стандартния начин - чрез командата print. Но вие трябва да отпечатате всички необходими тагове. Това не е излишно усложнение, напротив - то ви дава много повече възможности. Вашия скрипт трябва да започне с отпечатването на тага *HTML* и след това да впише всичко необходимо. Ето един примерен скрипт:

#!/usr/bin/perl

print "Content-type:text/html\n\n";

print "*html**head**title*Test Page*/title**/head*\n";
print "*body*\n";
print "*h2*Thank you!*/h2*\n"
print "*/body**/html*";

Ако създавате хипервръзка, вероятно ще искате да сложите адреса в кавички. Има много причини да желаете да използвате кавички във вашия HTML код, но те са част от CGI кода. За това, за да сложите кавички или друг специален сивмол, който би бил възприет като част от кода на Perl, във вашия HTML код, трябва да използвате системата на Perl за отпечатване на специални символи - а именно да сложите наклонена черта пред символа. В случая с кавишкие вашия код би изглеждал така:

print "*a href=\"../main.html\"*Back to main page*/a*"/n;

-Специални променливи, предавани през CGI

Когато един CGI скрипт бъде извикан, сървъра му подава и един асоциативен масив със следното съдържание:

ключова променлива и асоциирана стойност

DOCUMENT_ROOT Root сиректорията на сървъра
HTTP_COOKIE Cookie -то на потребителя, ако има такова
HTTP_HOST host -а на сървъра
HTTP_REFERER адреса на страницата, от която е извикан скрипта
HTTP_USER_AGENT Типа браузър на потребителя
HTTPS Тази променлива е "on" ако скрипта е извикан през защитен (secure) сървър
PATH системната директория на сървъра
QUERY_STRING въведена от потребителя информация при метода GET
REMOTE_ADDR IP адреса на потребителя
REMOTE_HOST host -a на потребителя, ако има такъв, в противен случай неговия IP адрес
REMOTE_USER username -a на потребителя, ако страницата от който е извикан скрипта изисква такъв
REQUEST_METHOD Метода с който е извикан скрипта (GET или POST)
SERVER_ADMIN E-mail -а на администратора на сървъра
SERVER_NAME Домейна на сървъра
Цялата тази информация се записва автоматично в асоциативния масив %ENV всеки път, когато е извикан скрипта. Вие можете да проверите и използвате която и да е част, по стандартния метод (виж Структура на Perl/CGI програма).

Това са основните неща, които трябва да знаете за Perl за да обработвате форми. Можете да създавате анкети, форми за регистрация и други потребителски форми за вашия сайт. Това е първата, основна стъпка към програмирането в уеб. Много често се налага да напишете отделна форма за изпращане на мнение за сайта, книга за гости, регистрация, анкета или друг тип форма, изискваща информация от потребителя, или такава която да извежда определена информация на базата на въведени от него параметри.

Perl е създаден с идеята да обработва информация и е идеално решение, когато не ви се налага да пишете сложни CGI скриптове, често и цели сайтове, базирани на CGI. Типичен пример за сайт, основаващ се изцяло на програмирането в уеб, са форумире и уеб-базираните пощи. Имайте предвид, че макар и да можете да изпращате E-mail през уеб-сървъра, чрез скрипт написан на Perl, той е относително тромав език и не е подходящ за цялостни сайтове, а само за обработка на форми.


Надявам се урока да ви е от полза. Не може да става дума за авторски права, защото все пак го има вече в учебник.


/ Трябва да сте регистриран за да напишете коментар /