Symfony2 и основы HTTP

Поздравляем! Начав изучение Symfony2, вы встали на правильный путь, чтобы стать более продуктивным, всесторонне развитым и популярным веб-разработчиком (хотя последнее - на ваше усмотрение). Symfony2 создан, чтобы предоставлять базовые, низкоуровневые инструменты, позволяющие вам разрабатывать быстрее, создавать более надёжные приложения, но при этом быть в строне от вашего собственного пути. Symfony построен на лучших идеях, заимствованных из различных технологий: инструменты и концепции, которые вы готовитесь изучить - представлены усилиями тысяч и тысяч людей на протяжении многих лет. Другими словами, вы не только изучаете “Symfony”, вы изучаете основы web, лучшие практики разработки, а также способы использования многих замечательных PHP-библиотек в составе Symfony2 или не зависимо от него. Итак, приготовьтесь.

Следуя философии Symfony2, эта глава начинается с объяснения основной концепции, типичной для web-разработки: HTTP. Не зависимо от вашего опыта или любимого языка программирования, эта глава обязательна к прочтению всем.

HTTP это Просто

HTTP (Hypertext Transfer Protocol или просто Протокол Передачи Гипертекста) - это текстовый язык, позволяющий двум компьютерам обмениваться сообщениями друг с другом. Вот и всё! Например, когда мы хотим посмотреть новенький комикс xkcd, имеет место (примерно) такой диалог:

images/http-xkcd.png

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

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

Шаг 1: Клиент отправляет запрос

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

Взгляните на первую часть взаимодействия (запрос) между браузером и веб-сервером xkcd:

images/http-xkcd-request.png

На языке HTTP этот запрос будет выглядеть примерно так:

GET / HTTP/1.1
Host: xkcd.com
Accept: text/html
User-Agent: Mozilla/5.0 (Macintosh)

Это простое сообщение содержит всю необходимую информацию о том, какой именно ресурс запрашивает клиент. Первая строка HTTP запроса наиболее важна - она содержит 2 вещи: запрошенный URI и HTTP-метод.

URI (например /, /contact, и т.д.) - это уникальный адрес или место, которое определяет запрошенный клиентом ресурс. HTTP-метод (например GET) определяет, что именно вы хотите сделать с запрошенным ресурсом. HTTP методы это глаголы в запросе и они определяют несколько типичных путей, которыми вы можете взаимодействовать с запрошенным ресурсом:

GET Получить ресурс с сервера
POST Создать ресурс на сервере
PUT Обновить ресурс на сервере
DELETE Удалить ресурс с сервера

Запомнив эти типы HTTP-методов, вы можете представить себе, как будет выглядеть HTTP-запрос на удаление записи в блоге:

DELETE /blog/15 HTTP/1.1

Note

На самом деле всего существует девять HTTP-методов, определённых в спецификации протокола HTTP, но многие из них очень мало распространены или же ограниченно поддерживаются. К примеру, многие современные браузеры не поддерживают методы PUT и DELETE.

В дополнение к первой строке, HTTP-запрос всегда содержит несколько информационных строк, именуемых заголовками (headers). Заголовки могут предоставлять различную информацию, такую как запрошенный Host, форматы ответа, которые поддерживает клиент (Accept) и приложение, используемое клиентом для выполнения запроса (User-Agent). Существует также много других заголовков, перечень которых вы можете найти в Википедии на странице List of HTTP header fields.

Шаг 2: Сервер возвращает ответ

С того момента как сервер получил запрос, он точно знает, какой ресурс нужен клиенту (основываясь на URI) и что клиент хочет с этим ресурсом сделать - на основании HTTP-метода. Например, в случае GET-запроса, сервер подготовит запрошенный ресурс и возвратит его в виде HTTP-ответа. Рассмотрим ответ от web сервера xkcd:

images/http-xkcd.png

Переведённый в формат HTTP, ответ, отправленный обратно в браузер, будет выглядеть примерно так:

HTTP/1.1 200 OK
Date: Sat, 02 Apr 2011 21:05:05 GMT
Server: lighttpd/1.4.19
Content-Type: text/html

