Журнал Системный Администратор, Октябрь 2004

Журнал Системный Администратор

Октябрь 2004

Цена: $4.5 US

  Подписаться

Зарегистриванные пользователи, пожалуйста следуйте этой ссылке

Версия для печати Вернуться к оглавлению

Внутренний веб-сервер

Сергей Супрунов

Что админу хорошо, то пользователю – смерть.

 

Народная мудрость

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

Почему был выбран столь экзотический способ? Во-первых, Perl я знаю несколько лучше, чем Delphi или C++ Builder. Во-вторых, такой подход без лишних усилий позволяет создавать клиент-серверные приложения, с которыми одновременно могут работать сотни пользователей, не заботясь о разработке клиентских программ – с этой ролью отлично справится любой браузер. В-третьих, налицо независимость от конкретных платформ и операционных систем – для сервера достаточно, чтобы на нем мог работать Apache + Perl (вы можете использовать и другую связку), от клиентов требуется лишь поддержка какого-нибудь графического обозревателя. И наконец, времени на разработку и сопровождение ПО в данном случае затрачивается заметно меньше, чем при «традиционных» способах.

Для наглядности рассмотрим в общих чертах процесс разработки незатейливого приложения: каталога пользователей ADSL, в который будут заноситься сведения об абонентах (фамилия, адрес, номер телефона), параметры конфигурации (IP-адреса, интерфейсы, номера PVC), параметры линии (длина, диаметр жилы, сопротивление шлейфа) и т. д.

В целях экономии места будет рассмотрена только первая функция (работа со сведениями об абонентах). Очень многие детали придется опустить. Так, не будут рассмотрены особенности Perl-модулей, подключаемых к нашим сценариям, способы работы с базой данных и т. д. Читателю понадобятся, по крайней мере, базовые знания Perl, HTML, PostgreSQL (или какой-нибудь другой СУБД). Если материал статьи окажется вам интересен, я постараюсь разложить все по полочкам в следующих статьях, оставляйте ваши отзывы на форуме журнала «Системный администратор».

Подготовительные мероприятия

Итак, прежде всего нам нужно собрать сервер. Я остановил свой выбор (поскольку все это уже есть и работает) на следующем ПО (правда, кое-что из этого списка уже пора бы обновить):

n  ОС: FreeBSD 5.2

n  Веб-сервер: Russian Apache 1.3.29

n  Язык программирования: Perl 5.6.1

n  СУБД: PostgreSQL 7.4.2

Рассматривать установку и конфигурирование всего этого я не буду – все довольно подробно описано и на страницах журнала, и на бескрайних просторах Интернета. Выбор именно этого ПО – вопрос, скорее, личных предпочтений, поскольку по каждому из пунктов можно привести массу как положительных, так и отрицательных доводов. В конце концов с тем же успехом (с поправкой на более высокие требования к ресурсам и вопросам безопасности) можно использовать и связку «Windows2003 – IIS – ASP – MSSQL».

Структура базы данных и доступ к СУБД

Начнем разработку с определения структуры БД. Нужно заметить, что это итерационный процесс, то есть обычно при разработке сценариев выясняется, что база данных должна быть несколько иной, потом корректировки вновь вносятся в код сценариев, снова исправляется БД и так до тех пор, пока разработчик не осознает, что эффект от дальнейшего улучшения уже не окупает затрат на исправления. Но мы остановимся на первой итерации, тем более что наша задача – показать сам принцип.

Итак, создадим БД с именем adsl, владельцем которой будет пользователь adsluser с паролем password. В ней нам потребуются следующие таблицы:

n  sessions – информация сеансов пользователей (см. далее):

n  id char(32) – идентификатор сессии;

n  a_session text – информация сессии;

n  login char(12) – имя пользователя;

n  password varchar – пароль пользователя.

n  users – информация об абонентах ADSL:

n  uid serial – уникальный идентификатор абонента;

n  name varchar – фамилия, имя, отчество;

n  address varchar – адрес проживания;

n  phone char(7) – номер телефона.

n  dslam– информация о ADSL-портах:

n  uid serial – уникальный идентификатор порта;

n  userid numeric – идентификатор подключенного на порт абонента;

