Основы веб-программирования

Материал из PhpWiki.

Перейти к: навигация, поиск

Содержание

Что такое веб-программирование

Если вы уже пробовали (а может быть, даже и небезуспешно :)) программировать, например, на Delphi, или Visual Basic, или даже Visual C++, то привыкли к такой схеме работы программы: нажимается кнопочка - выполняется код - выводится результат, и все это выполняется на одном компьютере.

В веб-программировании все обстоит по-другому.

Вы задумывались, что происходит, когда вы вводите в адресной строке браузера URL (Universal Resource Location, или в просторечии - адрес)? Cхема работы следующая:

  • Браузер открывает соединение с сервером
  • Браузер отправляет серверу запрос на получение страницы
  • Сервер формирует ответ (чаще всего - HTML-код) браузеру и закрывает соединение
  • Браузер обрабатывает HTML-код и отображает страницу

Обратите внимание: еще до того, как вы увидели на экране запрошенную страницу, соединение с сервером закрыто, и он о вас забыл. И когда вы введете другой (или тот же самый) адрес, или щелкните по ссылке, или нажмете на кнопку HTML-формы - та же схема повторится снова.

Такую схему работы называют "клиент-сервер". Клиент в данном случае - браузер.

Итак, соединение с веб-сервером длится всего несколько секунд (или долей секунд) - это промежуток времени между щелчком по ссылке (или другим способом запроса) и началом отображения страницы. Большинство браузеров во время соединения отображают некий индикатор, например, MS Internet Explorer отображает анимацию в правом верхнем углу.

Внимательный читатель здесь может заметить - а как же так, я уже читаю страницу, а индикатор все еще показывает процесс соединения? Дело в том, что тэг <img src=/...> (загрузка изображения) и некоторые другие - это не более чем еще один запрос к серверу - и выполняется он точно также, как и любой другой - по той же схеме. И запрос картинки, с точки зрения сервера, полностью независим от запроса HTML-ника.

Чтобы навсегда избавиться от восприятия HTTP как "черного ящика", "притворимся" браузером с помощью telnet'а:

Запустим telnet phpwiki.ru 80. Введем в окне терминала следующее (если ввод не отображается - ничего страшного):

GET / HTTP/1.0 [здесь нажмем Enter]
Host: phpwiki.ru [здесь нажмем Enter дважды]
Нажатие Enter соответствует, как правило, комбинации символов CR + LF, обозначаемых как \r\n. Далее будет использоваться именно это обозначение.

По экрану пробежит HTML-код страницы http://phpwiki.ru/. Как видите - ничего сложного.

Исходный код текущей страницы можно просмотреть практически в любом браузере, выбрав в меню "View|Source".

Картинки, фреймы - все это дополнительные запросы, точно такие же. Собственно, откуда берутся картинки в окне браузера: при парсинге (обработке) HTML-кода, браузер, натыкаясь на тэг <img src="/kartinka"> осуществляет дополнительный запрос к серверу - запрос картинки, и отображает ее на месте, где стоит тэг <img...>.

Попробуйте:

telnet www.ya.ru 80
GET /logo.gif HTTP/1.0\r\n
Host: www.ya.ru\r\n
\r\n

По экрану пробежит то, что вы увидите, если просмотрите логотип Яндекса в текстовом редакторе.

HTML-формы. Методы отправки данных на сервер

С HTML-формами вы наверняка уже встречались:

<form method="GET" action="/cgi-bin/form_handler.cgi">
Введите Ваше имя: <input type="text" name="name">
<br>
<input type="submit" name="okbutton" value="OK">
</form>

Сохранив данный код в HTML-файле и просмотрев его с помощью вашего любимого браузера, вы увидите привычную HTML-форму.

Рассмотрим используемые в этом небольшом примере тэги подробнее.

Тэг <form>, имеющий парный завершающий тэг </form>, собственно и задает форму. Его атрибуты - оба необязательные:

  • action - указывает URL (полный или относительный), на который будет отправлена форма. Отправка формы - это такой же запрос к серверу, как и все остальные (как я уже описал выше). Если этот атрибут не указать - форма отправляется на текущий документ, то есть "сама на себя".
  • method - способ отправки формы. Их два.
    • GET - отправка данных формы в адресной строке. Вы могли заметить на различных сайтах присутствие в конце URL символа "?" и следующих за ним данных в формате параметр=значение. Здесь "параметр" соответствует значению атрибута name элементов формы (см. ниже про тэг <input>), а "значение" - содержимому атрибута value (в нем, например, содержится ввод пользователя в текстовое поле того же тэга <input>). Для примера - попробуйте поискать что-нибудь в Яндексе и обратите внимание на адресную строку браузера. Это и есть способ GET.
    • POST - данные формы отправляются в теле запроса. Если не совсем понятно (или совсем непонятно), что это такое - не беспокойтесь, скоро мы к этому вопросу вернемся.

