20 ноября 2019 года    
Среда | 19:17    
Главная
 Новости
Базы данных
Безопасность PC
Всё о компьютерах
Графика и дизайн
Интернет-технологии
Мобильные устройства
Операционные системы
Программирование
Программы
Связь
Сети
 Документация
Статьи
Самоучители
 Общение
Форум







Разделы / Интернет-технологии / Perl

FAQ по perl и программированию для web.

Секция 2 из 2 - Предыдущая - Следующая

5. Web серверы

5.1 Как заставить WWW сервер исполнять CGI-программы?

Apache для Unix/Win32

Надо отредактировать конфигурационные файлы (я рассчитываю, что у вас default конфиги apache) (NB: Apache/1.3.6 и выше по умолчанию конфигурируется только файлом httpd.conf. Директивы все остались прежние, просто их слили в один файл)

Способ 1

srm.conf Директива ScriptAlias

ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/

и в файле access.conf прописать

<Directory /usr/local/apache/cgi-bin/>
 Options ExecCGI
  </Directory>
(если пригляделся, там нужно только раскоментировать опции) Это позволит вам помещать программы в каталог /usr/local/apache/cgi-bin/ и они будут видны по URL http://you/cgi-bin/program_name

Способ 2

Добавить в srm.conf директиву AddHandler cgi-script .cgi и вы сможете вызывать cgi-программу из любого каталога. Но она должна иметь окончание .cgi и для нее должно быть разрешено исполнение CGI (Options ExecCGI в access.conf, написано выше). Оба способа можно без проблем использовать совместно.

5.2 Мой CGI-скрипт не работает! Как установить причину?

Прежде чем читать дальше, убедитесь в том, что:

  • Вы запустили скрипт с командной строки (не через CGI!!!) и он работает, а не говорит "compilation stopped due to errors"
  • Скрипт не только работает, но и выдает нужный заголовок с пустой строкой после него
  • Для многопользовательских систем) Вы сделали тоже под пользователем, под которым работает сервер (su -m имя_пользователя -c script в Unix) и он столь же благополучно работает

5.3 Ошибки HTTP 500 и 403. Что это значит и что с ними делать

error_log для того и служит, чтобы туда смотреть :-)

Ошибка 403 "Forbidden"

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

Проверьте:

  • Может ли user (для apache под Unix это обычно nobody), под которым работает, сервер исполнять файл?
  • В частности, может ли сервер исполнять интерпретатор (в случае скриптовых языков)?
  • Можно ли для этого каталога исполнять программы CGI? (Options ExecCGI для apache)

Ошибка 500 "Internal Server Error"

Возникает в случае внутренней ошибки.

Проверьте:

  • Выводите ли вы строку "Content-Type:"?
  • Правильно ли вы ее выводите? (Content-type, Content_Type -- неправильно)
  • Заканчиваете ли вы хедеры пустой строкой? Нельзя написать print "Content-Type: text/html\n"; print "<HTML>\n"; Надо писать: print "Content-Type: text/html\n\n"; # Два "\n" print "<HTML>\n";

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

Совет: делайте use CGI::Carp qw(fatalsToBrowser) во время отладки, тогда вам выдадут сообщение об ошибке perl.

5.4 Почему $ENV{REMOTE_HOST} пуста?

apache устанавливает переменную окружения REMOTE_HOST, если ему разрешено проводить dns запросы для определения имени. Для 1.3.x по-умолчанию это выключено. Включается/выключается директивой HostNameLookups, которая может принимать 3 значения: on -- проводить запросы, off -- не запрашивать dns и double -- делать двойные запросы: запрашивать имя хоста, а затем по имени запрашивать ip адрес, для безопасности.

5.5 Почему $ENV{REMOTE_USER} пуста?

Переменная окружения REMOTE_USER устанавливается apache в случае, если скрипт защищен паролем. Как это сделать рассказано на http://www.apacheweek.com/features/userauth и http://www.apacheweek.com/features/dbmauth


