О насБлогКонтакты
Веб-разработка20 июля 1998 г. 6 мин 105Обновлено: 22 июня 2026 г.

Гостевая книга на Perl CGI: классика веб-разработки 1998 года

AunimedaAunimeda
📋 Содержание

В 1998 году гостевая книга была мерилом технической зрелости сайта. Статический HTML был для тех, кто не умеет программировать. Работающая гостевая книга означала: разработчик понимает CGI.

Стек: Perl 5 на Unix-сервере (Apache на Linux или Solaris), спецификация CGI для обмена данными между сервером и браузером, текстовый файл для хранения данных. MySQL в 1998 году стоил как отдельный тарифный план хостинга - $40-80 в месяц сверху. Текстовых файлов вполне хватало для гостевой книги с несколькими десятками посетителей в день.


Как работал CGI

CGI - Common Gateway Interface - стандарт запуска программ веб-сервером с передачей вывода браузеру. Механизм: сервер получает POST или GET запрос, запускает указанную программу (скрипт Perl), передаёт данные формы через переменные среды и STDIN, возвращает STDOUT программы браузеру как тело HTTP-ответа.

Критическое ограничение: каждый CGI-запрос порождал новый процесс. Каждая загрузка страницы запускала Perl с нуля, загружала скрипт, выполняла его и завершала процесс. Никакого состояния между запросами. Никакого пула соединений. При высокой нагрузке - медленно и ресурсоёмко. Для гостевой книги с пятьюдесятью посетителями в день - вполне приемлемо.


Полный скрипт гостевой книги

#!/usr/bin/perl
# guestbook.pl - гостевая книга на Perl 5 CGI, 1998 год
# Положить в cgi-bin/, chmod 755, chmod 777 для папки с данными

use strict;
use warnings;
use CGI;
use POSIX qw(strftime);

my $GUESTBOOK_FILE = '/home/mysite/data/guestbook.txt';
my $MAX_ENTRIES    = 100;

my $cgi    = new CGI;
my $action = $cgi->param('action') || 'view';

if ($action eq 'post') {
    handle_post($cgi);
} else {
    show_guestbook($cgi);
}