<html>
  <!-- HTML for the xkcd comic -->
</html>

HTTP-ответ содержит запрошенный ресурс (в данном случае это HTML-код страницы), а также дополнительные данные о самом ответе. Первая строка особенно важна - она содержит HTTP статус-код (в данном случае 200). Статус-код сообщает о результате выполнения запроса, направляемом клиенту. Был ли запрос успешен? Была ли в ходе выполнения запроса ошибка? Одни статус-коды обозначают успешные запросы, другие - ошибки, третьи сообщают, что клиент должен выполнить что-либо (например перенаправление на другую страницу). Полный список вы можете найти странице List of HTTP status codes в Википедии.

Подобно запросу, HTTP-ответ содержит дополнительную информацию, называемую HTTP-заголовками. Например, важным заголовком HTTP-ответа является Content-Type. Тело одного и того же ресурса может быть возвращено во множестве различных форматов, включая HTML, XML или JSON. Заголовок Content-Type сообщает клиенту, какой именно формат используется в данном ответе.

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

Запросы, Ответы и Web-разработка

Обмен запросами-ответами - это фундаментальный процесс, который движет все коммуникации во всемирной сети. И насколько важен этот процесс, настолько он прост.

Наиболее важным является следующий факт: вне зависимости от того, какой языка программирования вы используете, какое приложение создаёте (web, мобильное, JSON API) и даже какой философии следуете в разработке ПО, конечной целью приложения всегда будет приём и разбор запроса и создание соответствующего ответа.

Symfony спроектирован исходя из этих реалий.

Tip

Для того чтобы узнать больше про спецификацию HTTP, прочитайте оригинал HTTP 1.1 RFC или же HTTP Bis, который является инициативой по разъяснению оригинальной спецификации. Замечательный инструмент для проверки заголовков запроса и ответа при сёрфинге - это расширение для Firefox Live HTTP Headers.

Запросы и ответы в PHP

Как же вы обрабатываете “запрос” и создаете “ответ” при использовании PHP? На самом деле PHP немного абстрагирует вас от процесса:

<?php
$uri = $_SERVER['REQUEST_URI'];
$foo = $_GET['foo'];

header('Content-type: text/html');
echo 'The URI requested is: '.$uri;
echo 'The value of the "foo" parameter is: '.$foo;

Как бы странно это ни звучало, но это крохотное приложение получает информацию из HTTP-запроса и использует её для создания HTTP-ответа. Вместо того, чтобы парсить необработанный HTTP-запрос, PHP подготавливает суперглобальные переменные, такие как $_SERVER и $_GET, которые содержат всю информацию о запросе. Аналогично, вместо того, чтобы возвращать текст ответа, форматированный по правилам HTTP, вы можете использовать функции header() для создания заголовков ответов и просто вывести на печать основной контент, который станет контентным блоком ответа. В заключении PHP создаст правильный HTTP-ответ и вернет его клиенту:

HTTP/1.1 200 OK
Date: Sat, 03 Apr 2011 02:14:33 GMT
Server: Apache/2.2.17 (Unix)
Content-Type: text/html

The URI requested is: /testing?foo=symfony
The value of the "foo" parameter is: symfony

Запросы и ответы в Symfony

Symfony предоставляет альтернативу прямолинейному подходу из PHP посредством двух классов, которые позволяют взаимодействовать с HTTP-запросом и ответом самым простейшим способом. Класс Symfony\Component\HttpFoundation\Request - это простое объектно-ориентированное представление сообщения HTTP-запроса. С его помощью вы имеете все данные из запроса “на кончиках пальцев”:

<?php

use Symfony\Component\HttpFoundation\Request;

$request = Request::createFromGlobals();

// запрошенный URI (на пример /about) без query parameters
$request->getPathInfo();

// получаем GET и POST переменные соответственно
$request->query->get('foo');
$request->request->get('bar');

// получаем экземпляр UploadedFile определяемый идентификатором foo
$request->files->get('foo');

$request->getMethod();          // GET, POST, PUT, DELETE, HEAD
$request->getLanguages();       // массив языков, принимаемых клиентом