n  num char(10) – номер порта (в виде nDSLAM/nBOARD/nPORT);

n  vlan numeric(4) – номер VLAN, соответствующей ADSL-порту;

n  vpi numeric(3) – номер VPI;

n  vci numeric(3) – номер VCI, присвоенный клиенту;

n  interface char(15) – имя интерфейса, на котором будет вестись учет трафика;

n  ipaddress inet – IP-адрес, сопоставленный с данным портом.

n  lines – характеристики линий связи:

n  uid serial – уникальный идентификатор линии;

n  portid numeric – идентификатор порта DSLAM, на который подключена эта линия;

n  length numeric(5) – длина линии в метрах;

n  diameter numeric(2,1) – диаметр жилы в миллиметрах;

n  impedance numeric(4) – сопротивление шлейфа в Омах.

 

И еще одна таблица для хранения служебной информации:

n  st_modules – список функциональных модулей:

n  name char(20) – имя модуля;

n  description varchar – описание модуля;

n  ink varchar – ссылка на сценарий модуля;

n  allow char(12)[] – массив, хранящий имена пользователей, которым позволено работать с данным модулем;

n  orderby numeric(2) – данное поле задает порядок вывода модулей на экран.

В данном случае мы минимально задействуем расширенные возможности PostgreSQL, что позволит почти ничего не менять при использовании, например, MySQL.

Шаблон сайта – модульный подход

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

Выглядеть базовый сценарий будет примерно так:

#!/usr/bin/perl –w

#-------------------------------------------- adsl.cgi

 

use My::Insite;

 

# Подключаемся к БД и создаем объект CGI для работы с HTTP

$dbh = My::Insite->DBConnect('adsl', 'adsluser', 'password');

$cgi = My::Insite->CGIStart();

 

# Считываем значение HTTP-параметра «action»

($action = $cgi->param('action')) or $action = '';

 

# Выполняем процедуру выхода

if($action eq 'logoff') { &doLogoff; }

 

# Процедура авторизации

