Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель PHP 4 - Котеров Д. В

..pdf
Скачиваний:
92
Добавлен:
24.05.2014
Размер:
4.38 Mб
Скачать

38

Часть I. Основы Web-программирования

Использование формы

Как теперь нам сделать, чтобы пользователь мог в удобной форме ввести свое имя и возраст? Очевидно, нам придется создать что-то вроде диалогового окна Windows, только в браузере. Итак, нам понадобится обычный HTML-документ (например, с именем form.html и расположенный в корневом каталоге) с элементами этого диалога — полями ввода текста и кнопкой, при нажатии на которую запустится наш сценарий. Текст этого документа приведен в листинге 2.1.

Листинг 2.1. Документ /form.html с формой

<html><body>

<form action=script.cgi method=GET>

Введите имя:

<input type=text name="name" value="Неизвестный"><br>

Введите возраст:

<input type=text name="age" value="неопределенный"><br> <input type=submit value="Нажмите кнопку!"> </body></html>

Вы можете заметить, что некоторые атрибуты тэгов я написал в кавычках (на- пример, name="age"), а некоторые нет. Как показывает практика, везде, где это не конфликтует с синтаксисом HTML (то есть, в текстах, в которых нет про- белов и букв кириллицы), можно кавычки опускать. Мне лично нравится за- ключать значения полей name и value в кавычки, а остальные писать без них. Правда, стандарт на язык HTML это не допускает (он требует обязательного наличия кавычек), но большинство браузеров относится к этому весьма и весьма лояльно.

Загрузим наш документ в браузер. Получим примерно следующее:

Глава 2. Интерфейс CGI

39

Рис. 2.1. HTML-форма

Теперь, если занести в поле name свое имя, а в поле для возраста — возраст и нажать кнопку Нажмите кнопку!, браузер обратится к сценарию по URL, указанному в атрибуте action тэга <form> формы:

http://www.somehost.com/script.cgi

Он передаст через ? все параметры, которые помещены внутрь тэгов input в форме, отделяя их амперсандом (&). Имена полей и их значения будут разделены знаком =. Теперь вы понимаете, почему мы с самого начала использовали эти символы?

Итак, реальный URL, который будет сформирован браузером при старте сценария, будет таким (учитывая, что на странице был пользователь по имени Vasya и ему 20 лет):

http://www.somehost.com/script.cgi?name=Vasya&age=20

Самое, пожалуй, полезное, что можно вынести из рассмотренного примера, — то, что все URL-перекодирования и преобразования осуществляются браузером автоматически. То есть, пользователю теперь совершенно не нужно об этом задумываться и ломать голову над путаницей шестнадцатеричных кодов и управляющих символов.

Абсолютный и относительный путь к сценарию

Обратим внимание на поле action тэга <form>. Поскольку он не предваряется слэшем (/), то представляет собой относительный путь к сценарию. То есть браузер при анализе тэга попытается выдать запрос на запуск сценария, имеющего имя script.cgi и расположенного в том же самом каталоге, что и форма (точнее, HTML-документ с формой).

40

Часть I. Основы Web-программирования

Как вы, наверное, догадались, термин "каталог" здесь весьма условен. На са- мом-то деле имеется в виду не реальный каталог на сервере (о котором брау- зер, кстати, ничего не знает), а часть URL, предшествующая последнему сим- волу / в полном URL файла с формой. В нашем случае это просто

http://www.somehost.com. Заметьте, что здесь учитывается имя хоста. Как видим, все это мало похоже на обычную файловую спецификацию.

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

<form action="/some/path/script.cgi">

Браузер определит, что это абсолютный путь в пределах текущего хоста (точнее, хоста, на котором расположен документ с формой) по наличию символа / впереди пути. Рекомендуется везде, где только возможно, пользоваться таким определением пути, всячески избегая указания абсолютного URL с именем хоста — конечно, за исключением тех ситуаций, когда ресурс размещен сразу на нескольких хостах (такое тоже иногда встречается).

Во втором случае получится приблизительно следующее:

<form action="http://www.other.com/any/script.cgi">

