Skip to content

Latest commit

 

History

History
executable file
·
439 lines (268 loc) · 62.7 KB

web-server.md

File metadata and controls

executable file
·
439 lines (268 loc) · 62.7 KB

Настраиваем веб-сервер для работы с PHP из браузера

Это урок для тех, кто уже знает основы языка PHP. В нем мы научимся отображать простейшие странички в браузере. Для этого мы узнаем как работает, установим и научимся запускать встроенный в PHP веб-сервер, а также веб-сервер Апач.

Прежде чем все это делать, нам надо понять, зачем нужен веб-сервер и что происходит, когда браузер загружает страницу.

Как работает браузер

Браузер - это программа, которая загружает с веб-сервера и отображает на экране веб-страницы. Когда ты переходишь по ссылке или руками вводишь в адресную строку адрес вроде http://example.com/news.html, происходит следующее:

  • браузер устанавливает соединение через интернет с узлом example.com, с запущенной на нем программой под названием веб-сервер
  • браузер посылает программе-веб-серверу запрос по протоколу HTTP (протокол - это язык для общения программ между собой) с просьбой предоставить ему страницу по адресу example.com/news.html
  • веб-сервер в ответ отправляет файл в формате HTML (HTML - это текст, размеченный специальными тегами), содержащий тело страницы
  • браузер отображает этот файл на экране. Если страница включает в себя другие файлы, например, картинки или видеоролики, браузер для каждого из них устанавливает новое соединение и отправляет новый запрос на сервер

Протокол HTTP

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

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

Нам пока не требуется знать синтаксис и особенности HTTP наизусть, но надо иметь общее представление о нем. Потому изучим, как он устроен.

Протокол HTTP поддерживает 2 основных вида запросов (они называются методы): GET и POST. Метод GET используется для получения (скачивания) с сервера файлов и страниц (это одно и то же, так как веб-страница - это просто файл в формате HTML). Браузер указывает в HTTP-запросе URL файла, который он хотел бы получить, и веб-сервер возвращает в HTTP-ответе либо запрошенный файл, либо сообщение об ошибке.

Метод POST позволяет отправлять на сервер данные из заполненных пользователем форм, в том числе с приложенными файлами. Браузер указывает URL, на который отправляются данные, и прикладывает значения, введенные пользователем в поля формы. В ответ сервер может вернуть какую-то страницу (например, с текстом об успешном приеме данных), которую браузер покажет пользователю.

Вот пример HTTP-запроса, который может послать браузер для получения данных с сервера. Он просит сервер передать ему содержимое страницы по адресу http://example.com/news.html:

GET /news.html HTTP/1.1
Host: example.com
User-Agent: Chromius/43
Accept: text/html,application/xhtml+xml,application/xml
Accept-Language: ru, en

В HTTP запросы и ответы могут состоять из 3 частей - стартовая строка, заголовки и тело. В примере выше стартовая строка GET /news.html HTTP/1.1 содержит метод запроса, путь к файлу из URL и версию протокола. В HTTP любой запрос начинается со стартовой строки, содержащей эти 3 элемента.

URL - это указатель на конкретный файл или страницу на сервере ("адрес" или идентификатор страницы). Напомню, что у нас есть урок, который рассказывает подробнее, что такое URL и из каких частей он состоит.

После стартовой строки идут заголовки запроса. Они содержат дополнительную информацию о запросе и о браузере. Например, заголовок User-Agent содержит название и версию клиента, а заголовок Accept-Language указывает, на каких языках (в данном примере - русский и английский) клиент предпочел бы получить информацию. Обязательный заголовок только один - Host, он указывает домен, с которого мы запрашиваем страницу, все остальные заголовки можно не указывать. Заголовки заканчиваются одной пустой строкой.

После заголовков (и пустой строки) может идти тело запроса, содержащее какие-то данные, которые отправляются на сервер. В этом примере тело запроса отсутствует.

Вот как может выглядеть ответ на этот запрос, если запрашиваемая страница есть на сервере:

HTTP/1.1 200 Ok
Server: nginx/1.0.0
Date: Sat, 11 Jul 2015 01:54:57 GMT
Content-Type: text/html; charset=UTF-8

<h1>Hello World</h1>

Ответ точно также, как и запрос, начинается со стартовой строки. Она всегда содержит ровно 3 элемента: версию протокола, которую использует сервер (HTTP/1.1), код состояния, указывающий, успешно ли обработан запрос или была ошибка (в данном случае 200, успешно), и текстовое человекочитаемое пояснение кода состояния (Ok - все хорошо).

После стартовой строки идут заголовки. Заголовок Server описывает название и версию программы веб-сервера, которая обработала запрос. Заголовок Content-Type описывает тип файла, который содержится в теле ответа. В данном случае это text/html; charset=utf-8, то есть текстовый файл с разметкой на языке HTML в кодировке utf-8.

После заголовков идет пустая строка и за ней тело ответа, которое в данном примере содержит текстовый файл в формате HTML (<h1>Hello world</h1>). Браузер отобразит его содержимое на экране.

HTML файл содержит текст страницы и специальные теги (метки) вроде <h1>, которые разбивают этот текст на абзацы, заголовки, позволяют добавлять в него ссылки, картинки, формы и многое другое. Ты можешь увидеть полученный от сервера HTML-код любой страницы в интернете, зайдя на нее и нажав Ctrl + U в браузере или выбрав в меню что-то вроде "Вид" - "Показать исходный код страницы".

Заголовок Content-Type влияет на то, как именно браузер отобразит содержимое. Если там будет например указано image/png, то браузер будет воспринимать тело ответа как картинку в формате PNG и попытается его отобразить. Конструкция вроде image/png называется MIME-тип, она состоит из 2 частей: общего типа данных (image, text, audio) и конкретного формата файла. Подробнее про MIME-типы можно почитать в Википедии.

Как видишь, сервер в ответ сообщил что запрос обработан успешно. Вот пример ответа с сообщением об ошибке:

HTTP/1.1 404 Not Found
Server: nginx/1.0.0
Date: Sat, 11 Jul 2015 01:54:57 GMT
Content-Type: text/html; charset=UTF-8

Page /news.html was not found on this server.  

Этот ответ отличается от предыдущего кодом состояния. В данном случае он равен 404, что значит "запрошенный файл отсутствует на сервере". Тело ответа содержит HTML-код с текстом ошибки ("Page ... not found on this server"), которая будет показана пользователю.

Вот самые распространенные коды состояний:

  • 200 Ok - все ок, тело ответа содержит запрошенный файл
  • 500 Internal Server Error - сервер не смог обработать запрос из-за ошибки на нем
  • 404 Not Found - запрошенный файл не был найден на сервере (скорее всего, неправильно указан URL в запросе)
  • 403 Access Denied - доступ к запрошенному файлу запрещен
  • 301 Moved Permanently - запрошенный файл находится теперь по другому адресу, который указан в заголовке ответа Location. Браузер должен сделать новый запрос по этому адресу.
  • если тебе интересно, можешь глянуть полный список кодов состояний в Википедии

Рассмотрим напоследок еще пример POST запроса. Допустим, что где-то на сайте example.com есть форма логина, пользователь ввел туда свои логин и пароль и отправил ее. Вот как будет выглядеть POST запрос, который браузер отправит на сервер:

POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 26

login=ivan&password=123456

Этот запрос точно так же содержит стартовую строку, заголовки запроса, и тело запроса (в GET-запросе выше тела не было), в котором передаются введенные в форму пользователем данные (логин и пароль). Заголовок Content-Type указывает, что в теле содержатся данные из формы и указывает способ их кодирования, а заголовок Content-Length содержит размер данных в байтах.

Что отправит сервер в ответ на этот запрос, зависит от того, как программа на сервере будет обрабатывать эти данные.

Формат запросов и ответов, поведение веб-сервера описано в документации к протоколу HTTP. Если тебе интересны подробности, можно прочесть мой урок по HTTP или начать с Википедии: https://ru.wikipedia.org/wiki/HTTP.