if($action eq 'logon') {

    $savedLogin    = $cgi->param('login');

    $savedPassword = $cgi->param('password');

# ищем сессию для заявленного логина

    ($sSessId, $sPassword) = $dbh->selectrow_array('

           SELECT id, password FROM sessions WHERE login=?

                               ', undef, $savedLogin);

# если не нашли – повторный запрос авторизации

    if(!$sSessId) { &doLogon('Failed'); }

# если сессия есть, но пароль не соответствует введенному, повторный запрос авторизации

    if($sPassword ne $savedPassword) { &doLogon('Wrong'); }

 

# Если все нормально – сохраняем идентификатор сессии в cookie

    $cookie = $cgi->cookie(-name => 'sessid', -value => $sSessId);

    print $cgi->header(-cookie => $cookie);

    print 'Авторизация выполнена успешно.';

    print " <A href='adsl.cgi'>Продолжить...</A>";

    exit;

}

 

# action не имеет значения, пытаемся извлечь из cookie идентификатор сессии

$sessId = $cgi->cookie('sessid');

 

# Если безуспешно – уходим на авторизацию

if(!$sessId) { &doLogon('First'); }

 

# Если sessId есть, пытаемся получить пользователя этой сессии

($sLogin) = $dbh->selectrow_array('

    SELECT login FROM sessions WHERE id=?;

           ', undef, $sessId);

 

# Если удачно – открываем сессию, иначе – на авторизацию

if($sLogin) {

    $session = My::Insite->SessOpen($dbh, $sessId);

} else { &doLogon('Fialed'); }

 

# Выбираем из БД и выводим на экран список модулей

print $cgi->header;

$sth = $dbh->prepare('SELECT * FROM st_modules ORDER BY orderby;');

$sth->execute;

print "<P align='right'>Вы вошли под именем $sLogin | ";

print ' <A href="?action=logoff">Выход</A></P>';

while($rhash = $sth->fetchrow_hashref) {

# Печатать будем только те модули, для которых в поле allow есть имя вошедшего пользователя

    if($$rhash{allow} =~ m($sLogin)) {

           print "<DT><A href='$$rhash{link}'> $$rhash{name}</A>";

           print "<DD>$$rhash{description}<BR>";

    }

}

 

# Все закрываем (в принципе это не обязательно – все и так закроется)

My::Insite->SessClose($session);

My::Insite->DBDisconnect($dbh);

exit;

 

#-------------------------------------------- подпрограммы

sub doLogon {  # подпрограмма авторизации

    $status = shift @_;

    if($status eq 'Wrong') {

           $status = Неправильный логин или пароль.';

    } elsif($status eq 'Failed') {

           $status = Ошибка подключения данного пользователя.';

    } else {

           $status = Введите логин и пароль:';

    }

    print $cgi->header();

 

    print <<__HTML__;

<CENTER><H3>$status</H3><FORM method="POST">

<TABLE border="1"><TR><TD><TABLE>

<INPUT type="hidden" name="action" value="logon">

<TR><TD>Login:<TD><INPUT type="text" name="login" value="">

<TR><TD>Password:

    <TD><INPUT type="password" name="password" value="">

<TR><TD colspan="2" align="center">

    <INPUT type="submit" value="Войти">

</TABLE></TABLE></FORM></CENTER>

__HTML__

 

    exit;

}

 

sub doLogoff {  # подпрограмма закрытия сеанса

# Записываем cookie с истекшим «сроком годности» (отрицательное значение параметра expire), что уничтожит cookie в памяти

    $cookie = $cgi->cookie(-name   => 'sessid',

                         -value   => '',

                         -expires => '-1d');

    print $cgi->header(-cookie => $cookie);

    print '<HEADER>';

    print '<META http-equiv="refresh" content="1;url=adsl.cgi">';

    print '</HEADER>';

    print 'До новых встреч!';

    exit;

}

Задача данного сценария – выполнить авторизацию пользователя и предоставить ему список доступных для работы модулей. Управление поведением сценария осуществляется с помощью переменной «action», которая может иметь одно из следующих значений: logoff (закрыть сеанс), logon (выполнить процедуру авторизации, в ходе которой проверяется правильность пароля и открывается сессия, соответствующая данному пользователю, о чем делается запись в файлах cookie). Пустое значение данной переменной позволит вывести на экран перечень доступных модулей.

Список модулей хранится в БД, в таблице st_modules, откуда он выбирается и выводится на экран, причем отображаются только те модули, для которых в поле allow содержится имя текущего пользователя. Больше ничего от главного сценария не требуется. Для подключения к приложению очередного модуля достаточно поместить в папку cgi-bin реализующий его сценарий и добавить запись в таблицу st_modules. То есть в этой таблице будет что-то похожее:

adsl=> select link, name, allow from st_modules order by orderby;

 link       |          name            |       allow 

---------------------+------------------------------------------+----------------------

 adsl-users.cgi  | Абоненты ADSL            | {"admin","operator"}

 adsl-dslam.cgi  | Конфигурация DSLAM       | {"admin"}

 adsl-admin.cgi  | Модуль администратора    | {"admin"}

(записей: 3)

Результат работы сценария adsl.cgi представлен на рисунках 1 и 2.

Рисунок 1

Рисунок 2

Модуль My::Insite в моем случае будет размещаться по такому адресу: /usr/local/lib/perl5/site_perl/5.6.1/My/Insite.pm. Узнать пути, по которым Perl ищет подключаемые модули, позволяет специальная переменная @INC:

#!/usr/bin/perl

#-------------------- testpath.pl

 

$, = "\n";

print @INC;

exit;

На моей машине результат был получен следующий:

$ ./testpath.pl

/usr/local/lib/perl5/site_perl/5.6.1/mach

/usr/local/lib/perl5/site_perl/5.6.1

/usr/local/lib/perl5/site_perl

/usr/local/lib/perl5/5.6.1/BSDPAN

/usr/local/lib/perl5/5.6.1/mach

/usr/local/lib/perl5/5.6.1

Код модуля My::Insite представлен ниже:

package My::Insite;

 

use CGI;

use DBI;

use Apache::Session::Postgres;

 

sub CGIStart {

    return CGI->new;

}

 

sub DBConnect {

    my($obj, $dbName, $dbUser, $dbPwd) = @_;

    return DBI->connect('dbi:Pg:dbname='.$dbName, $dbUser, $dbPwd);

}

 

sub DBDisconnect {

    my($obj, $dbh) = @_;

    $dbh->disconnect;

    return(1);

}

 

sub SessOpen {

    my($obj, $dbh, $sessId) = @_;

    tie %session, 'Apache::Session::Postgres', $sessId,

                 {Handle => $dbh, LockHandle => $dbh};

    return(bless(\%session, $obj));

}

 

sub SessClose {

    my($obj, $session) = @_;

    untie(%$session);

    return(1);

}

 

return(1);

Как видите, сюда вынесены функции подключения к БД, работы с сессиями и т. д.

Может показаться, что в некоторых функциях нет смысла. Например, зачем создавать CGIStart, которая только и делает, что вызывает функцию new() модуля CGI? Не проще ли вызывать эту функцию самому и не захламлять модуль?

А теперь представьте, что вы решили вместо модуля CGI перейти на более функциональный. Что проще – переписывать все имеющиеся сценарии или изменить одну функцию в My::Insite?

Думаю, в этом модуле все понятно без комментариев. Если что непонятно – всегда под рукой man DBI, man CGI, man Apache::Session.

Доступ на сайт и Apache::Session

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

Пароли для простоты хранить и передавать будем в явном виде, признак правильного входа в приложение, а заодно и некоторые персональные настройки будем хранить, используя механизм сессий. В Perl это выглядит несколько сложнее, чем в PHP, зато проще сделать именно то, что нужно. Для работы понадобится модуль Apache::Session. Если на вашей системе такого нет, для FreeBSD его, как и большинство других модулей, можно установить из коллекции портов:

# cd /usr/ports/www/p5-Apache-Session

# make install

Более универсальный путь, пригодный практически для всех систем – использование архива CPAN. Этот метод описан на страницах руководства man perlmodinstall.

Так как база данных у нас есть, целесообразно для хранения сессионной информации использовать именно ее. Поэтому будем использовать подмодуль Apache::Session:: Postgres. Поскольку число пользователей нашего приложения ограничено и все они известны, то имеет смысл для каждого из них заранее создать сессию, в которой будут храниться все пользовательские данные, и при авторизации подключать именно ее. Такой подход позволит не беспокоиться об удалении старых сессий и о хранении идентификатора сессии между сеансами. Вручную создавать новую сессию не очень удобно, поэтому будем использовать такой небольшой сценарий:

#!/usr/bin/perl –w

#------------------------------------------ adsl-adduser.pl

 

use DBI;

use Apache::Session::Postgres;

 

$login    = $ARGV[0];   # первый аргумент – имя

$password = $ARGV[1];   # второй – пароль

 

# Запрашиваем все, что не передано в аргументах

if(!$login) {

    print 'Enter login: ';

    chomp($login = <>);

}

 

if(!$password) {

    print 'Enter password: ';

    chomp($password = <>);

}

 

# Создаем новую сессию и сразу закрываем

$dbh = DBI->connect('dbi:Pg:dbname=adsl', 'adsluser', 'password');

tie %session, 'Apache::Session::Postgres', undef,

               {Handle => $dbh, LockHandle => $dbh};

untie %session;

 

# В запись в таблице sessions, соответствующей нашему сеансу, добавляем имя пользователя и пароль, введенные выше

$pre = $dbh->prepare('

                 update sessions

                    set login = ?, password = ?

                    where login is null;

                    ');

$pre->execute($login, $password);

$dbh->disconnect;

exit;

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

Ну и раз информация сессий будет необходима нам в каждом модуле, то процедуры работы с ней вынесены в наш модуль My::Insite. Как все это будет работать – смотрите в листингах, приведенных в статье.

Взаимодействие с БД

Для работы с базой данных будем использовать Perl-модуль DBI с драйвером DBD::Pg. Данный модуль и нужный драйвер можно установить как из портов, так и из CPAN. Функции открытия и закрытия соединения вынесены в модуль My::Insite, остальное смотрите в коде конкретных модулей.

Модуль обработки информации об абоненте

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

Код модуля следующий:

#!/usr/bin/perl –w

#------------------------------------ adsl-users.cgi

 

use My::Insite;

 

$dbh = My::Insite->DBConnect('adsl', 'adsluser', 'password');

$cgi = My::Insite->CGIStart();

$action = $cgi->param('action');

 

# Пытаемся считать из cookie идентификатор сессии, если безуспешно – отправляем на авторизацию

$sessId = $cgi->cookie('sessid');

if(!$sessId) {

    &toLogon;

}

 

# Открываем сессию, или на авторизацию в случае ошибки

($sLogin) = $dbh->selectrow_array('

    SELECT login FROM sessions WHERE id=?;

                        ', undef, $sessId);

 

if($sLogin) {

    $session = My::Insite->SessOpen($dbh, $sessId);

} else {

    &toLogon;

}

 

# Проверяем, можно ли данному пользователю работать с этим модулем

($allow) = $dbh->selectrow_array('

        SELECT allow FROM st_modules WHERE link=?;',

            undef, 'adsl-users.cgi');

if($allow !~ m($sLogin)) {

    &toLogon;

}

 

print $cgi->header;

 

# Разбираем возможные действия

if   ($action eq ''          ) { &showUsers;  }

elsif($action eq 'user'      ) { &userForm;   }

elsif($action eq 'changeuser') { &changeUser; }

else { print 'Не могу выполнить: '.$action; }

 

My::Insite->SessClose($session);

My::Insite->DBDisconnect($dbh);

exit;

 

#------------------------------------- subroutines

 

sub showUsers {  # подпрограмма вывода списка абонентов

    $sth = $dbh->prepare('SELECT uid, name, address, phone

                          FROM users ORDER BY name;');

    $sth->execute;

 

    print '<TABLE><TR><TD><H3>Абоненты</H3>';

    print '<TD align="right"><A href="adsl.cgi">Главная</A>';

    print '<TR><TD colspan="2">';

    print '<TABLE><TR><TD align="right">';

    print '[ <A href="?action=user&type=add">

                 Добавить нового абонента</A> ]';

    print '<TR><TD><TABLE border=1><TR bgcolor=#AAAAFF>

                               <TH>Абонент

                               <TH>Адрес

                               <TH>Телефон

                               <TH>Действие;

 

    while(@res = $sth->fetchrow_array) {

       $oper = ($res[6] eq 'I'?'inlager':'outlager');

       print "<TR><TD>$res[1]<TD>$res[2]<TD>$res[3]<TD>

           [ <A href='?action=user&type=update&uid=$res[0] '>Изменить</A> ] ::

           [ <A href='?action=user&type=delete&uid=$res[0] '>Удалить</A> ]";

    }

    print '</TABLE><TR><TD align="right">';

    print '[ <A href="?action=user&type=add">Добавить нового абонента</A> ]';

    print '</TABLE></TABLE>';

    return;

}

 

sub userForm {  # выводит форму для манипуляций с данными

    $uid = $cgi->param('uid');

    $type = $cgi->param('type');

    if($type eq 'add') { $submitName = 'Добавить'; }

    elsif($type eq 'delete') { $submitName = 'Удалить'; }

    elsif($type eq 'update') { $submitName = 'Изменить'; }

    else {

           print 'Ошибка операции: '.$type;

           exit;

    }

    $header = $submitName.' абонента:';

 

    if($type ne 'add') {

        ($name, $address, $phone) = $dbh->selectrow_array('

        SELECT name, address, phone FROM users WHERE uid = ?;

           ', undef, $uid);

    } else {

       $name = $address = $phone = '';

    }

 

    if($type eq 'delete') {

       $in1 = "<B>$name</B>";

       $in2 = "<B>$address</B>";

       $in3 = "<B>$phone</B>";

    } else {

        $in1 = "<INPUT type='text' name='name' value='$name' size='35'>";

      $in2 = "<INPUT type='text' name='address' value='$address' size='35'>";

      $in3 = "<INPUT type='text' name='phone' value='$phone' size='7'>";

    }

 

    print <<__HTML__;

<CENTER><H3>$header</H3>

<FORM method="GET">

<TABLE border="1"><TR><TD><TABLE>

<INPUT type="hidden" name="action" value="changeuser">

<INPUT type="hidden" name="type" value="$type">

<INPUT type="hidden" name="uid" value="$uid">

<TR><TD>Абонент: <TD>$in1

<TR><TD>Адрес:   <TD>$in2

<TR><TD>Телефон: <TD>$in3

<TR><TD colspan="2"><HR>

<TR><TD>[ <A href="adsl-users.cgi?action=">Отмена</A> ]

    <TD align="right"><INPUT type="submit" value="$submitName">

</TABLE></TABLE></FORM></CENTER>

__HTML__

    exit;

}

 

sub changeUser {  # запись изменений в БД

    $type = $cgi->param('type');

    $uid = $cgi->param('uid');

    $name = $cgi->param('name');

    $address = $cgi->param('address');

    $phone = $cgi->param('phone');

 

    if($type eq 'add') {

        $res = $dbh->do('

        INSERT INTO users(name, address, phone)

                 VALUES(?, ?, ?);',

        undef, $name, $address, $phone);

    } elsif($type eq 'delete') {

        $res = $dbh->do('DELETE FROM users WHERE uid=?;', undef, $uid);

    } elsif($type eq 'update') {

        $res = $dbh->do('

        UPDATE users SET name=?, address=?, phone=? WHERE uid=?;

           ', undef, $name, $address, $phone, $uid);

    } else { print 'Ошибка операции: '.$type; }

 

    if($res) { print 'Операция выполнена успешно. '};

    print '<BR><A href="?action=">Продолжить...</A>';

    exit;

}

 

sub toLogon {

    print $cgi->header;

    print '<META http-equiv="refresh" content="1;url=adsl.cgi?account=logon">';

    print 'Ошибка входа. Перенаправление...';

    exit;

}

В данном случае для управления поведением сценария используется еще одна переменная – type. Если action определяет, на какую подпрограмму следует передавать управление, то type содержит информацию о том, что именно следует делать в данной подпрограмме.

Сгенерированный приложением список абонентов имеет вид, представленный на рисунке 3. Рисунок 4 демонстрирует форму для изменения данных.

Рисунок 3

Рисунок 4

Прочие модули

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

Что можно изменить?

Как известно, нет предела совершенству. Рассмотренный здесь пример был очень сильно урезан и упрощен, чтобы за деталями не потерялась суть и чтобы уложиться в рамки журнальной статьи. Однако, разрабатывая реальное приложение, имеет смысл сделать некоторые улучшения.

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

Можно сделать более гибкой систему разграничения доступа, помимо пользователей введя понятие групп пользователей, а также разграничивая права пользователей в пределах одного модуля (полный доступ, только чтение).

Механизм сессий используется очень слабо. Например, его можно использовать для передачи таких параметров как идентификатор пользователя (uid), вместо того чтобы делать это с помощью скрытых полей формы. Не совсем удобной выглядит необходимость в каждом модуле задавать логин и пароль для подключения к БД. Выносить это в модуль My::Insite неправильно (иначе будут сложности с использованием данного модуля в других приложениях для подключения к другим базам), а вот сделать что-то типа конфигурационного файла и брать нужные данные оттуда было бы намного лучше, поскольку в случае смены имени или пароля корректировка потребуется только в одном месте. В существенном улучшении нуждается проверка корректности вводимых данных, контроль ошибок, и т. д. В реальной жизни этим, конечно же, пренебрегать нельзя.

И вообще, можно сделать более удобный и красивый дизайн, добавить страничкам «динамизм» с помощью JavaScript (например, всплывающие подсказки, предупреждения и т. п.) и много еще чего хорошего и полезного.

Заключение

Ну что ж. Надеюсь, полученный результат хотя бы частично соответствует нашим ожиданиям, несмотря на множество недоработок, оставленных «за бортом». Мы получили гибкое, легко модифицируемое приложение, соответствующее большинству наших требований. В будущем его без труда можно расширить, добавив, например, модуль для работы с жалобами абонентов, для сбора статистики по потребленному трафику и оплатам и т. д. Единственное, чего мне в данный момент не хватает, это красивых отчетов, которые не стыдно было бы распечатать, сохранить в файл, отправить по электронной почте. Эта задача тоже решается довольно просто. Но об этом – в следующей статье.




Все права зарезервированы. Этот материал принадлежит или лицензирован компании PLARANA INC. Только для частного использования. Любое распространение запрещено без письменного разрешения PLARANA INC
Версия для печати Вернуться к оглавлению