Если атрибут method не указан - подразумевается "GET".

Тэг <input> - задает элемент формы, определяемый атрибутом type:

Значение "text" задает однострочное текстовое поле ввода. Значение "submit" задает кнопку, при нажатии которой происходит отправка формы на сервер.

Итак, что же происходит, когда мы нажимаем кнопку "OK"?

  • Браузер просматривает входящие в форму элементы и формирует из их атрибутов name и value данные формы. Допустим, введено имя Vasya. В этом случае данные формы - name=Vasya&okbutton=OK
  • Браузер устанавливает соединение с сервером, отправляет на сервер запрос документа, указанного в атрибуте action тэга <form>, используя метод отправки данных, указанный в атрибуте method (в данном случае - GET), передавая в запросе данные формы.
  • Сервер анализирует полученный запрос, формирует ответ, отправляет его браузеру и закрывает соединение
  • Браузер отображает полученный от сервера документ

Отправка того же запроса вручную (с помошью telnet) выглядит следующим образом (предположим, что доменное имя сайта - www.example.com):

telnet www.example.com 80
GET /cgi-bin/form_handler.cgi?name=Vasya&okbutton=OK HTTP/1.0\r\n
Host: www.example.com\r\n
\r\n

Как вы, скорее всего, уже догадались, нажатие submit-кнопки в форме с методом отправки "GET" аналогично вводу соответствующего URL (со знаком вопроса и данными формы в конце) в адресной строке браузера: http://www.example.com/cgi-bin/form_handler.cgi?name=Vasya&okbutton=OK

На самом деле, метод GET используется всегда, когда вы запрашиваете с сервера какой-либо документ, просто введя его URL, или щелкнув по ссылке. При использовании <form method="GET" ... >, к URL просто добавляются знак вопроса и данные формы.

Возможно, все эти технические подробности и упражнения с telnet-ом кажутся вам невероятно скучными и даже ненужными ("а при чем тут PHP?"). А зря. :) Это основы работы по протоколу HTTP, которые необходимо знать назубок каждому Web-программисту, и это не бесполезные теоретические знания - все это пригодится в ежедневной практике.

Теперь заменим первую строку нашей формы на следующую:

<form method="POST" action="/cgi-bin/form_handler.cgi">

Мы указали метод отправки "POST". В этом случае данные отправляются на сервер несколько другим способом:

telnet www.example.com 80
POST /cgi-bin/form_handler.cgi HTTP/1.0\r\n
Host: www.example.com\r\n
Content-Length: 22\r\n
\r\n
name=Vasya&okbutton=OK

При использовании метода POST данные формы отправляются уже после "двух Enter-ов" - в теле запроса. Все, что выше - на самом деле заголовок запроса (и когда мы использовали метод GET, данные формы отправлялись в заголовке). Для того, чтобы сервер знал, на каком байте закончить чтение тела запроса, в заголовке присутствует строка Content-Length. Преимущество метода POST - отсутствие ограничения на длину строки с данными формы.

При использовании метода POST невозможно отправить форму, просто "зайдя по ссылке", как было с GET.

Для краткости изложения, введем термины "GET-форма" и "POST-форма", где префикс соответствует значению атрибута method тэга <form>.

При использовании POST-формы, в ее атрибуте action можно указать после знака вопроса и параметры GET-формы. Таким образом, метод POST включает в себя и метод GET.

Технология CGI

В предыдущей главе мы с вами разобрались, как создать HTML-форму, и как браузер отправляет введенные в нее данные на сервер. Но пока что непонятно, что будет сервер делать с этими данными.

Сам по себе веб-сервер умеет просто отдавать запрошенную страницу, и ничего более того, и ему все переданные данные формы, в общем-то, совершенно безразличны. Для того, чтобы можно было обработать эти данные с помощью какой-либо программы и динамически сформировать ответ броузеру, и была изобретена технология CGI (Common Gateway Interface).

Взглянем на этот URL: http://www.example.com/cgi-bin/form_handler.cgi. Первое предположение, которое можно сделать на его счет, обычно такое: сервер отдает содержимое файла form_handler.cgi из каталога cgi-bin. Однако, в случае с технологией CGI дело обстоит по-другому. Сервер запускает программу form_handler.cgi и передает ей данные формы. Программа же формирует текст, который передается браузеру в качестве ответа на запрос.