Еще раз обратите внимание на то, что браузеру совершенно все равно, где находится запускаемый сценарий — на том же хосте, что и форма, или нет. Это позволяет создавать сайты, расположенные на нескольких хостах, "прозрачно" для их посетителей. Вся идеология сети Интернет и службы World Wide Web построена на этой идее — возможности свободного перемещения (и ее легкости) по гиперссылкам, где бы ни находился сервер, на который они указывают.

Метод POST и формы

Что же теперь нужно сделать, чтобы послать данные не методом GET, а методом POST? Нетрудно догадаться: достаточно вместо method=GET указать method=POST. Больше ничего менять не надо.

Если не задать параметра action в тэге <form> вообще, то по умолчанию подразумевается метод GET.

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

Однако в рассмотренной схеме не все гладко с точки зрения простоты: сценарий один, а файла-то два (документ с формой и файл сценария). Есть простое обходное

Глава 2. Интерфейс CGI

41

решение этой проблемы, которое рекомендуется применять всюду, где это только возможно: пусть сценарий в первую очередь проверяет, запущен ли он с параметрами или без них. Если параметров нет, то сценарий выдает пользователю HTML-документ с формой, в противном случае — результаты работы. Это удобно еще и потому, что, возможно, вы захотите, чтобы пользователь обязательно ввел свое имя. То есть, если он забудет это сделать, ему будет выдана все та же форма с сообщением напротив поля ввода для имени: "Извините, но Вы забыли ввести свое имя. Попробуйте еще, вдруг на этот раз получится?". А в следующей главе мы попутно рассмотрим, как проще всего определить, был запущен сценарий по нажатии кнопки или же просто набором его URL в браузере.

Приведенная схема минимизации количества документов стандартна и весьма универсальна (ее применют 99% сценариев, которые можно найти в Интерне- те). Она еще и удобна для пользователя, потому что не создает "мертвых" ссылок (любой URL сценария, который он наберет, пусть даже и без парамет- ров, будет корректным). Однако программирование этой схемы на Си (и на не- которых других языках) вызывает определенные проблемы. Язык PHP таких проблем лишен.

Глава 3

CGI изнутри

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

И это только небольшая часть вопросов, которые пока остаются открытыми. В этой главе я постараюсь вкратце описать, как же должны на самом деле быть устроены внутри CGI-сценарии. На мой взгляд, каждый программист обязан хотя бы в общих чертах знать, как работает то, что он использует — будь то операционная система (ОС) или удобный язык-интерпретатор для написания CGI-сценариев (каким является PHP). А значит, речь пойдет о программировании на Си. Я выбрал Си, т. к. это одно из самых лучших и лаконичных средств; кроме того, именно на Си чаще всего пишут те сценарии, которым требуется максимально критичное быстродействие (базы данных, поисковые системы, системы почтовой рассылки с сотнями тысяч пользователей и др.). В пользу этого языка говорит также и то, что его компиляторы можно встретить практически в любой сколько-нибудь серьезной ОС.

Тем не менее, вы не найдете в этой главе ни одной серьезной законченной программы на Си (за исключением разве что самой простой, типа "Hello, world!"). Несмотря на это, я попытаюсь описать практически все, что может понадобиться при программировании сценариев на Си (кроме работы с сокетами, — это тема для отдельной книги, да и, пожалуй, лишь косвенно примыкает к Web-программированию). По возможности я не буду привязываться к специфике конкретной ОС, ведь для CGI существует стандарт, независимый от операционной системы, на которой будет выполняться сценарий. Вооружившись материалом этой главы, можно написать самые разнообразные сценарии — от простых до самых сложных (правда, для последних потребуется также недюжинная сноровка).

И все-таки, моя цель — набросать общими мазками, как неудобно (повторюсь — именно неудобно!) программировать сценарии на языках, обычных для прикладного программиста (в том числе на Си и Си++). Как только вы проникнетесь этой идеей, мы плавно и не торопясь двинемся в мир PHP, где предусмотрены практически все удобства, так необходимые серьезному языку программирования сценариев.

Если вы не знакомы с языком Си, не отчаивайтесь. Все примеры хорошо комментированы, а сложные участки не нуждаются в непременном понимании "с первого про-

Глава 3. CGI изнутри

43