В качестве бонуса, класс Request выполняет большой объём работы в фоновом режиме, так что вам не придется заботиться о многих вещах. Например, метод isSecure() проверяет три различных значения в PHP, которые указывают, что пользователь подключается по защищенному протоколу (https).

Symfony также предоставляет класс Response: простое РHP-представление HTTP-ответа. Это позволяет вашему приложению использовать объектно-ориентированный интерфейс для конструирования ответа, который нужно вернуть клиенту:

<?php

use Symfony\Component\HttpFoundation\Response;
$response = new Response();

$response->setContent('<html><body><h1>Hello world!</h1></body></html>');
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/html');

// prints the HTTP headers followed by the content
$response->send();

Если бы Symfony ничего вам не предлагала, вы всегда должны были бы иметь набор инструментов для того чтобы можно было просто и быстро получить доступ к информации из запроса и объектно-ориентированный интерфейс для создания ответа. Даже если вы освоите более мощные возможности в Symfony, всегда держите в голове, что цель вашего приложения всегда заключается в том, чтобы интерпретировать запрос и создать соответствующий ответ, основываясь на логике вашего приложения

Tip

Классы Request и Response являются частью самостоятельного компонента HttpFoundation. Этот компонент может быть использован независимо от Symfony и он также предоставляет классы для работы с сессиями и загрузки файлов.

Путешествие от Запроса до Ответа

Как и HTTP-протокол, объекты Request и Response достаточно просты. Самая сложная часть создания приложения заключается в написании процессов, которые происходят между получением запроса и отправкой ответа. Другими словами, реальная работа заключается в написании кода, который интерпретирует информацию запроса и создает ответ (логика приложения).

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

Symfony создана специально для решения этих проблем, значит, вам не придется их решать.

Фронт-контроллер

Традиционно приложения создавались таким образом, чтобы каждая “страница” имела свой собственный файл:

index.php
contact.php
blog.php

При таком подходе имеется целый ряд проблем, включая жёсткие URLы (что если вам потребуется изменить blog.php на news.php и при этом сохранить все ваши ссылки?), а также необходимость вручную включать в каждый файл кучу файлов, включающих безопасность, работу с базами данных.

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

/index.php выполняет index.php
/index.php/contact выполняет index.php
/index.php/blog выполняет index.php

Tip

С использованием модуля mod_rewrite для Apache (или эквивалента для других web-серверов) URLы легко очистить от упоминания фронт-контроллера, т.е. останется лишь /, /contact и /blog.

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

Будьте организованы

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

<?php
// index.php

$request = Request::createFromGlobals();
$path = $request->getPathInfo(); // запрошенный URL