Ты можешь увидеть какой именно запрос отправляет твой браузер, зайдя на любой сайт, открыв developer tools на вкладке Network (Ctrl + Shift + I в Хроме (и его клонах вроде яндекс-браузера), Опере, Фаерфоксе, F12 в ИЕ, через меню "инструменты" в Сафари) и перезагрузив страницу. Ты увидишь список отправленных HTTP запросов, а при клике по ним сможешь их посмотреть.

Таким образом, задача браузера - отправить запрос на сервер, получить HTML файл и отобразить на экране. А задача веб-сервера - принимать и выполнять запросы от браузера, например предоставлять (или генерировать) запрошенные файлы и страницы.

Статические и динамические страницы

Есть 2 варианта, откуда веб-сервер может получать файлы, которые он отдает в браузер. Статический файл - это файл, который расположен на жестком диске сервера. Разработчик подготавливает файлы и загружает их на сервер, а тот по HTTP-запросу отдает их в браузер. Преимущество статических сайтов в том, что их просто создавать, они быстро работают, они очень надежны и там редко возникают ошибки или уязвимости, но у них есть недостаток: они не интерактивны, на них нельзя сделать регистрацию, добавление комментариев, постов, лайки и другие возможности, которые требуют многовенного обновления информации. Чтобы изменить что-то на таком сайте, веб-мастер должен вручную отредактировать эти HTML файлы и выгрузить измененные копии на сервер. Не-программист не может это сделать.

Например, статический сайт хорошо подходит для публикации документации, информационного сайта, финансового отчёта, книги, какой-то информации, которая редко обновляется.

Также, статический сайт имеет одну особенность: так как это просто набор файлов, то его можно просматривать даже без веб-сервера и связи с интернетом. Достаточно сохранить HTML-файл со страницей себе на компьютер (например, нажав Ctrl + S в браузере), после этого её можно открыть и просмотреть в браузере (дважды кликнув или перетащив его в окно браузера). Или можно сделать архив с содержимым сайта и раздавать пользователям.

Динамические страницы работают по другому принципу. В этом случае страница генерируется программно, "на лету". При поступлении запроса от браузера веб-сервер запускает программу (например, на PHP), и то, что выведет эта программа, веб-сервер отправит в браузер в качестве ответа на запрос. Обычно программа хранит данные сайта (статьи, новости, комментарии, пользователей) в базе данных, потому при поступлении запроса эти данные берутся из базы и подставляются в шаблон страницы.

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

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

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

Язык PHP заточен под создание динамических сайтов, что и сделало его очень популярным. Есть и другие языки, пригодные для этого - например, Java, Ruby, Python, Javascript, C# - но на PHP простые страницы сделать проще.

Веб-сервер

Итак, для того, чтобы отобразить в браузере какую-то страницу, нам нужен веб-сервер (чтобы отвечать на запросы браузера). Есть несколько разных программ-серверов, например Apache, Nginx, но мы начнем с изучения простого веб-сервера, встроенного в интерпретатор php.

Сначала нам необходимо установить PHP себе на компьютер (или получить доступ к линукс-серверу, где он уже установлен). Как именно устанавливать PHP, зависит от операционной системы:

  • для Windows - инструкции описаны в уроке по установке PHP на Windows
  • для линукс - способ зависит от используемого дистрибутива. Например, в дебиан или убунту это делается командой sudo apt-get install php5, в других дистрибутивах - немного другой командой. Погугли
  • для MacOS X - погугли
  • для андроида - придется помучиться. На июль 2016 года актуальна такая последовательность действий: для начала надо установить scripting layer for android - скорее всего его нет в Google Play, и надо установить apk файл вручную (будь осторожен, то что в Google Play находится по словам sl4a - это какие-то посторонние приложения). Затем из этого приложения надо установить PHP. И тогда появится возможность открыть окно командной строки и запускать сам PHP.

