Компонент HttpFoundation¶
Компонент HttpFoundation определяет объектно-ориентированный слой спецификации HTTP.
В PHP, запрос представлен некоторыми глобальными переменными ($_GET
,
$_POST
, $_FILES
, $_COOKIE
, $_SESSION
, ...), а ответ генерируется
некоторыми функциям (echo
, header()
, setcookie()
, ...).
Компонент Symfony HttpFoundation заменяет эти глобальные переменные и функции объектно-ориентироанным слоем.
Установка¶
1 | $ composer require symfony/http-kernel
|
Также вы можете клонировать репозиторий https://github.com/symfony/http-kernel.
Note
If you install this component outside of a Symfony application, you must
require the vendor/autoload.php
file in your code to enable the class
autoloading mechanism provided by Composer. Read
this article for more details.
Запрос¶
Наиболее распространённый способ создать запрос - основать его на текущих глобальных
переменных PHP c createFromGlobals()
:
1 2 3 | use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
|
Что почти эквивалентно более многословному, но также более гибкому,
вызову __construct()
:
1 2 3 4 5 6 7 8 | $request = new Request(
$_GET,
$_POST,
array(),
$_COOKIE,
$_FILES,
$_SERVER
);
|
Оценка данных запроса¶
Объект Запроса содержит информацию о запросе клиента. К этой информации можно получить доступ через несколько публичных свойств:
request
: эквивалент$_POST
;query
: эквивалент$_GET
($request->query->get('name')
);cookies
: эквивалент$_COOKIE
;attributes
: эквивалента нет - используется вашим приложением для хранения других данных (см. below);files
: эквивалент$_FILES
;server
: эквивалент$_SERVER
;headers
: наиболее эквивалентно субнабору$_SERVER
($request->headers->get('User-Agent')
).
Каждое свойство - это экземпляр ParameterBag
(или его подкласс), с классом содержания данных:
request
:ParameterBag
;query
:ParameterBag
;cookies
:ParameterBag
;attributes
:ParameterBag
;files
:FileBag
;server
:ServerBag
;headers
:HeaderBag
.
Все экземпляры ParameterBag
имеют методы
для извлечения и обновления данных:
all()
- Возвращает параметры.
keys()
- Возвращает ключи параметра.
replace()
- Заменяет текущие параметры новым набором.
add()
- Добавляет параметры.
get()
- Возвращает параметр по имени.
set()
- Устаналивает параметр по имени.
has()
- Возвращает
true
, если параметр определён. remove()
- Удаляет параметр.
Экземпляр ParameterBag
также имеет
некоторые методы для фильтрации значений ввода:
getAlpha()
- Возвращает алфавитные символы значения параметра;
getAlnum()
- Возвращает алфавитные символы и цифры значения параметра;
getBoolean()
- Возвращает значение параметра преобразованное в булево значение;
getDigits()
- Возвращает цифры значения параметра;
getInt()
- Возвращает значение параметра, преобразованное в число;
filter()
- Фильтрует параметр, используя функцию
filter_var
.
Все геттеры имеют до двух аргументов: первый - это имя параметра, а второй - значение по умолчанию, которое нужно вернуть, если параметр не существует:
1 2 3 4 5 6 7 8 9 10 | // строка запроса - '?foo=bar'
$request->query->get('foo');
// возвращает 'bar'
$request->query->get('bar');
// возвращает null
$request->query->get('bar', 'baz');
// возвращает 'baz'
|
Когда PHP импортирует запрос на запрос, он обрабатывает параметры запроса
как foo[bar]=baz
, особенным способом, создавая массив. Так что выможете
получить параметр foo
и вы получит обратно массив с элементом bar
:
1 2 3 4 5 6 7 8 9 10 | // строка запроса - '?foo[bar]=baz'
$request->query->get('foo');
// возвращает массив ('bar' => 'baz')
$request->query->get('foo[bar]');
// возвращает null
$request->query->get('foo')['bar'];
// возвращает 'baz'
|
Благодаря публичному свойству attributes
, вы можете хранить дополнительные
данные в запросе, который также является экземпляром ParameterBag
.
Это в основном используется для присоединения информации, которая принадлежит
Запросу и должна быть доступна из множества точек вашего приложения.
Наконец, сырые данные, отправленные в теле запроса, могут быть доступны,
используя getContent()
:
1 | $content = $request->getContent();
|
Например, это может быть полезно для обработки JSON строки, отправленной приложению удалённым сервисом, использующим метод HTTP POST.
Идентификация запроса¶
В вашем приложении,вам нужен способ идентифицировать запрос; в большинстве
случаев, это делается через "путь информации" запроса, который доступен через
метод getPathInfo()
:
1 2 3 | // для запроса к http://example.com/blog/index.php/post/hello-world
// путь информации - "/post/hello-world"
$request->getPathInfo();
|
Симуляция запроса¶
Вместо создания запроса, основанного на глобальных PHP, вы также можете симулировать запрос:
1 2 3 4 5 | $request = Request::create(
'/hello-world',
'GET',
array('name' => 'Fabien')
);
|
Метод create()
создаёт
запрос, основанный на URI, методе и некоторых параметрах (параметры запроса
или запроса (query), в зависимости от HTTP метода); и конечно, вы также можете
переопределить все другие переменные (по умолчанию, Symfony создаёт разумные
значения по умолчанию для всех глобальных переменных PHP).
Основываясь на таком запросе, вы можете переопределить глобальные переменные
PHP через overrideGlobals()
:
1 | $request->overrideGlobals();
|
Tip
Вы также можете дублировать существующий запрос через
duplicate()
или
изменить кучу параметров единственным вызовом к
initialize()
.
Доступ к сессии¶
Если у вас есть сессия, присоединённая к запросу, вы можете получить к ней
доступ через метод getSession()
;
метод hasPreviousSession()
сообщает вам, содержит ли запрос сессию, которая была запущена в одном из
предыдущих запросов.
New in version 4.1: Использование getSession()()
,
при неустановленной сессии стало устаревшим в Symfony 4.1. Это будет выбрасывать
исключение в Symfony 5.0, когда сессия является null
. Сначала проверяйте существование
сессии вызывая hasSession()()
.
Обработка заголовков HTTP¶
New in version 4.1: Класс HeaderUtils
появился в Symfony 4.1.
Обработка заголовков HTTP - это нетривиальная задача из-за экранирования и обработки
пробельных символов внитри заголовков. Symfony предоставляет класс
HeaderUtils
, который позволяет абстрагировать
эту сложность и определяет несколько методов для наиболее частых задач:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | use Symfony\Component\HttpFoundation\HeaderUtils;
// Разбивает заголовок HTTP с помощью одного или нескольких разделителей
HeaderUtils::split('da, en-gb;q=0.8', ',;')
// => array(array('da'), array('en-gb'), array('q', '0.8'))
// Объединяет массив массивов в один ассоциативный массив
HeaderUtils::combine(array(array('foo', 'abc'), array('bar')))
// => array('foo' => 'abc', 'bar' => true)
// Объединяет ассоциативный массив в строку для использования в заголовке HTTP
HeaderUtils::toString(array('foo' => 'abc', 'bar' => true, 'baz' => 'a b c'), ',')
// => 'foo=abc, bar, baz="a b c"'
// Экранирует строку, если необходимо
HeaderUtils::quote('foo "bar"')
// => 'foo \"bar\"'
// Деэкранирует строку
HeaderUtils::unquote('foo \"bar\"')
// => 'foo "bar"'
|
Доступ к данным заголовков Accept-*
¶
Вы можете с лёгкостью получить доступ к базовым данным, извлечённым из
заголовков Accept-*
, используя следующие методы:
getAcceptableContentTypes()
- Возвращает список приемлемых типов содержания, в порядке снижения качества.
getAcceptableFormats()
- Возвращает список приемлемых клиентских форматов, ассоциированных с запросом.
Заметьте, что
getAcceptableFormats()
будет использовать данные из
getAcceptableContentTypes()
и вернёт примлимые клиентские форматы:
1 2 3 4 5 | $request->getAcceptableContentTypes();
// returns ['text/html', 'application/xhtml+xml', 'application/xml', '*/*']
$request->getAcceptableFormats();
// returns ['html', 'xml']
|
getLanguages()
- Возвращает список приемлемых языков, в порядке снижения качества.
getCharsets()
- Возвращает список приемлемых наборов символов, в порядке снижения качества.
getEncodings()
- Возвращает список приемлемых кодировок, в порядке снижения качества.
Если вам нужно получить полный доступ к проанализированным данным из Accept
,
Accept-Language
, Accept-Charset
или Accept-Encoding
, вы можете использовать
класс утилиты AcceptHeader
:
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\HttpFoundation\AcceptHeader;
$acceptHeader = AcceptHeader::fromString($request->headers->get('Accept'));
if ($acceptHeader->has('text/html')) {
$item = $acceptHeader->get('text/html');
$charset = $item->getAttribute('charset', 'utf-8');
$quality = $item->getQuality();
}
// Приемлемые объекты заголовков сортируются в порядке снижения качества
$acceptHeaders = AcceptHeader::fromString($request->headers->get('Accept'))
->all();
|
Также поддерживаются значения по умолчанию, которые могут быть опционально включены
в заголовки Accept-*
:
1 2 3 4 5 | $acceptHeader = 'text/plain;q=0.5, text/html, text/*;q=0.8, */*;q=0.3';
$accept = AcceptHeader::fromString($acceptHeader);
$quality = $accept->get('text/xml')->getQuality(); // $quality = 0.8
$quality = $accept->get('application/xml')->getQuality(); // $quality = 0.3
|
New in version 4.1: Поддержка значений по умолчанию в заголовках Accept-*
была представлена в
Symfony 4.1.
Доступ к другим данным¶
Класс Request
имеет множество других методов, которые вы можете
использовать для доступа к информации запроса. Посмотрите на
API Запроса
,
чтобы узнать больше о них.
Переопределение запроса¶
Класс Request
не должен быть переопределён, так как это объект данных,
который представляет HTTP сообщение. Но при перемещении из системы наследования,
добавление методов или изменение некоторого поведения по умолчанию может
помочь. В этом случае, зарегистрируйте вызываемое PHP, которое может создать
экземпляр вашего класса Request
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | use App\Http\SpecialRequest;
use Symfony\Component\HttpFoundation\Request;
Request::setFactory(function (
array $query = array(),
array $request = array(),
array $attributes = array(),
array $cookies = array(),
array $files = array(),
array $server = array(),
$content = null
) {
return new SpecialRequest(
$query,
$request,
$attributes,
$cookies,
$files,
$server,
$content
);
});
$request = Request::createFromGlobals();
|
Ответ¶
Объект Response
содержит всю
информацию, которую нужно отпрвить обратно клиенту из заданного запроса.
Конструктор имеет до трёх аргументов: содержимое ответа, статус-код и массив
HTTP-заголовков:
1 2 3 4 5 6 7 | use Symfony\Component\HttpFoundation\Response;
$response = new Response(
'Content',
Response::HTTP_OK,
array('content-type' => 'text/html')
);
|
Эту информацию можно также изменять после создания объекта Ответ:
1 2 3 4 5 6 | $response->setContent('Hello World');
// публичный атрибут заголовка - ResponseHeaderBag
$response->headers->set('Content-Type', 'text/plain');
$response->setStatusCode(Response::HTTP_NOT_FOUND);
|
При установке Content-Type
Ответа, вы можете установить набор символов,
но лучше устанавливать его через метод
setCharset()
:
1 | $response->setCharset('ISO-8859-1');
|
Отметьте, что по умолчанию, Symfony предполагает, что ваши Ответы зашифрованы с помощью UTF-8.
Отправка ответа¶
До отправки Ответа вы можете по опционально вызвать метод
prepare()
, чтобы исправить
любую несовместимость со спецификацией HTTP (например, неправильный заголовок
Content-Type
):
1 | $response->prepare($request);
|
Отправка ответа клиенту в таком случае заключается в простом вызове
send()
:
1 | $response->send();
|
Установка Cookie¶
Cookie ответа могут быть изменены через публичный атрибут headers
:
1 2 3 | use Symfony\Component\HttpFoundation\Cookie;
$response->headers->setCookie(new Cookie('foo', 'bar'));
|
Метод setCookie()
берёт экземпляр Cookie
в качестве
аргумента.
Вы можете очистить cookie методом
clearCookie()
.
Отметьте, что вы можете создать объект Cookie
из сырого значения заголовка, используя fromString()
.
Управление HTTP-кешем¶
Класс Response
имеет богатый набор
методов для управления HTTP-заголовками, относящимися к кешу:
setPublic()
;setPrivate()
;expire()
;setExpires()
;setMaxAge()
;setSharedMaxAge()
;setTtl()
;setClientTtl()
;setLastModified()
;setEtag()
;setVary()
;
Note
Методы setExpires()
,
setLastModified()
и
setDate()
рринимают
любой объект, релизующий \DateTimeInterface
, включая неизменные объекты
дат.
Метод setCache()
может
быть использован дляустановки наиболее используемой кещ-информации в одном
вызове метода:
1 2 3 4 5 6 7 8 | $response->setCache(array(
'etag' => 'abcdef',
'last_modified' => new \DateTime(),
'max_age' => 600,
's_maxage' => 600,
'private' => false,
'public' => true,
));
|
Чтобы проверить, соответствуют ли валидаторы Ответа (ETag
, Last-Modified
)
условному значению, указанному в Запросе клиента, используйте метод
isNotModified()
:
1 2 3 | if ($response->isNotModified($request)) {
$response->send();
}
|
Если Ответ не был изменён, он устанавливает статус-код 304 и удаляет настоящее содержимое ответа.
Перенаправление пользователя¶
Чтобы перенаправить клиента по другому URL, вы можете использовать класс
RedirectResponse
:
1 2 3 | use Symfony\Component\HttpFoundation\RedirectResponse;
$response = new RedirectResponse('http://example.com/');
|
Потоковая передача ответа¶
Класс StreamedResponse
позволяет
вам создавать поток с Ответом для клиента. Содержимое ответа представляется
PHP вызываемым, а не строкой:
1 2 3 4 5 6 7 8 9 10 11 | use Symfony\Component\HttpFoundation\StreamedResponse;
$response = new StreamedResponse();
$response->setCallback(function () {
var_dump('Hello World');
flush();
sleep(2);
var_dump('Hello World');
flush();
});
$response->send();
|
Note
Функция flush()
не сбрасывает буферизацию. Если ob_start()
был вызван
до этого, или включена опция output_buffering
php.ini
, то вы должны
вызывать ob_flush()
до flush()
.
Кроме того, PHP не единственный слой, буферизирующий вывод. Ваш веб-сервер может также использовать буфер, в зависимости от конфигурации. Некоторые серверы, вроде Nginx, позволяют вам отключать буферизацию на уровне конфигурации или добавлять спеицальный заголовок HTTP в ответе:
1 2 | // отключает буферизацию FastCGI в Nginx только для этого ответа
$response->headers->set('X-Accel-Buffering', 'no')
|
Подача файлов¶
При отправке файла вы должны добавлять заголовок Content-Disposition
к
вашему ответу. И хотя создание этого заголовка для базовых загрузок файлов
- это просто, использование не ASCII имён файлов требует больших усилий.
makeDisposition()
абстрагирует тяжелую работу, скрывающуюся за простым API:
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
$fileContent = ...; // the generated file content
$response = new Response($fileContent);
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'foo.pdf'
);
$response->headers->set('Content-Disposition', $disposition);
|
Как вариант, если вы подаёте статичный файл, вы можете использовать
BinaryFileResponse
:
1 2 3 4 | use Symfony\Component\HttpFoundation\BinaryFileResponse;
$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);
|
BinaryFileResponse
автоматически обработает заголовки Range
и
If-Range
из запроса. Он также поддерживает``X-Sendfile`` (см. Nginx
и Apache). Чтобы воспользоваться этим, вам нужно определить, стоит ли
доверять заголовку X-Sendfile-Type
и вызвать
trustXSendfileTypeHeader()
,
если стоит:
1 | BinaryFileResponse::trustXSendfileTypeHeader();
|
С BinaryFileResponse
вы можете продолжать устанавливать Content-Type
отправленного файла, или изменять его Content-Disposition
:
1 2 3 4 5 6 | // ...
$response->headers->set('Content-Type', 'text/plain');
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'filename.txt'
);
|
Файл можно удалить после отправки запроса методом
deleteFileAfterSend()
.
Пожалуйста, заметьте, что это не работает, если установлен заголовок X-Sendfile
.
Если размер поданого файла неизвестен (например, потому что он создаётся на лету, или
потому что в нём зарегистрирован фильтр потока PHP и т.д.), то вы можете передать
экземпляр Stream
в BinaryFileResponse
. Это отключит обработку Range
и
Content-Length
, переключившись на механизм передачи данных chunked encoding:
1 2 3 4 5 | use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Stream;
$stream = new Stream('path/to/stream');
$response = new BinaryFileResponse($stream);
|
Note
Если вы только создали файл во время этого же запроса, файл может быть
отправлен без содержания. Это может произойти в связи со статистикой кешированного
файла, которая возвращает ноль в качестве размера файла. Чтобы исправить эту
проблему, вызовите clearstatcache(true, $file)
с путём к бинарному файлу.
Создание JSON Ответа¶
Любой тип ответа может быть создан через класс
Response
, путём установки
правильного содержания и заголовков. JSON ответ может выглядеть так:
1 2 3 4 5 6 7 | use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->setContent(json_encode(array(
'data' => 123,
)));
$response->headers->set('Content-Type', 'application/json');
|
Также существует полезный класс JsonResponse
,
который может сделать это ешё проще:
1 2 3 4 5 6 7 8 9 10 11 12 | use Symfony\Component\HttpFoundation\JsonResponse;
// если вы знаете, какие данные отправлять при создании запроса
$response = new JsonResponse(array('data' => 123));
// если вы не знаете, какие данные отправлять при создании запроса
$response = new JsonResponse();
// ...
$response->setData(array('data' => 123));
// если данные дляотправки уже зашифрованы в JSON
$response = JsonResponse::fromJsonString('{ "data": 123 }');
|
Класс JsonResponse
устанавливает заголовок Content-Type
в
application/json
и шифрует ваши данные в JSON при необходимости.
Caution
Чтобы избежать XSSI перехвата JSON, вам стоит передать ассоциативный массив
в JsonResponse
в качестве крайнего массива, а не индексированного массива,
чтобы финальный результат был объектом (например, {"object": "not inside an array"}
)
вместо массива (например, [{"object": "inside an array"}]
). Прочтите справочник OWASP,
чтобы узнать больше.
Только методы, отвечающие на запросы GET уязвимы к XSSI 'перехвату JSON'. Методы, отвечающие на запросы POST остаются неуязвимыми.
Обратный вызов JSONP¶
Есди вы используете JSONP, вы можете установить функцию обратного вызова, в которую должны быть переданы данные:
1 | $response->setCallback('handleResponse');
|
В этом случае, заголовок Content-Type
будет text/javascript
, а содержание
ответа будет выглядеть так:
1 | handleResponse({'data': 123});
|
Сессия¶
Информация сессии хранится в отдельном документе: Session Management.
Узнайте больше¶
- Configuring Sessions and Save Handlers
- Конфигурация сессий и обработчики сохранений
- Integrating with Legacy Sessions
- Интеграция с унаследованными сессиями
- Testing with Sessions
- Тестирование с сессиями
- Session Management
- Управление сессиями
- Controller
- Extending Action Argument Resolving
- Расширения разрешения аргумента действия
- How to Customize Error Pages
- Как настроить страницы ошибок
- How to Forward Requests to another Controller
- Как пересылать запросы другому контроллеру
- How to Define Controllers as Services
- Как определять контроллеры как сервисы
- How to Create a SOAP Web Service in a Symfony Controller
- Как создать SOAP веб-сервис в контроллере Symfony
- How to Upload Files
- Как загружать файлы
- Избегание запуска сессий для анонимных пользователей
- Store Sessions in a Database
- Лимит записи метаданных сесии
- Making the Locale "Sticky" during a User's Session
- Как сделать локаль "липкой" во время сессии пользователя
- Bridge a legacy Application with Symfony Sessions
- Свяжите унаследованные приложение с сессиями Symfony
- Session Proxy Examples
- Примеры прокси сессии
- Конфигурация каталога, где сохраняются файлы сессии
- Cache Invalidation
- Инвалидация кеша
- Varying the Response for HTTP Cache
- Варьирование ответа для HTTP-кеша
- Working with Edge Side Includes
- Работа с включениями крайней стороны
- HTTP Cache Expiration
- Окочание действия HTTP-кеша
- Кеширование страниц, содержащих CSRF-защищённые формы
- Working with Server Side Includes
- HTTP Cache Validation
- Валидация HTTP-кеша
- How to Use Varnish to Speed up my Website
- Как использовать Varnish для ускорения моего сайта
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.