in Профессиональное

Symfony2 book: Основы Symfony2 и HTTP / Symfony2 and HTTP Fundamentals

Обновление от Августа 2016: перевод документации Symfony 2 на русский опубликован в виде бесплатной книги на сайте Leanpub. Узнать подробности.

Данный перевод главы Symfony2 and HTTP Fundamentals публикуется с разрешения автора:

Канат Гайлимов
Web-разработчик, блогер
http://gailimov.info

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

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

HTTP это просто

HTTP (для гиков Hypertext Transfer Protocol) – текстовый язык, позволяющий двум машинам общаться между собой. Вот и все! Например, когда мы проверяем последний xkcd комикс, осуществляется следующая (приблизительно) беседа:

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

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

Шаг1: клиент посылает запрос

Каждое общение в сети начинается с запроса. Запрос – текстовое сообщение, созданное клиентом (например браузер, приложение iPhone и т.д.) в специальном формате, известном как HTTP. Клиент посылает запрос серверу, и затем ожидает ответа.

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

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

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

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

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

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

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

DELETE /blog/15 HTTP/1.1

Примечание.

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

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

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

Как только сервер получил запрос, он знает, какие именно ресурсы требуются клиенту (посредством URI) и что клиент хочет делать с ресурсом (посредством метода). Например, в случае с запросом GET, сервер подготавливает ресурс и возвращает его в виде HTTP-ответа. Рассмотрим ответ от веб-сервера xkcd:

В переводе на 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). Код статуса сообщает общий результат запроса обратно клиенту. Был ли запрос успешен? Были ли ошибки? Существуют другие коды статуса, которые указывают на успех, ошибку или то, что хочет клиент (например редирект на другую страницу). Полный список можно найти в Википедии.

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

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

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

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

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

Symfony спроектирован так, чтобы соответствовать этой действительности.

Примечение.

Чтобы узнать больше о спецификации HTTP, прочтите оригинальную HTTP 1.1 RFC или HTTP Bis статьи, которые отлично разъясняют исходную спецификацию. Великолепным инструментом для проверки заголовков запроса и ответа в браузере, является расширение для Firefox Live HTTP Headers.

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

Итак, как вы можете взаимодействовать с “запросом” и создавать “ответ” используя PHP? В действительности PHP абстрагирует вас от этого процесса:

&lt;?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-запросом и ответом более простым путем. Класс Request – простое объектно-ориентированное представление сообщения HTTP-запроса. С ним, в ваших руках имеется вся информация запроса:

use Symfony\Component\HttpFoundation\Request;
 
$request = Request::createFromGlobals();
 
// the URI being requested (e.g. /about) minus any query parameters
$request-&gt;getPathInfo();
 
// retrieve GET and POST variables respectively
$request-&gt;query-&gt;get('foo');
$request-&gt;request-&gt;get('bar');
 
// retrieves an instance of UploadedFile identified by foo
$request-&gt;files-&gt;get('foo');
 
$request-&gt;getMethod();          // GET, POST, PUT, DELETE, HEAD
$request-&gt;getLanguages();       // an array of languages the client accepts

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

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

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, имейте ввиду, что цель вашего приложения интерпретировать запрос и создавать соотвествующий ответ, основанный на логике вашего приложения.

Примечание:

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

Путешествие от запроса к ответу

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

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

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

Front Contoller

Традиционно, приложения строятся так, что каждой страницей сайта, является его физический файл.

  • index.php
  • contact.php
  • blog.php

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

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

/index.php executes index.php
/index.php/contact executes index.php
/index.php/blog executes index.php

Примечание:

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

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

Оставайтесь организованным

Но как вы узнаете внутри вашего front controller’а, какая страница должна быть отрендерена и выведена? Так или иначе, вы должны проверить входящий URI и выполнить различные части вашего приложения, в зависимости от его значения. Это можно сделать ужасно быстро:

// index.php
 
$request = Request::createFromGlobals();
$path = $request-&gt;getPathInfo(); // the URL being requested
 
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-&gt;send();

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

Выполнение приложения Symfony

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

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

Каждая страница вашего сайта определяется в файле конфигурации роутинга, который направляет различные URL’ы различными PHP функциям. Работой каждой функции вызываемой контроллером, является использование информации из запроса, наряду с другими инструментами Symfony делающими доступными создание и возвращение объекта Response. Другими словами, контроллер, это то, где проходит ваш код: место, где вы интерпретируете запрос и создаете ответ.

Это так просто! Давайте рассмотрим:

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

Symfony Request в действии

Давайте рассмотрим этот процесс в действии без погружения в детали. Пускай вы хотите добавить в ваше Symfony приложение страницу /contact. Сперва начнем с добавления записи в ваш конфигурационный файл роутинга.

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

Примечание:

В этом примере, для объявления конфигурации роутинга используется YAML

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

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

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

Symfony2: стройте ваши приложения, а не инструменты

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

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

Автономные инструменты: Компоненты Symfony2

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

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

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

Итог: Symfony2 Framework

Итак, что же такое фреймворк Symfony2? Фреймворк Symfony2 это PHP библиотека, выполняющая две основные задачи:

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

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

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

Write a Comment

Comment

*

        • Кстати миграция оказалась проще чем я думал ) Хотя первый раз статью невнимательно прочитал и долго мучался с required полями (думал что эти данные получаются из метаданных валидации). Но когда разобрался, дело пошло значительно веселее )

  1. может не в тему, но хожу по кругу с вопросом:
    Каким образом в KnpMenuBundle при формировании меню вот так

    public function __construct(Request $request, Router $router)
    {
    …..
    $this->addChild(‘menu’, $router->generate(‘menu’))->setLabel(‘Home’);

    $this->addChild(‘news’);
    $this[‘news’]->setUri($router->generate(‘news’))->setLabel(‘News’);
    $this[‘news’]->addChild(‘latest’, $router->generate(‘news’, array(‘name’ => ‘latest’)))->setLabel(‘Latest’);
    $this[‘news’]->addChild(‘popular’, $router->generate(‘news’, array(‘name’ => ‘popular’)))->setLabel(‘Popular’);
    …..
    }

    получить доступ к контейнеру, чтобы достать из него translator
    по подобию как в контроллерах:

    $tr = $this->get(‘translator’);
    … ->setLabel($tr->trans(‘Popular’));

    ?? ЗАРАНЕЕ БЛАГОДАРЕН ))

    • К сожалению не использовал этот пакет. Кстати не лучше напрямую на гитхабе разработчику писать? ) Он скорее поможет с особо непонятными случаями.