# ---------------------------------------------------------------
sub handle_post {
    my ($cgi) = @_;

    my $name    = $cgi->param('name')    || '';
    my $email   = $cgi->param('email')   || '';
    my $message = $cgi->param('message') || '';

    # Убираем HTML-теги - в 1998 году никаких rich-text редакторов
    for my $var ($name, $email, $message) {
        $var =~ s/[<>&"']//g;
        $var =~ s/\n/ /g;
    }

    if (length($name) < 2 || length($message) < 5) {
        print_header();
        print "<p><font color='red'><b>Ошибка:</b> Имя и сообщение обязательны.</font></p>\n";
        print "<p><a href='guestbook.pl'>Вернуться назад</a></p>\n";
        print_footer();
        return;
    }

    my $timestamp = strftime('%d.%m.%Y в %H:%M', localtime);

    my @entries = read_entries();
    unshift @entries, {
        name      => $name,
        email     => $email,
        message   => $message,
        timestamp => $timestamp,
    };

    @entries = @entries[0 .. $MAX_ENTRIES - 1] if @entries > $MAX_ENTRIES;
    write_entries(\@entries);

    # Редирект через meta refresh - HTTP 302 был ненадёжным в CGI-контексте
    print "Content-Type: text/html\n\n";
    print "<html><head><meta http-equiv='refresh' content='0;url=guestbook.pl'>\n";
    print "</head><body>Спасибо! <a href='guestbook.pl'>Нажмите здесь</a>, если не перешло автоматически.</body></html>\n";
}

# ---------------------------------------------------------------
sub show_guestbook {
    my ($cgi) = @_;
    print_header();

    print <<'FORM';
<h2>Оставить запись</h2>
<form method="POST" action="guestbook.pl">
  <input type="hidden" name="action" value="post">
  <table border="0" cellpadding="4" cellspacing="0">
    <tr>
      <td align="right"><b>Имя:</b> *</td>
      <td><input type="text" name="name" size="30" maxlength="60"></td>
    </tr>
    <tr>
      <td align="right"><b>E-mail:</b></td>
      <td><input type="text" name="email" size="30" maxlength="80"></td>
    </tr>
    <tr>
      <td align="right" valign="top"><b>Сообщение:</b> *</td>
      <td><textarea name="message" rows="5" cols="45"></textarea></td>
    </tr>
    <tr>
      <td></td>
      <td><input type="submit" value="Оставить запись"></td>
    </tr>
  </table>
</form>
<hr>
<h2>Записи</h2>
FORM

    my @entries = read_entries();

    if (!@entries) {
        print "<p><i>Записей пока нет - будьте первым!</i></p>\n";
    }

    for my $entry (@entries) {
        print "<div style='border-bottom:1px solid #cccccc; padding:6px 0; margin-bottom:6px;'>\n";
        print "<b>" . $entry->{name} . "</b>";
        print " <small><i>" . $entry->{timestamp} . ":</i></small><br>\n";
        print $entry->{message} . "\n";
        print "</div>\n";
    }

    print_footer();
}

# ---------------------------------------------------------------
sub read_entries {
    my @entries;
    return @entries unless -f $GUESTBOOK_FILE;

    open(my $fh, '<', $GUESTBOOK_FILE) or die "Не могу открыть файл: $!";
    my $entry = {};
    while (my $line = <$fh>) {
        chomp $line;
        if    ($line =~ /^NAME:(.*)$/)  { $entry->{name}      = $1 }
        elsif ($line =~ /^EMAIL:(.*)$/) { $entry->{email}     = $1 }
        elsif ($line =~ /^MSG:(.*)$/)   { $entry->{message}   = $1 }
        elsif ($line =~ /^TIME:(.*)$/)  { $entry->{timestamp} = $1 }
        elsif ($line eq '---') {
            push @entries, $entry if $entry->{name};
            $entry = {};
        }
    }
    close($fh);
    return @entries;
}

sub write_entries {
    my ($entries) = @_;
    open(my $fh, '>', $GUESTBOOK_FILE) or die "Не могу записать файл: $!";
    for my $e (@$entries) {
        print $fh "NAME:"  . $e->{name}      . "\n";
        print $fh "EMAIL:" . $e->{email}     . "\n";
        print $fh "MSG:"   . $e->{message}   . "\n";
        print $fh "TIME:"  . $e->{timestamp} . "\n";
        print $fh "---\n";
    }
    close($fh);
}

sub print_header {
    # Пустая строка после заголовка - обязательное требование CGI
    print "Content-Type: text/html; charset=windows-1251\n\n";
    print "<html>\n<head><title>Гостевая книга</title></head>\n";
    print "<body bgcolor='#FFFFFF' text='#000000'>\n";
    print "<h1>Гостевая книга</h1>\n";
}

sub print_footer {
    print "<hr><small>Работает на Perl CGI / Apache / Linux</small>\n";
    print "</body></html>\n";
}

Деплой в 1998 году: FTP, Telnet, chmod

Чтобы запустить CGI-скрипт на shared-хостинге в 1998 году:

  1. Загрузить скрипт по FTP в папку cgi-bin/ - веб-сервер выполнял CGI только из этой директории
  2. Подключиться по Telnet: telnet myhost.ru 23
  3. Выставить права: chmod 755 guestbook.pl
  4. Создать папку с данными и дать ей права на запись: chmod 777 /home/mysite/data/

chmod 777 - это была общепринятая практика. И одновременно - дыра в безопасности: любой другой пользователь на том же shared-сервере мог перезаписать ваш файл данных. Все это понимали. Делали именно так, потому что альтернатива требовала прав root, которых хостер не давал.

Самая частая ошибка - 500 Internal Server Error:

# Три причины, которые знал каждый Perl-разработчик в 1998 году:

# 1. Неверный путь к интерпретатору в shebang
head -1 guestbook.pl
which perl
# Должны совпадать

# 2. Windows-переносы строк (CRLF) на Unix-сервере - убивали shebang
file guestbook.pl
# Если "CRLF line terminators":
perl -pi -e 's/\r\n/\n/g' guestbook.pl

# 3. Читать лог Apache (если был доступ по SSH)
tail -20 /var/log/apache/error.log

Кодировка: windows-1251 в Рунете

В 1998-2001 году в Рунете шла негласная война кодировок. Московские сайты - windows-1251. Академические и питерские - KOI8-R (стандарт Unix-систем). Один и тот же текст, открытый в неправильной кодировке, превращался в нечитаемый набор символов.

Решение в Perl:

# Рунет 1998-2001: всё в windows-1251, CGI-скрипты тоже
print "Content-Type: text/html; charset=windows-1251\n\n";

# Если данные приходили из формы в KOI8-R и нужно конвертировать:
use Encode;
my $text_utf = Encode::decode('koi8-r', $raw_koi8);
my $text_win = Encode::encode('cp1251', $text_utf);

Рунет полностью переходил на UTF-8 очень медленно - Яндекс перешёл в 2009 году.


Почему PHP вытеснил Perl в веб-разработке

К 2000 году PHP 4 активно захватывал рынок веб-разработки. Причина не в том, что PHP был мощнее - Perl объективно гибче и выразительнее. Причина - в простоте деплоя.

PHP-страница:

  • Лежит рядом с HTML-файлами в public_html/
  • Выполняется через mod_php автоматически
  • Не требует отдельного cgi-bin/
  • Не требует chmod 755
  • Нет зависимости от пути к интерпретатору

Гостевая книга на Perl требовала понимания Unix-прав, протокола CGI и модуля CGI.pm до того, как вы могли вывести хоть что-то в браузер. PHP позволял написать <?php echo "Привет"; ?> в HTML-файл - и это работало немедленно.

Эта простота и решила судьбу веб-рынка: PHP выиграл сайтостроительство, Perl остался в системном администрировании и биоинформатике.

Гостевая книга на Perl CGI была первым практическим уроком: как веб-сервер общается с программой, как парсить данные форм, как читать и писать постоянное хранилище. Паттерны, актуальные в любом веб-фреймворке и сегодня.

Читайте также

Разработка сайта в Самаре в 2026 году - цены, сроки, технологииaunimeda
Веб-разработка

Разработка сайта в Самаре в 2026 году - цены, сроки, технологии

Сколько стоит разработка сайта в Самаре в 2026 году. Реальные цены на лендинги, корпоративные сайты и интернет-магазины. Что влияет на стоимость и как выбрать подрядчика.

Google Chrome и V8: когда JavaScript стал быстрым (2008)aunimeda
Веб-разработка

Google Chrome и V8: когда JavaScript стал быстрым (2008)

2 сентября 2008 года Google выпустил Chrome. Не браузер - манифест. Внутри был V8: JIT-компилятор JavaScript, написанный с нуля. SunSpider показал, что V8 в 10 раз быстрее Firefox 3. За один день ожидания от JavaScript радикально изменились. Начался browser war 2.0.

Ajax: имя, изменившее веб (2005)aunimeda
Веб-разработка

Ajax: имя, изменившее веб (2005)

18 февраля 2005 года Джесси Джеймс Гаррет опубликовал статью «Ajax: новый подход к веб-приложениям». Технология не была новой - XMLHttpRequest существовал с 1999 года. Но имя изменило всё: разработчики получили паттерн, словарь и разрешение думать о браузере как о платформе приложений.

Нужна IT-разработка для вашего бизнеса?

Разрабатываем сайты, мобильные приложения и AI-решения для бизнеса в России. Бесплатная консультация.

Разработка сайтов

Получить консультацию Все статьи