if (in_array($path, array('', '/')) {
    $response = new Response('Welcome to the homepage.');
} elseif ($path == '/contact') {
    $response = new Response('Contact us');
} else {
    $response = new Response('Page not found.', 404);
}
$response->send();

Решить же эту проблему достаточно сложно. К счастью, Symfony создана именно для этого.

Как устроено Symfony приложение

Когда вы даёте возможность Symfony обрабатывать запросы, жизнь становится много проще. Symfony следует простому шаблону при обработке каждого запроса:

Symfony2 request flow

Входящие запросы интерпретируются маршрутизатором и передаются в функцию-контроллер, которая возвращает объект Response.

Каждая “страница” вашего сайта должна быть определена в конфигурации маршрутизатора, чтобы распределять различные URL по различным PHP-функциям. Обязанность каждой такой функции, называемой controller, используя информацию из запроса - а также используя прочий инструментарий, доступный в Symfony, создать и вернуть объект Response. Другими словами, контроллер содержит ваш код: именно там вы должны превратить запрос в ответ.

Это не сложно! Давайте-ка взглянем:

  • Каждый запрос обрабатывается фронт-контроллером;
  • Система маршрутизации определяет, какую именно PHP-функцию необходимо выполнить, основываясь на информации из запроса и конфигурации маршрутизатора, которую вы создали;
  • Вызывается необходимая функция, в которой написанный вами код создаёт и возвращает соответствующий логике приложения объект Response.

Symfony Request в действии

Не закапываясь глубоко в детали, давайте посмотрим на этот процесс в действии. Предположим, вы хотите добавить страницу /contact к вашему Symfony приложению. Во-первых, надо добавить конфигурацию маршрутизатора для /contact URI:

contact:
    pattern:  /contact
    defaults: { _controller: AcmeDemoBundle:Main:contact }

Note

Этот пример использует YAML для того чтобы определить конфигурацию маршрутизатора. Конфигурацию можно также задавать и в других форматах - таких как XML или PHP.

Когда кто-либо посещает страницу /contact, URI совпадает с маршрутом и указанный нами ранее контроллер выполняется. Как вы узнаете в из главы Маршрутизация, строка AcmeDemoBundle:Main:contact это короткая форма записи, которая указывает на особый метод contactAction, определённый в классе MainController:

<?php

class MainController
{
    public function contactAction()
    {
        return new Response('<h1>Contact us!</h1>');
    }
}

В этом очень простом примере, контроллер создает объект Response, содержащий лишь простенький HTML-код “<h1>Contact us!</h1>”. В главе Контроллер, вы узнаете, как контроллер может отображать шаблоны, позволяя “представлению” существовать раздельно от кода в файлах шаблонов. Это дает возможность сосредоточиться в контроллере на работе с базами данных, обработке отправленных пользователем данных или отправке email сообщений.

Symfony2: Создавайте приложение, а не инструменты.

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

Хорошие новости заключаются в том, что эти проблемы не уникальны. Symfony предоставляет Фреймворк, полный инструментов, которые позволят вам создать ваше собственное приложение, а не ваши инструменты. При помощи Symfony2 вы использовать Фреймворк целиком или же только его часть.

Автономные библиотеки: Компоненты Symfony2

Что же собой представляет Symfony2? Прежде всего, Symfony2 - это коллекция более чем 20 независимых библиотек, которые могут быть использованы в любом PHP-проекте. Эти библиотеки, называемые Symfony2 Components, содержат полезные методы практически на любой случай жизни, не зависимо от того как именно ваш проект разрабатывается. Вот некоторые из них:

  • HttpFoundation - Содержит классы Request и Response, а также классы для работы с сессиями и загрузкой файлов;
  • Routing - мощная система маршрутизации, которая позволяет вам ставить в соответствие некоторому URI (например /contact) информацию о том, как этот запрос должен быть обработан (например вызвать метод contactAction());
  • Form - многофункциональный и гибкий фреймворк для создания форм обработки их сабмита;
  • Validator - система, предназначенная для создания правил для данных и последующей валидации - соответствуют ли данные, отправленные пользователями этим правилам;
  • ClassLoader - библиотека, позволяющая использовать PHP-классы без использования явного require для файлов, включающих требуемые классы.
  • Templating - тулкит для рендеринга шаблонов, поддерживает наследование шаблонов (например, декорирование шаблонов при помощи родительского шаблона aka layout), а также прочие типичные для шаблонов операции (escaping, условия, циклы и т.д.);
  • Security мощная библиотека для обеспечения всех типов безопасности внутри приложения;
  • Translation - Фреймворк для поддержки переводов в вашем приложении.

Каждый из этих компонентов независим и может быть использован в любом PHP-проекте, не зависимо от Symfony2.

Комплексное решение: Symfony2 Framework

Ну так что же это такое - Symfony2 Framework? Symfony2 Framework это PHP библиотека, которая решает 2 различных задачи:

  1. Предоставляет набор отобранных компонент (Symfony2 Components) и сторонних библиотек (например Swiftmailer для отправки почты);
  2. Предоставляет возможности по конфигурированию всего этого добра и “клей”, который скрепляет все библиотеки в единое целое.

Цель фреймворка - интеграция независимых инструментов и обеспечение их совместной работы. Сам фреймворк представляет собой Symfony Bundle (плагин), который можно конфигурировать или даже заменить.

Symfony2 предоставляет замечательный набор инструментов для быстрой разработки web-приложений, ничего не навязывающий непосредственно вашему приложению. Разработчик может быстро приступить к разработке, используя дистрибутив Symfony2, который предоставляет скелетон с типовыми настройками. А для пытливых умов... у неба нет потолка! )