PHP - это программа командной строки. Это значит, что у него нет никакого графического интерфейса с кнопками и окнами, а для выполнения какого-то действия надо набрать в командной строке правильную команду. Потому сначала рекомендую изучить наш урок по использованию командной строки. Настройки php задаются в файле php.ini, который в linux лежит в /etc/php/, а в Windows - в папке с PHP, но нам пока не требуется их менять.

Проверь, что ты смог правильно установить PHP. Для этого набери команду

php --version

Если все верно, то она выведет информацию о версии установленного интерпретатора PHP (если у тебя PHP не находится в PATH, то вместо php придется писать полный путь к файлу, например c:\php\php.exe). Еще одна полезная команда - это php -i - она выводит информацию о текущих настройках PHP. Все возможные опции, которые можно указать, перечислены в мануале по использованию php.

Встроенный в PHP сервер

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

Инструкции ниже требуют знания командной строки, если ты не знаешь ее, то прочти сначала урок по ней.

Итак, в PHP встроен простейший веб-сервер для разработчиков. Чтобы запустить его, создай папку, из которой будут раздаваться файлы, она называется корень сервера или document root. Допустим, это d:\server. Открой командную строку и перейди в эту папку, а затем запусти веб-сервер, набрав следующие команды. После каждой команды надо нажимать клавишу Enter. Будь внимателен при их наборе, не пропускай пробелы, не путай прямую и обратную наклонную черту:

d:
cd \server
c:\php\php.exe -S localhost:9001

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

-S обозначает «запуститься в режиме веб-сервера». Надо написать именно заглавную S, c маленькой буквой не заработает. localhost (вместо него можно еще писать 127.0.0.1 - это твой собственный адрес) обозначает принимать соединения только со своего компьютера, и не принимать соединения с других устройств (если хочешь чтобы твой сервер был доступен во всей локальной сети, пиши вместо localhost адрес 0.0.0.0 — после этого к тебе можно будет зайти по ip).

9001 — это номер порта, на котором сервер будет ждать соединения от браузера. Если произойдет ошибка и будет написано что этот порт уже занят, введи другое число (от 1 до 65534), например 9002. Вообще-то обычно для веб-сервера используется порт 80, но у тебя он может быть занят другими программами - например, скайпом, торрентокачалкой или чем-то еще (если это так, стоит зайти в их настройки и запретить его использовать на будущее). Также ты можешь увидеть список занятых портов командой netstat -an, а команда netstat -abn покажет программу, занявшую порт (нужно запускать эту команду из консоли с повышенными привилегиями).

Учти что в линуксе и маке, чтобы открыть порт ниже 1024, нужны права администратора (то есть сервер надо запускать через sudo: sudo php ..., что не очень безопасно и не рекомендуется).

Номер порта нужен для того, чтобы на компьютере можно было одновременно запустить несколько работающих с сетью программ. Мы назначаем каждой из них свой номер порта. Когда на компьютер приходят данные из сети, ОС по номеру порта понимает какой именно программе они адресованы. Две программы не могут использовать одновременно один и тот же порт. Подробнее о TCP-портах.

Завершить работу сервера можно, нажав Ctrl + C или закрыв окно консоли. Сервер будет в процессе работы писать в консоль информацию о поступающих от браузера запросах и информацию о возникающих ошибках.

Теперь надо проверить, как работает наш сервер. По умолчанию веб-сервер просто отдает файлы из корневой папки, путь к которым указан в URL. Создай в папке сервера файл, например 1.txt и напиши в нем текст, например hello world (латиницей, чтобы не беспокоиться о кодировках). После этого открой браузер и введи в адресную строку адрес

http://localhost:9001/1.txt

В этом URL мы указываем, что браузер должен отправить запрос по протоколу HTTP на узел localhost (то есть твой собственный компьютер), на порт 9001 (который использует веб-сервер) и запросить у него файл /1.txt. Если ты забыл что такое URL, то у нас есть урок по ним: ../network/urls.md.