Программу form_handler.cgi можно написать на любом языке программирования, главное - соблюдать в программе стандарт CGI. Можно использовать, например, популярный скриптовый язык Perl. А можно написать все и на Си. Или на shell-скриптах... Но мы, для примера, напишем эту программу на С. Но сначала разберемся, как происходит обмен данными между веб-сервером и CGI-программой.

Перед запуском CGI-программы, сервер устанавливает переменные окружения (вам они наверняка знакомы по команде PATH). В каждый мало-мальски серьезном языке программирования есть средства для чтения переменных окружения. Стандарт CGI определяет весьма значительный набор переменных, которые должны быть определены перед запуском CGI-программы. Рассмотрим сейчас только три из них:

  • REQUEST_METHOD - метод передачи данных - GET или POST (есть и другие, но пока мы их не рассматриваем)
  • QUERY_STRING - содержит часть URL после вопросительного знака, или, другими словами, данные GET-формы.
  • CONTENT_LENGTH - длина тела запроса (данные POST-формы).

Сервер запускает CGI-программу. Тело запроса передается программе в виде стандартного ввода (stdin) - будто бы эти данные были введены с клавиатуры.

Программа выдает ответ броузера на стандартный вывод (stdout) - "на экран". Этот вывод перехватывается веб-сервером и передается браузеру.

1: #include <stdio.h>
2: #include <stdlib.h>
3:
4: int main(void)
5: {
6: // Читаем переменные среды, установленные веб-сервером
7: char *query_string = getenv("QUERY_STRING");
8: char *request_method = getenv("REQUEST_METHOD");
9:
10: char *post_data; // Буфер для данных POST-запроса
11: int post_length = 0; // Длина тела запроса
12:
13: if (strcmp(request_method, "POST") == 0) { // Если получен POST-запрос,
14: post_length = atoi(getenv("CONTENT_LENGTH")); // сначала читаем из 
15: // переменной среды его длину,
16: if (post_length) { // если она не нулевая,
17: post_data = (char*)malloc(post_length+1); // выделяем память для буфера,
18: fread(post_data, post_length, 1, stdin); // читаем со стандартного ввода тело запроса,
19: post_data[post_length] = 0; // завершаем строку нулевым байтом.
20: }
21: }
22:
23: // Выводим заголовок ответа...
24: printf("Content-type: text/html\r\n\r\n");
25:
26: // и его тело:
27: printf("<h1>Здравствуйте!</h1>\r\n");
28:
29: if (strlen(query_string)) {
30: printf("<p>Параметры GET-формы: %s\r\n", query_string);
31: }
32:
33: if (post_length) {
34: printf("<p>Параметры POST-формы: %s (длина тела запроса: %d)\r\n", post_data, post_length);
35: free(post_data); // не забываем освободить выделенную в строке 17 память
36: }
37:
38: return 0;
39: }

Это простейшая CGI-программа на Си, выводящая содержимое полученных от веб-сервера параметров форм. Браузер в результате получит примерно следующий код (если "засабмитить" на эту программу POST-форму из последнего примера):

<h1>Здравствуйте!</h1>
<p>Параметры POST-формы: name=Vasya&okbutton=OK (длина тела запроса: 22)

Что при этом отобразится на экране пользователя, думаю, понятно без комментариев. :)

Как видите, даже простейшая программа вывода параметров не так-то проста. Более того, по стандарту HTTP почти все не алфавитно-цифровые символы (в т.ч. и русские буквы) передаются в так называемом UrlEncoded-виде (%XX, где XX - шестнадцатеричный код символа), и, если добавить в приведенную Си-программу код расшифровки UrlEncode, она уже не поместится на экран. А это - всего лишь базовые операции. А как вырастет программа на Си, если необходимо работать с базой данных?

Впрочем, написание CGI-программ на Си - довольно редкое извращение. :) Чаще всего это делают на Perl - языке, разработанном специально для обработки текстовых данных, а наличие модуля CGI делает написание CGI-скриптов намного более простой задачей. Здесь я не буду вас знакомить с Perl, отмечу лишь, что проблем остается достаточно: все же Perl не предназначен для Web, это язык универсальный. Да и сама технология CGI несовершенна: при каждом обращении происходит запуск программы (в случае с Perl - интерпретатор языка), а эта операция довольно ресурсоемкая: для домашней странички Васи Пупкина производительности, конечно, достаточно, но серьезный портал с десятками и сотнями тысяч хитов в сутки потребует уже огромных аппаратных мощностей.

