Вольный перевод официальной документации.
Компонент HttpFoundation, позволяет взаимодействовать с HTTP через объектно-ориентированный подход.
В PHP, запрос представлен, как совокупность глобальных переменных ($_GET, $_POST, $_FILES, $_COOKIE, $_SESSION, …), а ответ формируется с помощью некоторых функций (echo, header(), setcookie(), …)
Компонент Symfony – HttpFoundation – заменяет упомянутые глобальные переменные и функции, предоставляя объектно-ориентированный слой.
Установка
Вы можете его установить двумя способами:
- Через Composer (symfony/http-foundation – проект packagist.org)
- Скачать с официального репозитория Git (http://github.com/symfony/http-foundation)
Затем, подключить через require автолоадер – require ‘vendor/autoload.php’, без него, ваше приложение не сможет найти необходимые классы компонента.
Объект Request
Наиболее часто применяемый способ создать Request из глобальных переменных – использовать метод createFromGlobals():
use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals();
Который почти в точности соответствует более громоздкому, зато гибкому – через конструктор:
$request = new Request( $_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER );
Доступ к данным Request.
Объект Request содержит информацию о запросе клиента. Ее можно получить через определенные публичные (public) свойства:
- request: содержит информацию из $_POST;
- query: аналог $_GET ($request->query->get(‘name’));
- cookies: содержит информацию из $_COOKIE;
- attributes: собственное свойство – используется вашим приложением для хранения данных (см. ниже)
- 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() – приведет значение параметра к типу boolean;
- getDigits – цифры.
- getInt() – приведет к типу integer
- filter() – отфильтрует значение, через встроенную в PHP функцию filter_var
Все геттеры принимают до двух аргументов. Первый это имя параметра, второй – значение по умолчанию, если параметра не существует:
// строка запроса '?foo=bar' $request->query->get('foo'); // возвращается 'bar' $request->query->get('bar'); // null $request->query->get('bar', 'baz'); // 'baz'
Когда импортируется строка запроса вида foo[bar]=baz, она обрабатывается особым образом, в результате чего создается массив. То есть, если запросить параметр foo, вернется ассоциативный массив с элементом bar:
// строка запроса '?foo[bar]=baz' $request->query->get('foo'); // вернется array('bar' => 'baz') $request->query->get('foo[bar]'); // null $request->query->get('foo')['bar']; // 'baz'
Благодаря public свойству attributes, вы можете сохранять дополнительные данные в объект Request. Данное свойство тоже представляет собой экземпляр класса ParameterBag. В основном используется для передачи информации между различными подсистемами вашего проекта.
Наконец, сырые данные, отправленные в теле запроса, могут быть получены с помощью метода getContent():
$content = $request->getContent();
К примеру, может использоваться для извлечения JSON строки, которую отправил вашему приложению удаленный сервер, методом POST.
Идентификация запроса
Вашему приложению необходим способ идентификации полученного запроса. В большинстве случаев это делается через «path info» (информация о пути), которую можно извлечь посредствам метода getPathInfo():
// В запросе http://example.com/blog/index.php/post/hello-world $request->getPathInfo(); // «path info» - "/post/hello-world"
Эмуляция запроса
Вместо того, чтобы создавать объект Request на основе суперглобальных переменных ($_GET, $_POST и т.д.), вы можете сделать следующее:
$request = Request::create( '/hello-world', 'GET', array('name' => 'Fabien') );
Метод create() создает Request с указанным в первом параметре URI, затем HTTP методом и параметрами. И, конечно же, вы можете переопределить остальные переменные. По умолчанию, Symfony эмулирует (заполняет) адекватными значениями все данные, которые присутствуют в суперглобальных переменных при обычном запросе.
Можно переопределить данные, из глобальных переменных PHP, через метод overrideGlobals():
$request->overrideGlobals();
Вы так же можете создать копию запроса через duplicate(), или изменить сразу несколько параметров, вызвав initialize().
Доступ к сессии
Если у вас была стартована сессия, можете получить данные из нее – getSession(). Либо проверить, существует ли сессия (запущенная в ходе предыдущего запроса) – hasPreviousSession().
Доступ к заголовкам Accept-*
Можно легко получить данные, извлеченные из заголовков Accept-*, используя следующие методы:
- getAcceptableContentTypes() – возвращает список поддерживаемых типов (accepted content types), с приоритетом в убывающем порядке
- getLanguages() – вернет список поддерживаемых языков (accepted languages), в убывающем порядке
- getCharsets() – список поддерживаемых кодировок, тоже в убывающем порядке.
- getEncodings() – список поддерживаемых encoders (архиваторов) — gzip, deflate …, c убывающим приоритетом.
При необходимости полного доступа к заголовкам Accept, Accept-Language, Accept-Charset или Accept-Encoding, используйте утилитный класс AcceptHeader:
use Symfony\Component\HttpFoundation\AcceptHeader; $accept = AcceptHeader::fromString($request->headers->get('Accept')); if ($accept->has('text/html')) { $item = $accept->get('text/html'); $charset = $item->getAttribute('charset', 'utf-8'); $quality = $item->getQuality(); } // Данные из заголовков отсортированы в убывающем порядке, по приоритету. $accepts = AcceptHeader::fromString($request->headers->get('Accept')) ->all();
Доступ к остальным данным
Класс Request содержит множество других методов для доступа к информации о запросе. Изучите документацию Request API для более полной картины.
Переопределение Request
Класс Request не желательно переопределять, поскольку его объекты всего лишь представляют HTTP данные. Но при переходе с устаревших проектов, бывает удобно добавить определенные методы или изменить некоторое стандартное поведение. Чтобы этого добиться, зарегистрируйте PHP callable (функцию, метод, — то, что можно вызвать), который будет создавать объект класса Request:
use AppBundle\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 SpecialRequest::create( $query, $request, $attributes, $cookies, $files, $server, $content ); }); $request = Request::createFromGlobals();
Объект Response
Содержит всю необходимую информацию, для отправки ответа пользователю, на его запрос. Конструктор принимает до трех аргументов: контент, код статуса и массив с HTTP заголовками.
use Symfony\Component\HttpFoundation\Response; $response = new Response( 'Content', Response::HTTP_OK, array('content-type' => 'text/html') );
Перечисленные данные можно заменять после того, как объект был создан:
$response->setContent('Hello World'); // headers - свойство public, которое содержит экземпляр ResponseHeaderBag $response->headers->set('Content-Type', 'text/plain'); $response->setStatusCode(Response::HTTP_NOT_FOUND);
Можете также указать кодировку вместе с Content-Type, но лучше это делать через метод setCharset():
$response->setCharset('ISO-8859-1');
Кстати, по умолчанию, Symfony устанавливает кодировку в UTF-8
Отправка ответа (Response)
Перед тем, как отправить ответ, вызов prepare() исправляет любые несовместимые с HTTP установки (например, неверный заголовок Content-Type), хотя данный шаг не обязателен.
$response->prepare($request);
Сама отправка ответа крайне проста:
$response->send();
Установка Cookies
Нужные Cookies устанавливаются через public свойство headers:
use Symfony\Component\HttpFoundation\Cookie; $response->headers->setCookie(new Cookie('foo', 'bar'));
Метод setCookie() принимает в качестве аргумента экземпляр класса Cookie.
Сбросить Cookie позволяет метод clearCookie()
Кстати, есть возможность создать объект Cookie из «сырого» заголовка (raw header), — вызвать fromString(), которая появилась, начиная с Symfony 3.3.
Управление кэшем HTTP.
Класс Response включает богатый набор методов для управления HTTP заголовками, отвечающими за кэш.
- setPublic();
- setPrivate();
- expire();
- setExpires();
- setMaxAge();
- setSharedMaxAge();
- setTtl();
- setClientTtl();
- setLastModified();
- setEtag();
- setVary();
Наиболее часто используется setCache(), для установки одной командой:
$response->setCache(array( 'etag' => 'abcdef', 'last_modified' => new \DateTime(), 'max_age' => 600, 's_maxage' => 600, 'private' => false, 'public' => true, ));
Для валидации Response (ETag, Last-Modified), что он соответствуют условиям запроса клиента, используйте isNotModified():
if ($response->isNotModified($request)) { $response->send(); }
Если ответ не содержит изменений, устанавливается код 304 и удаляется весь контент из объекта.
Перенаправление пользователя (Redirect)
Чтобы перенаправить пользователя на другой URL, используйте класс RedirectResponse:
use Symfony\Component\HttpFoundation\RedirectResponse; $response = new RedirectResponse('http://example.com/');
Отправка ответа потоком (Streaming a Response)
Класс StreamedResponse, позволяет вам отправлять поток обратно клиенту. Контентом является вызываемая функция (PHP callable), вместо строки:
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();
Имейте ввиду, что flush() на очищает буфер. Если была запущена функция ob_start() или в файле php.ini активирован output_buffering, необходимо вызвать ob_flush(), перед flush().
В дополнение следует отметить, что не только PHP, как таковой, может буферизировать вывод, но и ваш веб сервер. Некоторые из них, например Nginx, позволяют отключить буферизацию в конфигурационном файле, либо с помощью специального заголовка в ответе:
// отключает буферизацию FastCGI в Nginx, для текущего ответа $response->headers->set('X-Accel-Buffering', 'no')
Обработка файлов
При отправке файлов, вы должны добавить заголовок Content-Disposition в ответ. Создать данный заголовок для простого скачивания легко. Чего нельзя сказать, если используются имена файлов отличные от ASCII. Метод makeDisposition() выполняет всю сложную работу, предоставляя простой интерфейс:
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; $fileContent = ...; // сгенерированное содержимое файла $response = new Response($fileContent); $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'foo.pdf' ); $response->headers->set('Content-Disposition', $disposition);
В качестве альтернативы, когда отправляются готовые файлы, используйте BinaryFileResponse:
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():
BinaryFileResponse::trustXSendfileTypeHeader();
Вместе с BinaryFileResponse, возможно устанавливать заголовок Content-Type или менять Content-Disposition.
// ... $response->headers->set('Content-Type', 'text/plain'); $response->setContentDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'filename.txt' );
При необходимости, после отправки файла его можно удалить через deleteFileAfterSend(). Обратите внимание, прием не сработает при установленном заголовке X-Sendfile.
Класс Stream был впервые реализован в Symfony 3.3
Когда размер отправляемого файла не известен (например, потому, что он генерируется «на лету», или зарегистрирован в PHP stream filter и т.д.) вы можете передать экземпляр Stream в BinaryFileResponse. Это отключит обработку заголовков Range и Content-Length; переключится в chunked encoding.
use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\File\Stream; $stream = new Stream('path/to/stream'); $response = new BinaryFileResponse($stream);
Если же вы прямо сейчас сгенерировали файл, в ходе текущего запроса, он может отправиться пустым. Такое случается из-за кэширования статуса файла, в результате чего, будет возвращен нулевой размер. Чтобы избежать данной проблемы, вызовите clearstatcache(true, $file), передав путь к файлу.
Генерирование JSON ответа
Любой тип ответа может быть создан через класс Response, посредствам установки соответствующих заголовков. Отправка JSON может выглядеть так:
use Symfony\Component\HttpFoundation\Response; $response = new Response(); $response->setContent(json_encode(array( 'data' => 123, ))); $response->headers->set('Content-Type', 'application/json');
Класс JsonResponse также может быть полезен, он сделает отправку еще проще:
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 }');
Метод fromJsonString() был добавлен в версии Symfony 3.2
Упомянутый класс устанавливает заголовок Content-Type в значение application/json и сериализует ваши данные в JSON, если необходимо.
Во избежание XSSI JSON Hijacking, следует передавать ассоциативный массив, в качестве внешнего, в JsonResponse, а не индексный. Чтобы получился объект, например {«object»: «not inside an array»} вместо [{«object»: «inside an array»}]. Для более подробной информации изучите рекомендации OWASP.
Только методы связанные с GET запросами подвержены XSSI ‘JSON Hijacking’.
JSONP Callback
Если вы используете JSONP, можете установить callback функцию, в которую должны быть переданы данные.
$response->setCallback('handleResponse');
В таком случае, Content-Type будет установлен в значение text/javascript. Пример ответа:
handleResponse({'data': 123});
Сессии
Подробная информация о работе с сессией, описана в отдельной статье – Session Management.
root4root aka admin
[…] ← Предыдущая […]