Если все верно, ты должен увидеть содержимое текстового файла, а в консоли появится строчка с этим запросом. Если что-то не работает - перепроверяй, запущен ли php, что он пишет в консоль, правильно ли ты написал слово localhost и номер порта.

Попробуй теперь скопировать в корневую папку какую-нибудь картинку, например, cat.jpg, и открыть в браузере URL http://localhost:9001/cat.jpg . Ты должен увидеть эту картинку.

Повторим еще раз, что происходит в этом случае:

  • браузер соединяется с веб-сервером на порту 9001
  • браузер отправляет запрос на получение файла cat.jpg
  • сервер обрабатывает запрос, находит файл и отправляет ответ с кодом 200, заголовком Content-Type: image/jpg и содержимым файла в теле ответа
  • браузер извлекает картинку из тела ответа и отображает на экране

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

Пока мы запрашивали текстовые файлы и картинки, сервер в PHP отдавал их как статические файлы. Но сервер умеет еще генерировать страницы динамически. Если мы запросим PHP-файл, то веб-сервер вместо отдачи этого файла запустит записанный в нем код и отдаст в браузер то, что выводит этот код.

Создадим в корневой папке файл 1.php с таким содержимым:

<?php
header("Content-Type: text/plain; charset=utf-8");
echo mt_rand(1, 100);

Функция header() позволяет добавить в HTTP-ответ произвольный заголовок. В данном случае мы используем ее, чтобы сказать браузеру, что данные, которые мы выводим, являются обычным текстом в кодировке utf-8 без HTML-разметки и надо отобразить его как есть. Если это не сделать, то по умолчанию PHP укажет тип text/html, и браузер будет думать что перед ним текст в формате HTML. Из-за этого, например, не будут отображаться переводы строк.

Если ты знаешь язык PHP, то наверно догадываешься, что выведет этот скрипт. Набери в браузере URL http://localhost:9001/1.php. Если все верно, ты увидишь на экране случайное число. Обнови страницу и число поменяется. Это происходит из-за того, что каждый раз браузер отправляет новый HTTP-запрос, сервер видит что запрашивается php файл, и запускает написанную в нем программу, и отдает в браузер то, что выводит эта программа. Ну а браузер отображает полученный текст на экране.

Обрати внимание, что браузер сам не умеет выполнять PHP-код. Он лишь посылает запрос на сервер и отображает то, что придет в ответ. PHP-код выполняет именно сервер.

Если ты видишь вместо него белую страницу, а исходный код в браузере показывает текст скрипта - значит скрипт не выполнился. Проверь, правильный ли URL в адресной строке браузера. Если там что-то вроде file://d:/server/1.php - значит ты невнимательно прочел инструкции выше. Протокол file:// обозначает, что браузер открывает файл напрямую с диска, а не запрашивает с веб-сервера, и потому PHP-код не будет выполняться.

Если при выполнении скрипта возникают какие-то ошибки, они отображаются в консоли, где запущен сервер. Не забывай туда поглядывать.

Наконец, давай сделаем еще один файл, который показывает текущие настройки PHP и который пригодится нам если что-то пойдет не так. Создай файл info.php с текстом:

<?php phpinfo();

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

Передача аргументов в скрипт

Для того, чтобы передать нашей PHP-программе какие-то данные, мы можем вписать их в query string (перечитай урок про URL, если не знаешь, что это такое). Это часть URL, которая идет после знака вопроса, например: http://localhost:9001/add.php?x=10&y=20. Перед запуском программы PHP анализирует query string и извлекает значения из нее в специальный массив $_GET. Напишем скрипт add.php, который выводит на экран сумму переданных значений:

<?php 
header("Content-Type: text/plain; charset=utf-8");

$x = array_key_exists('x', $_GET) ? floatval($_GET['x']) : 0;
$y = array_key_exists('y', $_GET) ? floatval($_GET['y']) : 0;
$sum = $x + $y;

echo "$x + $y = $sum\n";