А теперь взглянем на веб-сервер Apache. По своей природе он модульный, и позволяет подключать расширения добавлением одной строки в конфигурационный файл. (Ну, хорошо, хорошо, двух строк. :)). Было бы прекрасно, если бы существовал скриптовый язык, заточенный именно под Web, да еще и при желании подключаемый модулем к Апачу, не так ли? Ну, вы уже поняли, к чему я клоню :) - это и есть PHP.

PHP: Препроцессор Гипертекста

В 1994-м году, один программист, по имени Rasmus Lerdorf, намучавшись с классическим перловым модулем CGI, решил написать несколько собственных Perl-скриптов, дабы было попроще создавать собственную домашнюю страницу, и назвал все это дело Personal Home Page (PHP). Через некоторое время ему понадобилось обрабатывать формы, ну и для увеличения производительности все было переписано на C - так появился Personal Home Page/Forms Interpreter (PHP/FI) 2.0. Труды свои Расмус, следуя принципам Open Source, выложил на всеобщее обозрение, и, в принципе, на некоторм количестве сайтов PHP/FI вполне успешно использовался, хотя был довольно примитивен.

В 1997-м на PHP/FI - в поисках инструмента для удобного Веб-скриптинга - наткнулись два других программера - Andi Gutsman и Zeev Suraski. Сама идея им понравилась, но функциональность и скорость работы PHP/FI оставляли желать лучшего, и Andi и Zeev решились переписать PHP с нуля. Язык получился универсальный и мощный, и вскоре привлек внимание множества веб-разработчиков: к концу 1998 года PHP3 использовался на ~10% веб-серверов. Скромное название "Personal Home Page" уже не очень-то соответствовало реальности, и название было изменено на - в лучших Unix-традициях - рекурсивное: PHP: Hypertext Preprocessor.

Такая популярность PHP побудила Andi и Zeev на повторное переписание кода, прежде всего - с целью еще большего увеличения производительности. "Движок" PHP 4, названный Zend Engine, разрабатывался усилиями уже сформировавшегося и с тех пор непрерывно расрастающегося PHP community, и в 2000-м году вышла 4-я версия PHP, ставшая менее чем через полгода стандартом для Веб-разработки под Unix (и не только): каждый уважающий себя хостер предоставлял поддержку PHP. Летом 2004-го года команда разработчиков PHP представила 5-ю версию, основанного на новом Zend Engine 2, со "взрослой" поддержкой ООП, сейчас активно разрабатывается PHP6...

Впрочем, хватит лирики. Давайте посмотрим на простой PHP-скрипт. Сначала немного изменим HTML-форму из предыдущего раздела:

<form method="POST" action="form_handler.php">
Введите Ваше имя: <input type="text" name="name">
<br>
<input type="submit" name="okbutton" value="OK">
</form>

А теперь - form_handler.php:

<html>
<body>
<?
echo "<h1>Привет, <b>" . $_POST['name'] . "</b></h1>!";
?>
</body>
</html>

В отличие от Си или Perl, php-скрипт представляет собой обычную, в общем-то, HTML-страницу: "просто так" написанные тэги передаются "как есть", будто бы это обычный html-ник. Сам скрипт заключается в специальные тэги <? и ?>, внутри которых мы используем для вывода текста оператор echo. Таких блоков может быть сколько угодно, все, что между ними, интерпретируется как обычный html.

Переменные GET-запроса попадают в массив $_GET, переменные POST-запроса - в массив $_POST, серверные переменные (типа IP-адреса, имени скрипта и т.д.) - в $_SERVER, оператор "точка" (.) - объединение строк... Причем все служебные операции (чтение stdin и переменных среды, Url-декодирование) уже произвел сам PHP. Удобно, не так ли?

Далее. Зачем нам два файла - HTML с формой и PHP-скрипт? Вполне достаточно одного скрипта:

<html>
<body>
<?
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
echo "<h1>Привет, <b>" . $_POST['name'] . "</b></h1>!";
}
?>
<form method="POST">
Введите Ваше имя: <input type="text" name="name">
<br>
<input type="submit" name="okbutton" value="OK">
</form>
</body>
</html>

Мы убрали из тэга form атрибут action - это значит, что форма отправляется "сама на себя", т.е. на текущий URL. Это иногда называют "postback form". В строке 4 с помощью оператора if проверяется, использовался ли для загрузки документа метод POST (аналог строки 13 примера на Си), и - если это так - в следующей строке выводится приветствие.

бензиновая мотопомпа Honda WH20XK1 DF E1

Ссылки
Реклама