чтения". Еще раз оговорюсь, что материал этой и следующей глав предназначен для того, чтобы вы получили приблизительное представление о том, как же устроен протокол HTTP и как программы взаимодействуют с его помощью. Думаю, что без этих знаний невозможна никакая профессиональная работа на поприще Webпрограммирования. Так что не особенно расстраивайтесь, если вы совсем не знаете Си — ведь эта глава содержит гораздо больше, нежели просто описание набора Сифункций. В ней представлен материал, являющийся связующим звеном между CGI и HTML, детально описываются тэги форм и их наиболее полезные атрибуты, приемы создания запросов и многое другое. Все это, безусловно, понадобится нам и при программировании на PHP.

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

Передача документа пользователю

Вначале рассмотрим более простой вопрос: как программа посылает свой ответ (то есть документ) пользователю.

А сделано это просто и логично (а главное, универсально и переносимо между операционными системами): сценарий просто помещает документ в стандартный поток вывода (на Си он называется stdout), который находится под контролем программного обеспечения сервера. Иными словами, программа работает так, как будто нет никакого пользователя, а нужно вывести текст прямо на "экран". (Это она так думает, на самом деле выводимая информация будет перенаправлена сервером в браузер пользователя. Ясно, что у сценария никакого "экрана" нет и быть не может.)

Ответ программы, как и запрос пользователя, должен состоять из заголовков. Иными словами, мы не можем просто направить документ в стандартный поток вывода: нам сначала нужно по крайней мере указать, в каком формате информация должна быть передана пользователю. Действительно, представьте, что произойдет, если браузер попытается отобразить GIF-рисунок в текстовом виде? В худшем случае вашим пользователям придется всю жизнь лечиться от заикания — особенно если до этого их просили ввести номер кредитной карточки.…

Заголовки ответа

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

44

Часть I. Основы Web-программирования

его значение, разделенные двоеточием. Наличие пустого заголовка в конце также можно интерпретировать как два стоящих подряд обозначения \n\n. Затем, как обычно, могут следовать данные ответа, которые и являются документом, который будет отображен браузером.

Заголовок кода ответа

Однако здесь все же имеется одно отличие от формата, который используется в заголовках запроса. Дело в том, что первый заголовок ответа обязан иметь слегка специфичный вид — в нем не должно быть двоеточия. Он задает так называемый код от- вета сервера и выглядит, например, так:

HTTP/1.1 OK

или так:

HTTP/1.1 404 File Not Found

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

Чаще всего (за исключением редких случаев) браузеры не обращают особого внимания на заголовок кода ответа, а просто выводят следующий за ним документ. Кроме того, такой заголовок формируется сервером, а в сценарии мы никак не можем его изменить (правда, есть специальный заголовок Status, но мы не будем здесь о нем говорить). Поэтому я и не рассматриваю подробно этот вопрос в данной книге.

Вот другие наиболее распространенные заголовки ответа.

Content-type

r Формат: Content-type: mime_тип; charset=koi8-r

Задает тип документа и его кодировку. Параметр charset задает кодировку документа (в нашем примере это KOI8-R). Поле mime_тип определяет тип информации, которую содержит документ:

rtext/html — HTML-документ;

rtext/plain — простой текстовый файл;

rimage/gif — GIF-изображение;

rimage/jpeg — JPG-изображение;

rеще несколько десятков других типов.

Pragma

Формат: Pragma: no-cache

Глава 3. CGI изнутри

45

Запрещает кэширование документа браузером, так что при повторном визите на страницу браузер гарантированно загрузит ее снова, а не извлечет из своего кэша. Это может быть полезно, если страница содержит, например, динамический счетчик посещений.

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

Location

Формат: Location: http://www.otherhost.com/somepage.html

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

Рекомендуется всегда указывать в заголовке Location абсолютный путь вме- сте с именем хоста, а не относительный. Дело в том, что, как показывает прак- тика, не все браузеры правильно реагируют на относительные пути и вытво- ряют все, что им заблагорассудится.