Если пользователь не укажет в URL значения x и y, то PHP не поместит их в массив $_GET. Когда наш скрипт попытается обратиться к $_GET['x'], произойдет ошибка. Потому мы делаем проверку, что в массиве есть такой элемент, и если его нет, то присваиваем переменной значение 0. Если элемент есть, то мы обрабатываем значение функцией floatval, которая преобразует любое значение в целое или дробное число. Даже если пользователь вместо числа напишет бессмысленное значение вроде x=xyz, в программе не произойдет ошибки.

В PHP7 добавили новый оператор объединения с null, с помощью которого можно чуть упростить код:

$x = floatval($_GET['x'] ?? 0);

В этом месте ты можешь достать свои старые задачи, которые ты делал на PHP, и попробовать позапускать их через сервер и браузер.

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

Пробелы и перевод строки

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

header("Content-Type: text/plain; charset=utf-8");

Это заставит браузер воспринимать то, что выводит твоя программа, как обычный текст, а не HTML-код, и уважать переносы строк в нем. Иначе перенос строки будет в исходном коде страницы (его можно увидеть нажав Ctrl + U), но на самой странице его не будет.

Ведь по умолчанию веб-сервер отдает результат в браузер, говоря что это HTML-файл, а в этом языке любое число пробелов и переводов строк выводится как один пробел. Отдавая заголовок Content-Type, мы говорим браузеру что наш файл содержит обычный текст и не должен интерпретироваться как HTML код.

Индексный файл

Если ты попытаешься открыть URL, в котором не указано имя файла, например: http://localhost:9001/, то сервер будет искать файлы с названием index.php или index.html в корневой папке. Это так называемый "индексный" файл, который отдается по умолчанию, если конкретное имя файла не указано.

Если в URL указано только имя папки, без файла, то сервер будет искать индексный файл в ней. Ну, например, для URL http://localhost:9001/some/folder/ сервер будет искать файлы d:\server\some\folder\index.php или d:\server\some\folder\index.html.

Произвольные URL

До сих пор мы указывали путь к файлу или папке в URL. Но, что если мы хотим, чтобы наша страница имела бы более красивый URL, не /1.php, а например http://localhost:9001/latest-news, который не соответствует папке или файлу на диске? Для этого нам придется написать свой скрипт, который будет анализировать запрашиваемый URL и решать, что делать. Скрипт должен либо обработать запрос и что-то вывести, либо вернуть значение false для того, чтобы обработать запрос стандартным образом.

Эта возможность описана в мануале PHP по встроенному серверу.

Имя скрипта маршрутизации надо указать при запуске веб-сервера. Попробуем написать простой скрипт с такой логикой:

  • если запрошен URL /latest-news, то выполнить скрипт news.php
  • если запрошен URL /hello, то вывести фразу "hello world"
  • иначе искать указанный в URL файл

Создадим в корневой папке скрипт router.php с таким кодом. Если ты видишь тут незнакомые функции и команды, погугли их:

<?php
// Получаем запрошенный URL и вырезаем из него путь
$url = $_SERVER['REQUEST_URI'];
$path = parse_url($url, PHP_URL_PATH);

if ($path == '/latest-news') {
    // Выполняем программу из указанного файла
    require __DIR__ . '/news.php';
    exit;
} elseif ($path == '/hello') {
    header("Content-Type: text/plain; charset=utf-8");
    echo "Hello world";
    exit;
}

// Возвращаем управление веб-серверу, если мы не смогли обработать запрос
// Веб-сервер попытается самостоятельно найти указанный в запросе файл
return false;

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

php -S localost:9001 router.php

Попробуй открыть URL вроде http://localhost:9001/hello и проверить, что все работает, как и задумано.