6. Ускоряемся

6.1 Моя CGI программа работает слишком медленно/создает большую нагрузку на сервер/etc. Что делать?

  • Вкомплировать интерпретатор в сервер (mod_dtcl, mod_perl, pyapache для apache, серверы с поддержкой servlets, AOL server с его поддержкой tcl).
  • Переделать на FastCGI или аналогичную технологию.
  • Переписать ее на компилируемый язык, если написано на скриптовом. Тем самым вы избавитесь от оверхедов интерпретатора.
  • Взять более мощный сервер (IBM RS/6k F50, к примеру :-)

6.2 Что такое mod_perl?

Это perl, вкомпилированный в apache, что придает многие преимущества:

  • Скрипты и модули компилируются один раз, что значитльно увеличивает скорость ответа на запрос HTTP (до 2000%);
  • Можно написать свои обработчики различных стадий запроса;
  • Кеширование сединений с сервером БД -- еще прибавка к скорости;
  • Директива SSI #perl позволяет в server-parsed html вызывать функции perl;
  • секции <perl></perl> в конфиге apache, для его конфигурирования;

Правда, ничего не дается даром и самый важный недостаток mod_perl -- огромный расход памяти: если обычный apache занимает при работе обычно меньше мегабайта, то apache с mod_perl размером в 10-15 Mb -- вполне нормальное явление. Но, при правильной настройке, значительная часть этой памяти будет shared между копиями процессов.

Основной сайт про mod_perl - http://perl.apache.org, в частности великолепный mod_perl Guide на http://perl.apache.org/guide/.

6.3 В чем разница между скриптом для CGI и mod_perl?

Программа CGI исполняется один раз, за тем умирает. Под mod_perl же она загружается и исполняется до смерти сервера (в случае использования Apache::Registry, см. далее), поэтому она требует более аккуратного описания - закрывать файлы, не использовать много памяти, и тд.

Более подробно о переписывании программ CGI под mod_perl - http://perl.apache.org/guide/porting.html.

6.4 В чем разница между исполнением под Apache::Registry и Apache::PerlRun?

6.5 Как использовать постоянное соединение с БД под mod_perl?

6.6 Что такое FastCGI?


7. Использование других языков для написания CGI-программ

7.1 Можно ли писать CGI-программы не на Perl?

Да, можно. CGI не зависит от используемого языка программирования.

7.2 Какие существуют библиотеки на других языках для CGI?

Это далеко неполный список. (Почти полностью списано из CGI_metaFAQ от brian d foy).

C/C++

Haskell

По поводу Haskell http://wave.philol.msu.ru/fp/haskell/

Phyton

rexx

Ada

Java


8. БД на плоских файлах

8.1 Я хочу какую-нибудь простейшую БД и прямо сейчас!

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

Файл с данными может выглядеть так:

phones.data
Иванов И.И.::888-0000::Какая-то улица, 17, кв 40
Сидоров П.И.::888-8429::Другая улица, 5, кв 21
...... и тд.

тогда программа, которая читает данные, может быть примерно такого вида:

dump_phones.pl
#!/usr/bin/perl
$filename = 'phones.data';
# открываем файл
open DATA, $filename or die "Невозможно открыть $filename: $!";

# читаем построчно из файла
while (<DATA>) {
        chomp; # удаление символа конца строки
        # теперь в $_ есть строка и мы ее разделяем на переменные
        ($name, $phone, $address) = split(/::/);
        # и выведем на печать
        print "Имя: $name, телефон: $phone, адрес: $address\n";
}
close DATA;

Больше проблем возникает в случае, если надо удалить или отредактировать запись, но и их можно довольно просто и элегантно решить, если использовать механизм редактирования на месте (inplace edit) -- при использовании операции "ромб"(<>), можно читать из одного файла, а писать в другой:

change_phones.pl
#!/usr/bin/perl
$^I = '~'; # запускаем inplace edit
while (<>) { # Обратите внимание, что мы не открывали файл: при такой
        #конструкции имя файла берется из командной строки
        chomp;
        ($name, $phone, $address) = split(/::/);
        if (.... некоторое условие, при котором мы оставляем наши данные ... )
        {
                print "$name::$phone::$address\n"; # теперь данные есть в новом файле
        }
}

если запустить это программу как

change_phones.pl phones.data,
то в текущем каталоге будут два файла: phones.data, с записями, которые удовлетворили нашим условиям и phones.data  -- предыдущая копия.

Также, во многих случаях, всю программу такого типа можно записать как one-liner:

perl -i~ -n -e 'print if(... условие)'

Двоичные файлы

Для чтения двоичных файлов в Perl можно использовать функции read и unpack. К примеру, если использовать двоичный файл для хранения телефонной книги такого формата:

40 символов -- фамилия, И.О.

10 символов -- номер телефона,

60 символов -- адрес,

то строка описания формата для unpack будет выглядеть так:

$format_str = 'A40 A10 A60'
, а сама программа, аналогичная первому примеру:
binary_phones.pl
#!/usr/bin/perl
$format_str = 'A40 A10 A60';
open DATA, 'binary.dat' or die "$!";
while (read(DATA, $buf, 40+10+60)) { # <DATA> не покатит: такая
# конструкция будет читать до символа перевода строки, а это не то, что нужно
        ($name, $phone, $address) = unpack($format_str, $buf);
        # Теперь в $name, $phone, $address есть данные и с ними можно делать
        # все, что захочется
}
close DATA;

Чтобы вывести в файл такую запись можно использовать конструкцию типа

print FILE pack($format_str, $name, $phone, $address);

8.2 Можно ли как-нибудь из Perl получить доступ к dbf файлам?

Да, можно. На http://www.fi.muni.cz/~adelton/ есть модуль XBase, который позволяет читать/писать dbf. При чтении он даже поддерживает индексы. Кроме того, в комплект поставки также входит модуль DBD::XBase, при помощи которого можно оперировать dbf на SQL (более подробно про DBI -- далее).

8.3 А к MS access .mdb?

К файлам MS Access нельзя обращаться из perl напрямую, по крайней мере, в настоящее время.

К MS Access можно обращаться по ODBC, при помощи DBD::ODBC.

8.4 Зачем и как нужно запирать (lock) файлы?

Представьте себе ситуацию когда одновременно работают несколько копий одной и той же программы (к примеру, cgi-скрипты, обслуживающие запросы), читающие/пишущие в один файл, тогда рано или поздно возникнет ситуация при которой один скрипт прочитал данные, произвел над ними некоторые действия и собрался записать их назад в файл, но в это же время другой скрипт тоже прочитал данные, тоже произвел над ними действия, но (!) он прочитал старые данные, которые он и запишет поверх данных, выданных другим скриптом. Таким образом, в файле останутся данные записанные одним из скриптов -- в лучшем случае, в худшем -- структура файла будет испорчена. Чтобы этого избежать в Unix и большинстве других ОС есть системный вызов flock(2) или аналогичный.

Как использовать flock

К примеру, скрипт который записывает имена вызывающих хостов в файл. (На деле такой список, конечно, можно получить из журнала регистрации web-сервера).

lock_exm.pl
#!/usr/bin/perl
use Fcntl; # Импорт констант
open (HOSTS, '>>hosts.log'); # Файл открыт для добавления записи
flock(HOSTS, LOCK_EX);
# Теперь файл заблокирован: Если любой другой скрипт тоже вызовет flock на
# этом файле, его flock не вернет управление в программу, пока мы не
# разблокируем файл. Обратите внимание: flock -- декларативная функция, если
# один из скриптов ее не использует при записи, то вся ваша блокировка не
# работает.
print HOSTS $ENV{REMOTE_HOST}, "\n"; # записали строку
close HOSTS; # Файл при закрытии разблокируется автоматически

# Вывести сообщение для пользователей
print "Content-Type: text/plain\n\n";
print "Название вашего хоста записано\n";