В браузере Netscape имеется ошибка, проявляющаяся, когда сценарий выво- дит заголовок Location с указанием перейти на собственный URL (то есть, сам на себя, для этого даже придуман специальный термин self-redirect). Такое решение не так бесполезно, как кажется, и используется, например, в гостевых книгах. В этом случае Netscape прекрасно принимает ответ сценария, но затем почему-то сообщает о том, что "документ не содержит данных". Как решить указанную проблему, см. в части V книги.

Set-cookie

Формат: Set-cookie: параметры_cookie

Устанавливает Cookie в браузер пользователя. Позже в этой главе мы рассмотрим подробнее, что такое Cookies и как с ними работать.

Date

Формат: Date: Sat, 08 Jan 2000 11:56:26 GMT

Указывает браузеру дату отправки документа.

46

Часть I. Основы Web-программирования

Server

Формат: Server: Apache/1.3.9 (Unix) PHP/3.0.12

Устанавливается сервером и указывает браузеру тип сервера и другую информацию о серверном программном обеспечении.

Пример CGI-сценария

Настало время привести небольшой сценарий на Си, который иллюстрирует некоторые возможности, которые были описаны выше (листинг 3.1).

Листинг 3.1. Простейший сценарий script.c

#include <time.h>

// Нужна для инициализации функции rand()

#include <stdio.h>

// Включаем поддержку функций ввода/вывода

#include <stdlib.h>

// А это — для поддержки функции rand()

// Главная функция. Именно она и запускается при старте сценария. void main(void) {

//инициализируем генератор случайных чисел int Num; time_t t; srand(time(&t));

//в Num записывается случайное число от 0 до 9 Num = rand()%10;

//далее выводим заголовки ответа. Тип — html-документ printf("Content-type: text/html\n");

//запрет кэширования

printf("Pragma: no-cache\n");

//пустой заголовок printf("\n");

//выводим текст документа — его мы увидим в браузере printf("<html><body>"); printf("<h1>Здравствуйте!</h1>");

printf("Случайное число в диапазоне 0-9: %d",Num); printf("</body></html>");

}

Исходный текст можно откомпилировать и поместить в каталог с CGI-сценариями на сервере. Обычно стараются все сценарии хранить в одном месте — в каталоге cgibin, у которого имеется разрешение на выполнение всех файлов внутри него. Правда, это правило не является обязательным — конечно же, можно разместить файлы сценария где душе угодно (не забыв проставить соответствующие права на каталог в на-

Глава 3. CGI изнутри

47

стройках сервера). На мой взгляд, логично хранить файлы сценариев там, где это наиболее вам удобно, а не пользоваться общепринятыми штампами. Теперь наберем в адресной строке браузера:

http://www.myhost.com/cgi-bin/script.cgi

Мы получим нашу HTML-страницу. Заметьте, что при нажатии Reload (а также при повторном посещении страницы) браузер перезагрузит страницу целиком, а не возьмет ее копию из своего кэша (это можно видеть по постоянно изменяющемуся случайному числу или по лампочкам модема). Мы добились такого результата благодаря заголовку

Pragma: no-cache

Давайте теперь посмотрим, что нужно изменить в нашем сценарии, чтобы его вывод представлял из себя с точки зрения браузера не HTML-документ, а рисунок. Пусть нам нужен сценарий, который бы передавал пользователю какой-то GIF-рисунок (например, выбираемый случайным образом из некоторого списка). Делается это абсолютно аналогично: выводим заголовок

Content-type: image/gif

Затем копируем один-в-один нужный нам GIF-файл в стандартный поток вывода (лучше всего — функцией fwrite, т. к. иначе могут возникнуть проблемы с "бинарностью" GIF-рисунка). Теперь можно использовать этот сценарий даже в таком контексте:

... какой-то текст страницы ...

<img src=http://www.myhost.com/cgi-bin/script.cgi>

... продолжение страницы ...

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

Еще раз обращаю ваше внимание на такой момент: CGI-сценарии могут использоваться не только для вывода HTML-информации, но и для любого другого ее типа — начиная с графики и заканчивая звуковыми MIDI-файлами. Тип документа задается в единственном месте — заголовке Content-type. Не забывайте добавлять этот заголовок, в противном случае пользователю будет отображена стандартная страница сервера с сообщением о 500-й ошибке (для сервера Apache), из которой он вряд ли что поймет.