Вот краткое объяснение использованных в скрипте выше конструкций:

  • $_SERVER - это специальный массив, в который перед выполнением скрипта PHP помещает информацию о конфигурации сервера и параметрах пришедшего HTTP-запроса. Ну например, пришедшие от браузера заголовки помещаются в этот массив. В нем есть элемент REQUEST_URI, который содержит указанный в запросе путь из URL. Если тебе любопытно, что в этом массиве есть еще, сделай скрипт с командой var_dump($_SERVER) и посмотри.
  • parse_url - это функция, которая извлекает из URL указанную часть
  • require - это команда PHP, которая выполняет код из указанного в ней файла.
  • __DIR__ - это встроенная в PHP константа, которая содержит полный путь к папке, где находится данный PHP-файл (например, c:\programs\php)
  • exit - команда завершения PHP программы
  • header - это функция, которая добавляет в HTTP-ответ (который отдаст веб-сервер) указанный заголовок

Установка Апача

Апач сложнее чем встроенный сервер, но дает больше возможностей.

Урок: Установка Апача

Что делать дальше

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

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

Наконец, стоит почитать туториал в официальном мануале PHP и научиться добавлять в HTML странички PHP код, а также обрабатывать данные из форм:

После этого прочитай урок про шаблоны.

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

Полезные функции и конструкции PHP, которые стоит изучить

  • require, require_once
  • header()
  • setcookie()

Задачи

Отправка HTTP запроса

Изучив протокол HTTP, попробуем самостоятельно отправить HTTP запрос и получить на него ответ. Для этого мы будем использовать программу telnet. Она работает под Windows и под linux (и возможно под маком). Вообще, она предназначена не для выполнения HTTP запросов, а использовалась много лет назад для выполнения команд на удаленном сервере. Но так как она просто передает вводимые данные на сервер и выводит полученный ответ, то мы можем использовать ее для своих целей.

Запускается она так: telnet хост порт, например telnet wikipedia.org 80. Обрати внимание, что надо писать именно имя хоста или IP-адрес, а не URL. Нельзя написать telnet http://wikipedia.org - это работать не будет. Номер порта для протокола HTTP по умолчанию - это 80.

Если запустить эту команду, то она соединится с указанным сервером на указанном порту и будет передавать на сервер все, что мы напечатаем, а затем отобразит присланный сервером ответ. То есть мы можем набрать текст HTTP-запроса и увидеть на экране HTTP-ответ от сервера. Но руками набирать HTTP-запрос не очень удобно, так как легко ошибиться (а исправить ошибку нельзя), и на сервере может стоять ограничение по времени, после которого он разорвет соединение, не дождавшись окончания запроса. Потому лучше заранее записать запрос в текстовый файл, и запустить команду, чтобы она читала бы запрос из него, вот так:

telnet example.com 80 < c:\file.txt

При сохранении файла выбери в редакторе опцию "кодирование перевода строк" как "Windows". В протоколе HTTP перевод строки должен кодироваться как \r\n (CR LF). Кстати, Блокнот Windows использует именно такой формат, и потому подойдет для набора запроса.

В файле с запросом должно быть примерно такое содержимое:

GET / HTTP/1.1
Host: example.com
User-Agent: human

Не забудь, что запрос должен как минимум содержать стартовую строку и заголовок Host. Не забудь заменить название домена на правильное и добавить одну пустую строку после заголовков. Создав файл, попробуй выполнить команду telnet. Если все верно, то ты должен увидеть ответ от сервера, начинающийся со строки вроде HTTP/1.1 200 Ok. Попробуй добиться именно ответа с кодом 200, пробуя разные сайты.

Так как сейчас (в 2016 году) многие сайты уже перешли на протокол HTTPS (который отличается от HTTP использованием шифрования и портом 443 по умолчанию), то часто ты будешь получать ответы, требующие перейти на зашифрованное соединение, вроде такого:

HTTP/1.1 303 See Other
Location: https://example.com/

К сожалению, написать зашифрованный запрос руками в текстовом редакторе вряд ли возможно. Однако, есть программы, которые могут взять шифрование на себя. Для этого тебе надо установить библиотеку OpenSSL. Найти сборку под Windows не так-то просто. На момент написания статьи (2016) ссылки на скачивание библиотеки под Windows доступны на этой странице: https://wiki.openssl.org/index.php/Binaries (отсюда переходим на сайт https://indy.fulgan.com/SSL/ и скачиваем самую новую версию вроде openssl-1.0.2m-i386-win32.zip или openssl-1.0.2m-x64_86-win64.zip в зависимости от разрядности ОС. Впрочем, 32-битная версия должна работать везде. Скачанный архив распаковываем в любую папку и при желании добавляем ее в PATH). Под linux библиотека устанавливается командой вроде sudo apt-get install openssl в зависимости от используемого дистрибутива.

