Пишем движок сайта на Lazarus
Несколько лет назад где-то в Сети мне встретилась заметка, посвящённая CMS, написанной на Delphi. Понятно, всё это дело могло крутиться исключительно под Windows, что в сфере Web скорее исключение (Linux и BSD рулят).
Но самая идея написания сайто-движка на «нестандартном» языке показалась мне забавной. И вот давеча решил я немного покопать в эту сторону. Из академического интереса так сказать. А что? Камрад, вон, пилит 8-битный процессор паяльником.
Для начала немного теории. Давным-давно (году этак в 1993-м), когда статичный HTML всем уже порядком наскучил, а всякие PHP ещё не родились, придумали умные люди забавную штуку под названием CGI. В детали углубляться не буду — люди вы грамотные, сами нагуглите. Нам же важен один факт — силами CGI на сервере можно выполнить программу практически на любом языке — будь-то Pascal или вообще VisualBasic. Главное, чтобы программа была откомпилирована в формате, подходящем для ОС на конкретном сервере. Таким образом, кстати, пилят сайты на Python (интерпретатор Python выполняется как модуль CGI).
Когда народ попривык к шустрому интернету, скорости CGI стало маловато, и другие умные люди придумали FastCGI — почти тот же самый CGI, но типа весь из себя fast. Фишка в том, что FastCGI не плодит сущности, и крутит каждую программу в одном потоке.
С написанием программа под CGI заморочек ровным счётом никаких — юзай себе стандартный вывод в консоль, и будет тебе счастье. Для Pascal, например, будет как-то так:
program first; begin writeln('Content-type: text/plain'); writeln; writeln('Hello, world!'); end.
Передали серверу заголовок и увидели в браузере старый-добрый Hello, world!.
С FastCGI всё немного интереснее, ибо он юзает не консоль, а самые, что ни на есть Socket’ы (ну, или вообще голый TCP/IP). И вот тут уже есть небольшая заморочка — писать под FastCGI можно на любом языке с одним условием — под рукой должна быть реализация Socket’ов или (в идеале) — API для работы с FastCGI.
Я, как можно догадаться, писать недо-сайт буду на реализации Pascal, известной как FreePascal. Более того, возьму аж цельный Lazarus, дабы не отставать от коллег, упомянутых в самом начале статьи.
Но для начала давайте организуем небольшой сервачок для своих бесчеловечных опытов. Для начала поставим Apache. Я работаю под Windows 7, так что качал win-сборку версии 2.4.3. Затем цепляем к Apache модуль FastCGI. Я взял реализацию mod_fcgid с сайта самого Apache.
Чтобы организовать виртуальный хост с поддержкой FastCGI, нужно немного подправить конфиг Апача (c:Apache24confhttpd.conf
).
Нас интересуют две строчки:
LoadModule fcgid_module modules/mod_fcgid.so LoadModule rewrite_module modules/mod_rewrite.so
Первую придётся в конфиг добавить, со второй — убрать #
в начале строки. Собственно, mod_rewrite нам понадобится позже, но чтобы два раза не ходить, правим сразу.
Ну, и собственно, создаём хост с FastCGI (добавляем указанные строчки в конец файла httpd.conf
):
<Directory "c:/Apache24/fcgi-bin"> AllowOverride None Options None Require all granted </Directory> <Directory c:/Apache24/fcgi-bin/> SetHandler fcgid-script Options +ExecCGI Order allow,deny Allow from all </Directory>
Теперь при обращении к приложениям, расположенным в каталоге c:/Apache24/fcgi-bin
Apache будет отдавать браузеру результат их (приложений) работы. Например, если мы напишем программу test.exe
и откроем в браузере ссылку http://localhost/test.exe
— увидим результат работы test.exe
.
Но вернёмся к Lazarus (надо же, наконец, написать тот самый test.exe
). Как ни странно, средства для разработки CGI и FastCGI в его составе уже есть. Чтобы из задействовать — устанавливаем набор компонентов fpWeb из папки c:lazaruscomponentsfpweb
. Теперь в IDE появились шаблоны проектов FastCGI. Воспользуемся: Файл -> Создать. В появившемся окне ищем Приложение FastCGI.
Вуаля! Заготовка FastCGI-приложения готова. Осталось создать обработчик события OnRequest
для компонента TFPWebModule1
. Например, такой:
AResponse.ContentType := 'text/html;charset=utf-8'; AResponse.Contents.Add('Hello World!');
Но, как вы понимаете, тупо выводить текст по прямой ссылке — это как-то… хм… статично. Нафига же нам тогда FastCGI вообще? Давайте сделаем прототип работы с ЧПУ. Для начала снова поправим конфиг Apache. Точнее — добавляем в его конец такие строки:
<IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^(.*)$ c:/Apache24/fcgi-bin/pascalpress?$1 [T=application/x-httpd-cgi,L] </IfModule>
Попутно настраиваем проект в Lazarus таким образом, чтобы при компиляции получать файл с именем pascalpress (без расширения!).
Теперь пишем в обработчике OnRequest
такой код:
{ TFPWebModule1 } // Базовая функция (выполняется каждый раз при обращении к скрипту) procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean); // вывод в текст страницы (просто для удобства - закос под PHP) function echo(str: string): boolean; begin AResponse.Contents.Add(str); end; var i: integer; lData: String; begin // задаём тип контента - текст/html (для графики могут понадобиться другие типы) AResponse.ContentType := 'text/html;charset=utf-8'; // корневой каталог сервера WWW:=ExtractFilePath(ParamStr(0)); // если нет параметров (т.е. загружается главная страница сайта) if (ARequest.QueryFields.Count<=0) or (ARequest.QueryFields[0]='/') then begin // грузим index.html echo(WWW+'/index.html'); AResponse.Contents.LoadFromFile(WWW+'/index.html'); end // иначе - разбираем параметры else begin // параметры URL (ЧПУ вида mysite.ru/1/2/3) дадут 1 параметр вида /1/2/3) // использовать в запросах ? нельзя - всё, что идёт после него, отрезается for i:=0 to ARequest.QueryFields.Count-1 do begin // если есть файл с именем, равным имени ключа, грузим его целиком if FileExists(WWW+ARequest.QueryFields[i]) then AResponse.Contents.LoadFromFile(WWW+ARequest.QueryFields[i]); end; end; // типа закончили формирование ответа Handled := True; end;
Получить данные от пользователя тоже очень просто:
var email: string; ... email:=ARequest.ContentFields.Values['email']
Как можно догадаться, этот код позволяет получить значение поля ввода с именем email.
Собственно, на этом можно введение в написание CMS на Lazarus закончить. Что мы имеем в результате? Наша программа принимает на входе URL (немного извращённо, зато надёжно). При этом наличие самой программы скрывается. Фактически, если использовать в URL расширения .php
или .html
— посетители сайта вообще не заподозрят, что сайт крутится не на типичном скриптовом языке. Также мы можем получить данные из полей ввода.
Осталось прикрутить БД, организовать загрузку файлов на сервер — и путь к написанию простейшей CMS открыт. Ну, и ещё нужно всё это дело откомпилировать под Linux или BSD, чтобы можно было запустить на нормальном хостинге. В теории, даже Apache не обязателен — FastCGI можно под nginx завести. Если будет не лень, ещё немного покопаю эту тему.
PS. Желающие могут скачать исходники и готовое FastSGI-приложения из примера, собранное под Windows.
Обновлено 9 марта 2013 года
После беглого изучения вопроса определил для себя плюсы и минусы fpWeb против PHP (с последним немного знаком — на уровне правки чужого кода под свои нужды и написания скриптов средней руки):
+ скорость обработки данных. PHP не заточен под обработку данных, хотя с массивами там работать удобно (именованные массивы — вещь, которой в Pascal’е нативно нет 🙁 ). Плюс есть немало библиотек под специфические задачи (математика, графика и т.п.). На PHP тоже немало всего есть, но вещи, отличные от стандартных, обычно гемморойно делаются. Всё — имхо, возможно я просто плохо знаю PHP.
+ обработка исключение / защита от дурака. Может я плохо знаю PHP (или какие-то его специализированыне библиотеки), но мне показалось, что Pascal в этом плане имеет весомое преимущество. Конечно, выполнение приложения в «вечном цикле» FastCGI усложняет жизнь (как правильно заметил SSerge). Но в целом это решаемо. Кодом на Паскале управлять проще, поскольку он более предсказуем. PHP-скрипт «повесить» на порядок проще (собственно, если бы сервер такие скрипты не «гасил» при превышении лимита времени — не был бы PHP так распространён).
+ Контроль за расходом памяти. Изначально интерпретатор PHP ест меньше памяти, чем Pascal-CGI, но тут надо учитывать, что всё может измениться при высокой нагрузке и если в случае Pascal расход памяти сильно зависит от качества кода, то в PHP, как мне кажется, этим делом управлять труднее (если, допустим, интерпретатор PHP при выполнении определённых языковых конструкций кушает определённый объём памяти, и с ростом количества подключений этот объём растёт, вы, скорее всего, ничего с этим поделать не сможете; а код на Pascal всегда можно переписать; понятно, что и на PHP код можно переписать, но в общем и целом нет гарантий, что вы не упрётесь в косяки интерпретатора, которые обойти не удастся).
— Необходимость более строго следить за кодом в плане проверки значений переменных и т.п., чтобы не вызвать фатальный сбой в приложении (что может повесить сервер) — здесь нет надзирателя в виде интерпретатора PHP, который за вами приберёт. С другой стороны, хороший программист должен из без «указки сверху» следить за своим кодом 🙂
— Сложность в развёртывании на типовых хостингах. Не все хостеры разрешат свободно запускать FastCGI-приложения. С другой стороны, при грамотной настройке серверного ПО FastCGI может быть заметно безопаснее, чем PHP, поскольку его можно «изолировать» от остальной части сервера (отдельный chroot).
— Сложность с правкой кода «на лету» — вам каждый раз придётся компилировать приложение и заливать его рабочий сервер; а для этого придётся временно останавливать его выполнение, что на рабочих сайтах/сервисах не есть хорошо. Возможно, это можно как-то обойти, но я пока не придумал (плохо знаю фишки Apache/nginx).
Пока как-то так.
6 комментариев
Хм… Хотя всегда писал вебовское на PHP, эта идея мне крайне понравилась. А почему бы и нет? 🙂
Delphi Web Script + FastCGI и не придется каждый раз компилировать приложение. Пиши себе скрипты как на php.