Более подробный рассказ о flock и пример доступны на http://w3.stonehenge.com/merlyn/WebTechniques/col04.html

8.5 Чего делать на системах

Судя по perlfaq5(1), можно использовать модуль File::Lock с CPAN.


9. Реляционные, пост-реляционные и объектно-реляционные СУБД

9.1 Как использовать DBI?

Введение

DBI -- это интерфейс прикладных программ к СУБД, использующим SQL в качестве языка запросов. Сам DBI определяет только набор функций, переменных и соглашений. Вся непосредственная работа выполняется Database Drivers (DBD) -- модулями, обеспечивающими связь с СУБД. DBI только обеспечивает стандартный интерфейс для этих драйверов.

Полная схема архитектуры при работе DBI выглядит примерно так:

+----------------------+
| Прикладная программа |
+----------------------+
| DBI                  |
+----------------------+
| DBD                  |
+----------------------+
| СУБД                 |
+----------------------+

Подсоединение к СУБД

обеспечивается при помощи метода connect класса DBI:

$dbh = DBI->connect($dsn, $user, $auth, {options});

$dbh

Это объект, при помощи его методов осуществляются взаимодействия с СУБД.

$dsn

Строка, определяющая к какой базе данных подсоединятся и другие параметры. Зависит от DBD. На сегодняшний момент стандарта нет, но рекомендовано использовать стиль ODBC:

&dbi:<имя DBD>:databasename=<название БД>;host=<Имя хоста>;port=<порт>&;
$user

Имя пользователя.

$auth

Нечто, авторизующее пользователя. Обычно пароль.

options

Параметры DBI, передаются через анонимный хеш. В настоящее время понимаются три параметра:

RaiseError

если установлен, то при любой ошибке DBI убивает программу

PrintError

если установлен, то при ошибке DBI вызывает warn

AutoCommit

определяет порядок работы с транзакциями.

Например:

$dbh = DBI->connect('dbi:Pg:dbname=apavel', 'apavel', 'SomeSecret',
{RaiseError=>1, AutoCommit=>0});
Означает: Подсоедение к СУБД PostgreSQL, к базе данных apavel, с именем пользователя apavel и паролем. Все ошибки будут вызывать die, что удобно при отладке, а все изменения будут внесены только при подтверждении (commit) транзакций.

Отсоединение обеспечивается при помощи метода disconnect: $dbh->disconnect();

Механизм курсоров и подготовки запросов

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

Пример таблицы, используемый в дальнейшем:

foo.sql
create table foo (
        bar varchar(50),
        baz int
)

Получение данных

$cursor = $dbh->prepare('select bar, baz from foo');
# теперь $cursor -- курсор, и его необходимо исполнить
$cursor->execute;
# После исполнения запроса, результат можно получить из курсора при помощи
# метода fetchrow_array
while (($bar, $baz) = $cursor->fetchrow_array) {
        print "bar is: $bar, baz: $baz\n";
}

Placeholders

Очень часто бывает надо подготовить какой-либо запрос, а потом использовать его с разными значениями данных. DBI предлагает для механизм placeholders: В запросе на месте таких данных указываются вопросительные знаки, а сами значения передаются в метод execute() курсора. Например:

$cursor = $dbh->prepare('select bar from foo where baz=?');
$cursor->execute($baz);
Особенно удобно это в случае вставки данных:
$cursor = $dbh->prepare('insert into foo(bar, baz) values(?, ?)');
while ( ... ) {
        $cursor->execute($bar, $baz);
}

Таким образом, СУБД разбирает запрос только один раз, а затем просто исполняет его, что экономит время. (Естественно, это верно только для DBMS с раздельными parse и execute, сейчас ни MySQL, ни PostgreSQL такое не поддерживают, поэтому их реализации DBD просто сохраняют запрос переданный $dbh->prepare() и затем подставляют в него данные при каждом $sth->execute().)

Работа без курсоров

