Логические операторы && (AND) и || (OR). 4 страница
$со = new CGI:
$greetingcookie = $co->cookie(
-name=>'greetings',
-value=>\%greetings,
-expires=>'+365d' );
print $co->header(-cookie=>$greetingcookie);
Заметьте, что для создания теневой посылки вы передаете ее в качестве именованного параметра CGI-методу header.
Как прочитать теневую посылку.Для чтения теневой посылки используется обычный CGI-метод, получающий в качестве параметра имя посылки. После этой операции можно использовать данные хэша %greetings:
$со = new CGI;
%greetings = $co->cookie('greeting');
print $greetings{'name'}
Вот и вся работа с теневыми посылками. Но имейте в виду, что многие пользователи не желают, чтобы программы хранили какие бы то ни было данные на их машинах.
Листинг 5.17. Hellocookie.pl
#!/usr/bin/perl
use CGI;
$co = new CGI;
%greetings = $co->cookie('greeting');
if ($co->param('name')) {$greetings{'name'} =$co->param('name')}
print $greetings{'name'};
if ($co->param('birthday') )
=~ m/\d\d\/\d\d/)
{$greetings{'birthday'} = $co->param('birthday');}
($date, $month, $year) = (lockaltime)[3, 4, 5];
$date = join ("/", $month + 1, $day);
if(exists($greetings{'name'})) {
$greetingstring = "Hello ". $greetings{'name'};
$greetingstring .= ", happy birthday!" if ($date eq $greetings{'birthday'});
$greetingstring =~ s/</</;
$promt = "If you want to change this page's settings,
just enter new data below.";
} else { $promt = "To have this page greet you next time,
enter your data below "; }
$greetingcookie = $co->cookie(
-name=>'greetings',
-value=>\%greetings,
-expires=>'+365d' );
if ($co->param('name') || $co->param('birthday')) {
print $co->header(-cookie=>$greetingcookie);
} else { print $co->header;}
$co->start_html(-title=>"Cookie Example",), $co->center(
$co->h1("Cookie Example"), $co->p, $co->h1("$greetingstring"),
$promt, $co->startform, "Your name: ",
$co->textfield(
-name=>'name',
-default=>'',
-override=>1 ), $co->p,
"Your birthday (mm/dd): ", $co->textfield(
-name=>'birthday',
-default=>'',
-override=>1
),
$co->p, $co->submit, $co->reset,
$co->endform), $co->end_html;
Доступ к базам данных.
Язык программирования Perl превратился из инструмента, используемого преимущественно администраторами Unix-систем, в наиболее распространенную платформу разработки для World Wide Web. Perl не предназначался изначально для Web, но простота его использования и мощные функции для работы с текстом сделали естественным его применение для CGI-программирования. Сходным образом, MySQL со своей высокой скоростью и широкми возможностями стала очень привлекательным средством для веб-разработчиков. Естественно поэтому, что был разработан интерфейс Perl к MySQL, объединив, таким образом их достоинства.
В настоящий момент существуют два интерфейса между Perl и MySQL. Более ранний состоит из специализированного интерфейса Mysql.pm. Другой, более новый интерфейс является подключаемым модулем в комплекте DBI (DataBase Independent) - независимых от базы данных модулей. DBI является попыткой обеспечить общий Perl API для доступа к любым базам данных и предоставления более высокой переносимости. Интерфейс DBI стал наиболее надежным и стандартным, и разработчики MySQL рекомендуют пользоваться только DBI, поскольку дальнейшая разработка модуля Mysql.pm прекращена. Однако многие унаследованные системы все еще используют их, поэтому мы расскажем здесь и о них.
DBI.Рекомендуемым методом доступа к базам данных MySQL из Perl является интерфейс DBD/DBI. DBD/DBI означает DataBase Dependent/DataBase Independent (Зависимый от базы данных/Независимый от базы данных). Название связано с двухъярусной реализацией интерфейса. В нижнем ярусе находится зависимый от базы данных уровень. На нем существуют свои модули для каждого типа базы данных, доступного из Perl. Поверх этого уровня находится независимый от базы данных уровень. Это тот интерфейс, которым вы пользуетесь при доступе к базе данных. Выгода такой схемы в том, что программисту нужно знать только один API уровня независимости от базы данных. Когда появляется новая база данных, кому-нибудь нужно лишь написать для нее модуль DBD (зависимый), и она станет доступна всем программистам, использующим DBD/DBI.
Как и в любом модуле Perl, для получения доступа нужно указать DBI в директиве use:
#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);
use DBI;
При запуске программ Perl для MySQL/mSQL следует всегда задавать аргумент командной строки -w. Благодаря этому DBI будет перенаправлять все специфические для MySQL сообщения об ошибках на STDERR, и вы сможете увидеть ошибки, вызванные работой с базой данных, не прибегая к явной проверке их в программе.
Всякое взаимодействие между Perl, с одной стороны, и MySQL— с другой, производится с помощью объекта, известного как описатель базы данных (handle). Описатель базы данных (database handle) - это объект, представленный в Perl как скалярная ссылка и реализующий все методы, используемые для связи с базой данных. Одновременно можно открыть любое число описателей базы данных, ограничение накладывают только ресурсы системы. Метод connect() использует для создания описателя формат соединения DBI:servertype:database:hostname:port (имя узла и порта необязательны), дополнительными аргументами служат имя пользователя и пароль:
my $dbh= DBI->connect( 'DBI:mysql:mydata ', undef, undef);
my $dbh = DBI->connect('DBI:mysql:mydata', 'me', 'mypass"};
Атрибут servertype является именем специфического для базы данных DBD-модуля, в нашем случае «mysql» (обратите внимание на точное использование регистра). В первом варианте создается соединение с сервером MySQL на локальной машине через сокет Unix. Это наиболее эффективный способ связи с базой данных, который должен использоваться при соединении на локальном сервере. Если указано имя узла, оно используется для соединения с сервером на этом узле через стандартный порт, если только не задан и номер порта. Если при соединении с сервером MySQL вы не указываете имя пользователя и пароль, то пользователь, выполняющий программу, должен обладать достаточными привилегиями в базе данных MySQL.
В Perl 5 используются два соглашения по вызову модулей. В объектно-ориентированном синтаксисе для ссылки на метод определенного класса используется символ стрелки «->» (как в DBI->connect). Другой метод - использование непрямого синтаксиса, в котором за именем метода следует имя класса, а затем - аргументы. В последнем примере метод connect следовало бы записать как connect DBI 'DBI:mysql:mydata', 'me', 'mypass'.
После соединения с сервером MySQL описатель базы данных - во всех примерах этого раздела $dbh - становится шлюзом к базе данных. Например, так готовится запрос SQL:
$dbh->prepare($query);
При работе с MySQL можно включать в запрос другие базы данных, явно указывая их имена. Кроме того, в MySQL, при необходимости одновременного доступа к нескольким базам данных можно создать несколько описателей базы данных и использовать их совместно.
Для иллюстрации использования DBI рассмотрим следующие простые программы. В листинге 5.18 datashow.pl принимает в качестве параметра имя узла; при отсутствии параметра принимается имя «localhost». Затем программа выводит список всех баз данных, имеющихся на этом узле.
Листинг 5.18. Скрипт datashow.pl показывает базы данных, имеющиеся на сервере MySQL
#!/usr/bin/perl -w
use strict;
use CGI qw( standard);
use CGI Carp;
# Использовать модуль DBI use DBI CGI use_named_parameters(1),
my ($server, $sock, $host);
my $output = new CGI;
$server = param('server') or $server ="";
# Подготовить DBD-драйвер для MySQL
my $driver = DBI->install_driver('mysql');
my @databases = $driver->func($server '_ListDBs');
# Если параметр §databases не определен, # предполагаем, что на
# этом узле не запущен сервер MySQL Однако это может быть вызвано
# другими причинами Полный текст сообщения об ошибке
# можно получить проверив $DBI errmsg
if (not @databases) {
print header, start_html( title => Данные по $server , BGCOLOR => white ),
print <<END_OF_HTML, <H1>$server</h1>
Ha $server , по-видимому не запущен сервер mSQL </body></html> END_OF_HTML
exit(0)}
print header, start_html(title =>"Данные по $host", BGCOLOR => white),
print <<END_OF_HTML, <H1>$host</h1>
<P>Соединение с $host на сокете $sock<P>Базы данных <br><UL>
END_OF_HTML
foreach(@databases) {
print "<LI>$_\n";}
print << END_OF_HTML
</ul></body></html>
exit(0)
В листинге 5.19 tableshow.pl принимает в качестве параметров имя сервера базы данных (по умолчанию «localhost») и имя базы данных на этом сервере. Затем программа показывает все таблицы, имеющиеся в этой базе данных.
Листинг 5.19. Скрипт tableshow.pl выводит список всех таблиц в базе данных
#!/usr/bin/perl -w
use strict;
use CGI qw( standard);
use CGI Carp;
# Использовать модуль Msql.pm
use DBI;
CGI use_named_parameters(1);
my ($db);
my $output = new CGI;
$db = param( db ) or die( He указана база данных' );
# Connect to the requested server
my $dbh = DBI->connect( DBI mysql $db $server, undef, undef);
# Если не существует $dbh значит, попытка соединения с сервером
# базы данных не удалась Возможно, сервер не запущен,
# или не существует указанной базы данных
if (not $dbh) {
print header, start_html( title => Данные по $host => $db ,
BGCOLOR => white )
print <<END_OF_HTML <H1>$host</h1> <H2>$db</h2>
Попытка соединения не удалась по следующей причине <BR>
$DBI errstr </body></html> END_OF_HTML
exit(0) }
print header, start_html( title => Данные по $host => $db ,
BGCOLOR => white ), print <<END_OF_HTML,
<H1>$host</h1> <H2>$db</h2><р>
Таблицы:<br><UL>
END_OF_HTML
# $dbh->listtable возвращает массив таблиц,
# имеющихся в текущей базе данных.
my @tables = $dbh->func('_ListTables');
foreach (@tables) {
print "<LI>$_\n"; }
print <<END_OF_HTML; </ul>
</body></html> END_OF_HTML
exit(0);
И наконец, листинг 5.20 показывает, как вывести все сведения о некоторой таблице.
Листинг 5.20. Скрипт tabledump.pl выводит сведения об указанной таблице
#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);
use CGI::Carp;
# Использовать модуль DBI
use DBI;
CGI::use_named_parameters(1);
my ($db,$table);
my $output = new CGI;
$server = param('server') or $server = '';
$db = param('db') or die("He указана база данных !");
# Соединиться с указанным сервером.
my $dbh = DBI->connect("DBI:mysql:$db:$server", undef, undef);
# Готовим запрос к серверу, требующий все данные таблицы.
my $table_data = $dbh->prepare("select * from $table");
# Посылаем запрос серверу.
$table_data->execute;
# Если возвращаемое значение не определено, таблица не существует # или пуста; мы не проверяем, что из двух верно,
if (not $table_data) {
print header, start_html('title'=>
"Данные no $host => $db => $table", 'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>$host</h1><H2>$db</h2>
Таблицы'$table' нет в $db на $host.
</body></html>
END_OF_HTML
exit(0); }
# Теперь мы знаем, что есть данные для выдачи. Сначала выведем
# структуру таблицы.
print header, start_html('title'=>"Данные по $host => $db =>
$table",'BGCOLOR'=>'white');
print <<END_OF_HTML; <H1>$host</h1> <H2>$db</h2>
<H3>$table</h3><p><TABLE BORDER> <CAPTION>Поля</caption> <TR>
<TH>Поле<TH>Тип<TH>Pa3Mep<TH>NOT NULL </tr> <UL>
END_OF_HTML
# $table_data->name возвращает ссылку
# на массив полей таблицы.
my @fields = @{$table_data->NAME};
# $table_data->type возвращает ссылку на массив типов полей.
# Возвращаемые типы имеют стандартные обозначения SQL,
# а не специфические для MySQL.
my @types = @{$table_data->TYPE};
# $table_data->is_not_null возвращает ссылку на массив типа Boolean,
# указывающий, в каких полях установлен флаг 'NOT NULL'.
my @not_null = @{$table_data->is_not_null};
# $table_data->length возвращает ссылку на массив длин полей. Они
фиксированные для типов INT и REAL, но переменные (
# заданные при создании таблицы) для CHAR.
my @length = @{$table_data->length};
# Все перечисленные выше массивы возвращаются в одном и том же
# порядке, поэтому $fields[0],$types[0], $not_null[0] and
# $length[0] относятся к одному полю.
foreach $field (0. .$#fields) {
print "<TR>\n"; print "<TD>$fields[$field]<TD>$types[$field]<TD>";
print $length[$field] if $types[$field] eq 'SQL_CHAR';
print "<TD>";
print 'Y' if ($not_null[$field]);
print "</tr>\n"; }
print <<END_OF_HTML; </table>
<P><B>Data</b><br><OL>
END_OF_HTML
#построчно перемещаемся по данным: DBI::fetchrow_array().
# Мы сохраним данные в массиве в таком же порядке, как и в
# массивах (@fields, @types, etc.), которые мы создали раньше.
while(my((@data)=$table_data->fetchrow_array) {
print "<LI>\n<UL>";
for (0..$#data) {
print "<LI>$fields[$_] => $data[$_]</li>\n";
}
print "</ul></li>"; }
print <<END_OF_HTML;
</ol>
</body></html>
END_OF_HTML
Пример приложения, использующего DBI.DBI допускает любые SQL-запросы, поддерживаемые MySQL. Например, рассмотрим базу данных, используемую в школе для ведения учета учащихся, состава классов, результатов экзаменов и т. д. База данных должна содержать несколько таблиц: одну для данных о предметах, другую для данных об учащихся, таблицу для списка экзаменов и по одной таблице для каждого экзамена. Возможность MySQL выбирать данные из нескольких таблиц, используя объединение таблиц, позволяет совместно использовать таблицы как согласованное целое для создания приложения, облегчающего работу учителя.
Для начала мы хотим учесть данные об экзаменах по разным предметам. Для этого нам нужна таблица, содержащая названия и числовые идентификаторы для экзаменов. Нам потребуется также отдельная таблица для каждого экзамена. В этой таблице будут содержаться результаты испытаний для всех учащихся, а также высший балл для проведения сравнения. Таблица test имеет следующую структуру:
CREATE TABLE test (
id INT NOT NULL AUTO_INCREMENT, name CHAR(100),
subject INT, num INT
Для каждого отдельного экзамена структура таблицы такая:
CREATE TABLE t1 (
id INT NOT NULL, q1 INT, q2 INT, q3 INT, q4 INT, total INT )
К имени таблицы t присоединен идентификатор экзамена из таблицы test. При создании таблицы пользователь определяет количество вопросов. Поле total содержит сумму баллов по всем вопросам.
Программа, обрабатывающая данные экзаменов, называется test.pl. Эта программа, текст которой следует ниже, позволяет только добавлять новые экзамены. Просмотр экзаменов и их изменение не реализованы и оставлены в качестве упражнения.
Этот сценарий хорошо показывает возможности DBI:
Листинг 5.21. Скрипт test.pl добавляет новые экзамены
#!/usr/bin/perl -w
use strict;
require my_end;
use CGI qw(:standard);
my $output = new CGI;
use_named_parameters(1);
# Использовать модуль DBI.
use DBI;
# DBI::connect() использует формат 'DBI:driver:database'
# используется драйвер MySQL и открывается база данных 'teach',
my $dbh = DBI->connect('DBI:mysql:teach');
# Операция добавления распределена между тремя функциями. Add -
# выводит пользователю форму шаблона для создания нового экзамена, sub add {
$subject = param('subject') if (param('subjects')); $subject = "" if $subject eq 'all';
print header, start_html('title'=>'Create a New Test', 'BGCOLOR'=>'white');
print <<END_OF_HTML; <Н1>Создание нового экзамена</п1>
<FORM ACTION="test.pl" METHOD=POST>
<INPUT TYPE=HIDDEN NAME="action" VALUE="add2"> Предмет: END_OF_HTML
my @ids = ();
my %subjects = ();
my $out2 = $dbh->prepare("select id,name from subject order by name");
$out2->execute;
while(my($id,$subject)=$out2->fetchrow_array) {push(@ids,$id);
$subjects{"$id"}'= $subject; }
print popup_menu(
'name'=>'subjects',
'values'=>[@ids],
'default'=>$subject,
'labels'=>\%subjects);
print <<END_OF_HTML; <br>
Число вопросов: <INPUT NAME="num" SIZE=5><br>
Название или идентификатор (например, дата) экзамена:
<INPUT NAME="name" SIZE=20> <Р>
<INPUT TYPE=SUBMIT VALUE=" Следующая страница ">
<INPUT TYPE=RESET> </form></body></html> END_OF_HTML }
Эта функция выводит форму, позволяющую пользователю выбрать предмет для экзамена, а также количество вопросов и название. Для вывода списка имеющихся предметов выполняется запрос к таблице предметов. При выполнении в DBI запроса SELECT он должен быть сначала подготовлен, а затем выполнен. Функция DBI::prepare полезна при работе с некоторыми серверами баз данных, позволяющими осуществить операции над подготовленными запросами, прежде чем выполнить их. Для MySQL это означает лишь запоминание запроса до вызова функции DBI:: execute .
Результаты работы этой функции посылаются функции add2, как показано ниже:
Листинг 5.21. Продолжение скрипта test.pl
sub add2 {
my $subject = param('subjects');
my $num = param('num');
$name = param('name') if param('name');
my $out = $dbh->prepare("select name from subject where
id=$subject");
$out->execute;
my ($subname) = $out->fetchrow_array;
print header, start_html('title'=>"Создание экзамена по предмету $subname", 'BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1> Создание экзамена по предмету $subname</h1> <h2>$name</h2>
<P><FORM ACTION="test.pl" METHOD=POST>
<INPUT TYPE=HIDDEN NAME="action" VALUE="add3">
<INPUT TYPE=HIDDEN NAME="subjects" VALUE="$subject">
<INPUT TYPE=HIDDEN NAME="num" VALUE="$num">
<INPUT TYPE=HIDDEN NAME="name" VALUE="$name">
Введите количество баллов за каждый правильный ответ.
Сумма баллов не обязательно должна равняться 100.
<Р> END_OF_HTML
for (1.,$num) {
print qq%$_: <INPUT NAME="q$_" SIZE=3> %;
if (not $_ % 5) { print "<br>\n", }
}
print <<END_OF_HTML;
<P>Введите текст экзамена:<br>
<TEXTAREA NAME="test" ROWS=20 COLS=60>
</textarea><P>
<INPUT TYPE=SUBMIT VALUE="Ввести экзамен ">
<INPUT TYPE=RESET>
</form></body></html>
END_OF_HTML
}
Эта функция динамически генерирует форму для экзамена, основываясь на параметрах, введенных в предыдущей форме. Пользователь может ввести количество баллов для каждого вопроса экзамена и полный текст самого экзамена. Выходные данные этой функции посылаются завершающей функции add3, как показано ниже:
Листинг 5.21. Продолжение скрипта test.pl
sub add3 {
my $subject = param('subjects');
my $num = param('num');
$name = param('name') if param('name');
my $qname;
($qname = $name) =" s/'/\\'/g;
my $q1 = "insert into test (id, name, subject, num)
values ("'', $qname', $subject, $num)";
my $in = $dbh->prepare($q1);
$in->execute;
# Извлечем значение ID , которое MySQL создал для нас
my $id = $in->insertid,
my $query = "create table t$id (id INT NOT NULL, ";
my $def = "insert into t$id values ( 0, ";
my $total = 0;
my @qs = grep(/"q\d+$/,param);
foreach (@qs) {
$query = $_ " INT,\n',
my $value = 0,
$value = param($_) if param($_);
$def = '$value, ";
$total += $value; }
$query = "total INT\n)"; $def = "$total)',
my $in2 = $dbh->prepare($query);
$in2->execute;
my $in3 = $dbh->prepare($def);
$in3->execute,
open(TEST,">teach/tests/$id") or die('A: $id $' ),
print TEST param('test'), \n",
close TEST;
print header, start_html('title =>'Экзамен создан', ;'BGCOLOR'=>'white');
print <<END_OF_HTML; <Н1>Экзамен создан</H1> <p>Экзамен создан<P>
<A HREF='..'>Перейти</а> на домашнюю страницу 'В помощь учителю'.<br>
<А HREF='test.pl'>перейти</a> на главную страницу экзаменов.<br>
<А HREF= 'test.pl'?action=add'>Добавить</a> следующий экзамен
</body></html>
END_OF_HTML
}
MysqlPerl.Монти Видениус, автор MySQL, написал также и интерфейс Perl к MySQL, Mysql.pm. Mysql.pm входит составной частью в пакет msql-mysql-modules Йохена Видмана (Jochen Wiedmann).
Чтобы показать некоторые функции Mysql.pm, вернемся к примеру с экзаменами. Займемся таблицей предметов. Ее структура описана выше.
Чтобы продемонстрировать, как работает Mysql.pm, мы подробно изучим ту часть subject.pl, которая позволяет пользователю добавлять предметы. Операция «add» (добавление) была так же разбита на две отдельные функции. Первая функция - изменения, выводит форму, позволяющую ввести название предмета и фамилию преподавателя:
Листинг 5.22. Скрипт subject.pl
#!/usr/bin/perl -w
use strict;
use CGI qw(:standard);
my $output = new CGI;
use_named_parameters(1);
# Использовать модуль MySQL
use MySQL;
# используется localhost и открывается база данных 'teach',
my $dbh = Mysql->connect(undef,'teach', 'teacher',undef);
# Операция добавления распределена между двумя функциями. Add -
# выводит пользователю форму шаблона для создания нового предмета,
&add if not (param('action'));
foreach(param('action')){
/add2/ and do{&add2; last;};
}
sub add {
print header, start_html('title'=>'Добавить предмет','BGCOLOR'=>'white');
print <<END_OF_HTML;
<H1>Добавить предмет</h1> <form METHOD=POST ACTION="subject.pl"> <P>Название предмета:<input TYPE=TEXT size=40 name="name" ><br>
Фамилия учителя: <input TYPE=TEXT size=40 name="teacher" ><br><INPUT TYPE=HIDDEN NAME="action" VALUE="add2">
<INPUT TYPE=SUBMIT VALUE="Следующая страница "><INPUT TYPE=RESET> </form><p><A HREF="..">Перейти</a> к домашней странице Помощи учителю. </body></html>
END_OF_HTML
}
Функция проверяет, не имеют ли какие-либо поля предустановленные значения. Это придает функции дополнительную гибкость, позволяя использовать ее как шаблон для классов со значениями по умолчанию, возможно, генерируемыми какой-либо другой CGI-программой.
Значения, полученные в первой части процесса добавления, передаются обратно CGI-программе для использования в функции add2. Функция add2 сначала проверяет, существует ли уже предмет. Если существует, то пользователю посылается сообщение об ошибке, и он может ввести другой предмет.
Листинг 5.22. Продолжение скрипта students.pl
sub add2 {
my $name = param('name');
my $query_name = "'".$name."'";
# Строим запрос для проверки существования предмета,
# Если введена фамилию учителя, отдельно проверяем фамилию,
# поскольку могут быть два курса с одинаковым названием, но
# разными учителями,
my ($teacher, $query_teacher, $query);
$query ="select id, name ";# from subject where name=$query_name";
if (param('teacher')) {
$teacher = param('teacher'); $query.=", teacher";
$query_teacher = "'".$teacher."'"; }
$query .= " from subject where name=$query_name";
if (param('teacher')) {$query .= " and teacher=$query_teacher";}
# Теперь посылаем запрос серверу MySQL.
my $out = $dbh->query($query);
# Проверяем значение $out->numrows, чтобы узнать, были ли
# возвращены какие-либо строки. Если были, то мы выходим с
# сообщением, что предмет уже существует
if ($out->numrows)
{ # Печать страницы 'Предмет уже существует'.
print header, start_html('title' =>'Предмет уже существует', 'BGCOLOR'=>'white');
print <<END_OF_HTML;
<h1>Предмет уже существует</H1><A HREF='..'>Перейти</a> на домашнюю страницу 'В помощь учителю'.<br>
<A HREF= 'subject.pl'?action=add'>Добавить</a> следующий предмет
</body></html>
END_OF_HTML
} else {
$out = $dbh->query("select * from subject");
my ($id) = $out->fetchrow+1;
# Теперь вводим информацию в базу данных, используя
# полученное число в качестве ID
$query = "INSERT INTO subject (id, name, teacher) VALUES ($id, $query_name, $query_teacher)"; $dbh->query($query);
print header, start_html('title' =>'Предмет создан', 'BGCOLOR'=>'white');
print <<END_OF_HTML;
<h1>Предмет создан</H1><A HREF='..'>Перейти</a> на домашнюю страницу 'В помощь учителю'.<br>
<A HREF= 'subject.pl'?action=add'>Добавить</a> следующий предмет
</body></html>
END_OF_HTML
}
Дата добавления: 2015-08-26; просмотров: 1218;