Установив библиотеку, ты можешь установить HTTPS соединение и отправить запрос такой командой:

openssl s_client -connect example.com:443 < file.txt

Если исполняемый файл openssl не находится в PATH, то надо писать полный путь к нему, например, c:\openssl\openssl.exe. Обрати внимание, что HTTPS по умолчанию использует порт 443 вместо 80. Openssl возьмет на себя шифрование твоего запроса и расшифровку ответа.

При соединении openssl выведет довольно много отладочной информации об используемом сервером сертификате и параметрах SSL/TLS шифрования. Ее вывод можно отключить, дописав к команде (до символа <) флаг -quiet. Про другие опции можно прочесть в мануале по openssl s_client (англ., сложный).

Разумеется, для отправки HTTP-запросов есть и программы с графическим интерфейсом. Они гуглятся по словам "GUI HTTP client". Однако, чтобы ими пользоваться, надо понимать основы протокола HTTP и стоит все-таки хотя бы раз попробовать отправить запрос вручную. Примеры приложений (и расширений для ФФ):

Еще один кредит

Для решения этой задачи тебе надо изучить основы HTML, HTML-формы и глобальную переменную $_GET в PHP.

Сделай форму с 3 полями ввода: сумма кредита, ежемесячная выплата, комиссия и проценты в месяц. При их заполнении программа должна рассчитать кредит (аналогично задаче про айфон) и вывести один из вариантов:

  • надпись "поле X заполнено неверно", если введены неправильные данные
  • надпись "выплатить кредит невозможно так как ежемесячный прирост X больше ежемесячной выплаты", если выплатить кредит не получится
  • надпись "время выплаты: N месяцев, сумма выплаты: X"

Экранирование

Для решения этой задачи надо изучить основных HTML, HTML-формы и функцию htmlspecialchars().

Сделай страницу с формой (использующей метод GET) из поля ввода textarea (с именем text) и кнопки отправки. Никакого оформления и CSS не требуется, просто черный текст на белом фоне. При вводе любого текста и нажатия кнопки внизу под textarea должен отобразиться введенные текст и ссылка.

Текст должен точно соответствовать введенному, корректно отображать все символы, включая пробелы и переводы строк (тут тебе поможет тег pre), точно так как их ввел пользователь. Проверь что сочетания вроде

%20%20
<b>test
&amp;amp;
"'\<>
&t=1&

Отображаются ровно в том же виде как введены.

Кроме текста, надо выводить ссылку вида script.php?text=.....&lt=1, которая содержит в параметре text введенный текст, а в параметре lt единицу и открыв которую, мы можем увидеть его на странице.

Это задача на умение правильно экранировать символы. Например, в языке HTML символы вроде < или & имеют специальное значение (они открывают тег или HTML мнемонику) и их надо правильно экранировать. Аналогично с параметрами ссылки.

Подсказки:

  • про то, как корректно вставить текст с любыми символами в HTML-код, написано в уроке про XSS
  • по умолчанию в HTML любое число переводов строк и пробелов воспринимается как один пробел. Чтобы сохранить все пробелы и переводы строк при выводе, нужно либо использовать HTML-тег pre, либо CSS-свойство white-space
  • при подстановке параметра в ссылку вроде x.php?a=... необходимо корректно экранировать спецсимволы. Как это сделать, описано в уроке про структуру URL
  • при подстановке ссылки в HTML-атрибут href ее, разумеется, тоже нужно корректно экранировать

Я тебя помню

Для решения этой задачи необходимо изучить куки, переменную $_COOKIE и функцию setcookie().

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

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

Просмотреть куки можно с помощью developer tools в браузере.