DBI предоставляет несколько методов для такого рода работы: Методы для запросов:

selectrow_array

Возвращает одну строку запроса в виде массива

selectall_arrayref

Возвращает весь ответ сервера в виде массива ссылок на массивы.

Методы для выражений, не возвращающих значений
do

исполняет запрос

Пример:

#получить значение bar при baz=3
($bar) = $dbh->selectrow_array('select bar from foo where baz=3');

# установить baz в некоторое значение при bar='somestring'
$dbh->do("update set baz=1 where bar='somestring'");

9.2 Как работать с записями кусочками: первые N записей, следующие N...?

Можно несколькими способами: 1. Просто прокручивая курсор:

        $c = $dbh->prepare('select baz, bar from foo');
        $c->execute;
        # если нужна последовательность с 26 по 50

        for ($k = 0; $k < 26; $k++) {
                $c->fetchrow_array;
        }
        # теперь можно вывести данные
        print "<table border=1><tr><th>bar</th><th>baz</th></tr>\n";
        while (($bar, $baz) = $c->fetchtrow_array) {
                print "<tr><td>$bar</td><td>$baz</td></tr>\n";
        }
        $c->finish; # Закрыть курсор
        print "</table>";

2. Используя курсоры СУБД

        # Показан синтаксис PostgeSQL
        $dbh->do('declare mycursor cursor for select bar, baz from foo');
        $dbh->do('move 25');

        # И теперь будем получать данные
        $c = $dbh->prepare('fetch forward 25 in mycursor');
        while (($bar, $baz) = $c->fetchrow_array) {
                print ....;
        }
        $c->finish;
        $dbh->do('close mycursor');

3. Для MySQL можно использовать директиву LIMIT

        $c = $dbh->prepare('select bar, baz from foo limit 26,25');
        while (($bar, $baz) = $c->fetchrow_array) {
                print ....;
        }
        $c->finish;

9.3 Где взять документацию/учебник по SQL?

  • В книжных магазинах сейчас обширный набор книг по SQL. (От рекомендаций воздержусь -- ни одну из них я не читал)
  • На MCP personal bookshelf ( http://pbs.mcp.com) есть книга "Teach yourself SQL in 21 days" -- весьма рекомендую, очень хорошая книга.
  • На citforum ( http://www.citforum.ru) были какие-то руководства

9.4 Как можно подсоединится к MS SQL Server?

Вроде как можно при помощи DBD::FreeTDS


10. Где взять perl для m$ windows?


11. Борьба с глюками m$ windows

11.1 Почему мой perl ругается на flock()?

Потому что на windows нет flock(2).

Используйте File::Lock с CPAN.

11.2 Тоже самое, но с fork()?

Windows это не unix. Они fork не умеют.

11.3 Как бы мне добится нормальной сортировки в алфавитном порядке, преобразования в прописные/строчные буквы?

Perl для этого опирается на механизм locale. К счастью, это работает и на windows:

Если надо работать с CP866, пишем

  use locale;
  use POSIX;
  &POSIX::setlocale(&POSIX::LC_ALL, "Russian_Russia.866");
  print uc "Да, здесь будут заглавные буквы";

если KOI8 (sic!):

  &POSIX::setlocale(&POSIX::LC_ALL, "Russian_Russia.20866");

если с CP1251 -- просто

use locale;

Со всеми тремя кодировками работают и uc/lc, и /\w/

Проверено под NT на perl 5.005_02, собраном через VC++ 5.0 из исходников, скачанных с CPAN/ports/win32/Standard/

11.4 Почему скрипты, которые я написал дома на windows, при загрузке на unix машину не работают?

Этому может быть миллион разных причин, но самая часто встречающаяся -- unix и наследники CP/M используют разные последовательности конца строки.

Если вы их загружаете по ftp, включите режим ASCII.

Секция 2 из 2 - Предыдущая - Следующая

 FAQ по perl и программированию для web.
Лента новостей


2006 (c) Copyright Hardline.ru