The HttpFoundation Component
The HttpFoundation component defines an object-oriented layer for the HTTPspecification.
In PHP, the request is represented by some global variables ($_GET
,$_POST
, $_FILES
, $_COOKIE
, $_SESSION
, …) and the response isgenerated by some functions (echo
, header()
, setcookie()
, …).
The Symfony HttpFoundation component replaces these default PHP globalvariables and functions by an object-oriented layer.
Installation
- $ composer require symfony/http-foundation
Note
If you install this component outside of a Symfony application, you mustrequire the vendor/autoload.php
file in your code to enable the classautoloading mechanism provided by Composer. Readthis article for more details.
This article explains how to use the HttpFoundation features as anindependent component in any PHP application. In Symfony applicationseverything is already configured and ready to use. Read the Controllerarticle to learn about how to use these features when creating controllers.
Request
The most common way to create a request is to base it on the current PHP globalvariables withcreateFromGlobals()
:
- use Symfony\Component\HttpFoundation\Request;
- $request = Request::createFromGlobals();
which is almost equivalent to the more verbose, but also more flexible,__construct()
call:
- $request = new Request(
- $_GET,
- $_POST,
- [],
- $_COOKIE,
- $_FILES,
- $_SERVER
- );
Accessing Request Data
A Request object holds information about the client request. This informationcan be accessed via several public properties:
request
: equivalent of$_POST
;query
: equivalent of$_GET
($request->query->get('name')
);cookies
: equivalent of$_COOKIE
;attributes
: no equivalent - used by your app to store other data (see below);files
: equivalent of$_FILES
;server
: equivalent of$_SERVER
;headers
: mostly equivalent to a subset of$_SERVER
($request->headers->get('User-Agent')
).Each property is aParameterBag
instance (or a sub-class of), which is a data holder class:request
:ParameterBag
;query
:ParameterBag
;cookies
:ParameterBag
;attributes
:ParameterBag
;files
:FileBag
;server
:ServerBag
;headers
:HeaderBag
.AllParameterBag
instances havemethods to retrieve and update their data:- Returns the parameters.
keys()
- Returns the parameter keys.
replace()
- Replaces the current parameters by a new set.
add()
- Adds parameters.
get()
- Returns a parameter by name.
set()
- Sets a parameter by name.
has()
- Returns
true
if the parameter is defined. remove()
Removes a parameter.The
ParameterBag
instance alsohas some methods to filter the input values:- Returns the alphabetic characters of the parameter value;
getAlnum()
- Returns the alphabetic characters and digits of the parameter value;
getBoolean()
- Returns the parameter value converted to boolean;
getDigits()
- Returns the digits of the parameter value;
getInt()
- Returns the parameter value converted to integer;
filter()
- Filters the parameter by using the PHP
filter_var
function.All getters take up to two arguments: the first one is the parameter nameand the second one is the default value to return if the parameter does notexist:
- // the query string is '?foo=bar'
- $request->query->get('foo');
- // returns 'bar'
- $request->query->get('bar');
- // returns null
- $request->query->get('bar', 'baz');
- // returns 'baz'
When PHP imports the request query, it handles request parameters likefoo[bar]=baz
in a special way as it creates an array. So you can get thefoo
parameter and you will get back an array with a bar
element:
- // the query string is '?foo[bar]=baz'
- $request->query->get('foo');
- // returns ['bar' => 'baz']
- $request->query->get('foo[bar]');
- // returns null
- $request->query->get('foo')['bar'];
- // returns 'baz'
Thanks to the public attributes
property, you can store additional datain the request, which is also an instance ofParameterBag
. This is mostly usedto attach information that belongs to the Request and that needs to beaccessed from many different points in your application.
Finally, the raw data sent with the request body can be accessed usinggetContent()
:
- $content = $request->getContent();
For instance, this may be useful to process a JSON string sent to theapplication by a remote service using the HTTP POST method.
Identifying a Request
In your application, you need a way to identify a request; most of the time,this is done via the "path info" of the request, which can be accessed via thegetPathInfo()
method:
- // for a request to http://example.com/blog/index.php/post/hello-world
- // the path info is "/post/hello-world"
- $request->getPathInfo();
Simulating a Request
Instead of creating a request based on the PHP globals, you can also simulatea request:
- $request = Request::create(
- '/hello-world',
- 'GET',
- ['name' => 'Fabien']
- );
The create()
methodcreates a request based on a URI, a method and some parameters (thequery parameters or the request ones depending on the HTTP method); and ofcourse, you can also override all other variables as well (by default, Symfonycreates sensible defaults for all the PHP global variables).
Based on such a request, you can override the PHP global variables viaoverrideGlobals()
:
- $request->overrideGlobals();
Tip
You can also duplicate an existing request viaduplicate()
orchange a bunch of parameters with a single call toinitialize()
.
Accessing the Session
If you have a session attached to the request, you can access it via thegetSession()
method;thehasPreviousSession()
method tells you if the request contains a session which was started in one ofthe previous requests.
Processing HTTP Headers
Processing HTTP headers is not a trivial task because of the escaping and whitespace handling of their contents. Symfony provides aHeaderUtils
class that abstractsthis complexity and defines some methods for the most common tasks:
- use Symfony\Component\HttpFoundation\HeaderUtils;
- // Splits an HTTP header by one or more separators
- HeaderUtils::split('da, en-gb;q=0.8', ',;');
- // => [['da'], ['en-gb','q=0.8']]
- // Combines an array of arrays into one associative array
- HeaderUtils::combine([['foo', 'abc'], ['bar']]);
- // => ['foo' => 'abc', 'bar' => true]
- // Joins an associative array into a string for use in an HTTP header
- HeaderUtils::toString(['foo' => 'abc', 'bar' => true, 'baz' => 'a b c'], ',');
- // => 'foo=abc, bar, baz="a b c"'
- // Encodes a string as a quoted string, if necessary
- HeaderUtils::quote('foo "bar"');
- // => '"foo \"bar\""'
- // Decodes a quoted string
- HeaderUtils::unquote('"foo \"bar\""');
- // => 'foo "bar"'
Accessing Accept-* Headers Data
You can access basic data extracted from Accept-*
headersby using the following methods:
getAcceptableContentTypes()
- Returns the list of accepted content types ordered by descending quality.
getLanguages()
- Returns the list of accepted languages ordered by descending quality.
getCharsets()
- Returns the list of accepted charsets ordered by descending quality.
getEncodings()
- Returns the list of accepted encodings ordered by descending quality.If you need to get full access to parsed data from
Accept
,Accept-Language
,Accept-Charset
orAccept-Encoding
, you can useAcceptHeader
utility class:
- 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();
- }
- // Accept header items are sorted by descending quality
- $acceptHeaders = AcceptHeader::fromString($request->headers->get('Accept'))
- ->all();
The default values that can be optionally included in the Accept-*
headersare also supported:
- $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
Accessing other Data
The Request
class has many other methods that you can use to access therequest information. Have a look atthe Request API
for more information about them.
Overriding the Request
The Request
class should not be overridden as it is a data object thatrepresents an HTTP message. But when moving from a legacy system, addingmethods or changing some default behavior might help. In that case, register aPHP callable that is able to create an instance of your Request
class:
- use App\Http\SpecialRequest;
- use Symfony\Component\HttpFoundation\Request;
- Request::setFactory(function (
- array $query = [],
- array $request = [],
- array $attributes = [],
- array $cookies = [],
- array $files = [],
- array $server = [],
- $content = null
- ) {
- return new SpecialRequest(
- $query,
- $request,
- $attributes,
- $cookies,
- $files,
- $server,
- $content
- );
- });
- $request = Request::createFromGlobals();
Response
A Response
object holds all theinformation that needs to be sent back to the client from a given request. Theconstructor takes up to three arguments: the response content, the statuscode, and an array of HTTP headers:
- use Symfony\Component\HttpFoundation\Response;
- $response = new Response(
- 'Content',
- Response::HTTP_OK,
- ['content-type' => 'text/html']
- );
This information can also be manipulated after the Response object creation:
- $response->setContent('Hello World');
- // the headers public attribute is a ResponseHeaderBag
- $response->headers->set('Content-Type', 'text/plain');
- $response->setStatusCode(Response::HTTP_NOT_FOUND);
When setting the Content-Type
of the Response, you can set the charset,but it is better to set it via thesetCharset()
method:
- $response->setCharset('ISO-8859-1');
Note that by default, Symfony assumes that your Responses are encoded inUTF-8.
Sending the Response
Before sending the Response, you can optionally call theprepare()
method to fix anyincompatibility with the HTTP specification (e.g. a wrong Content-Type
header):
- $response->prepare($request);
Sending the response to the client is done by calling the methodsend()
:
- $response->send();
Setting Cookies
The response cookies can be manipulated through the headers
publicattribute:
- use Symfony\Component\HttpFoundation\Cookie;
- $response->headers->setCookie(Cookie::create('foo', 'bar'));
ThesetCookie()
method takes an instance ofCookie
as an argument.
You can clear a cookie via theclearCookie()
method.
Note you can create aCookie
object from a raw headervalue using fromString()
.
Managing the HTTP Cache
The Response
class has a rich setof methods to manipulate the HTTP headers related to the cache:
setPublic()
;setPrivate()
;expire()
;setExpires()
;setMaxAge()
;setSharedMaxAge()
;setTtl()
;setClientTtl()
;setLastModified()
;setEtag()
;setVary()
;
Note
The methods setExpires()
,setLastModified()
andsetDate()
accept anyobject that implements \DateTimeInterface
, including immutable date objects.
The setCache()
methodcan be used to set the most commonly used cache information in one methodcall:
- $response->setCache([
- 'etag' => 'abcdef',
- 'last_modified' => new \DateTime(),
- 'max_age' => 600,
- 's_maxage' => 600,
- 'private' => false,
- 'public' => true,
- ]);
To check if the Response validators (ETag
, Last-Modified
) match aconditional value specified in the client Request, use theisNotModified()
method:
- if ($response->isNotModified($request)) {
- $response->send();
- }
If the Response is not modified, it sets the status code to 304 and removes theactual response content.
Redirecting the User
To redirect the client to another URL, you can use theRedirectResponse
class:
- use Symfony\Component\HttpFoundation\RedirectResponse;
- $response = new RedirectResponse('http://example.com/');
Streaming a Response
The StreamedResponse
class allowsyou to stream the Response back to the client. The response content isrepresented by a PHP callable instead of a string:
- 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
The flush()
function does not flush buffering. If ob_start()
hasbeen called before or the output_buffering
php.ini
option is enabled,you must call ob_flush()
before flush()
.
Additionally, PHP isn't the only layer that can buffer output. Your webserver might also buffer based on its configuration. Some servers, such asNginx, let you disable buffering at the config level or by adding a special HTTPheader in the response:
- // disables FastCGI buffering in Nginx only for this response
- $response->headers->set('X-Accel-Buffering', 'no')
Serving Files
When sending a file, you must add a Content-Disposition
header to yourresponse. While creating this header for basic file downloads is straightforward,using non-ASCII filenames is more involving. ThemakeDisposition()
abstracts the hard work behind a simple API:
- use Symfony\Component\HttpFoundation\HeaderUtils;
- use Symfony\Component\HttpFoundation\Response;
- use Symfony\Component\HttpFoundation\ResponseHeaderBag;
- $fileContent = ...; // the generated file content
- $response = new Response($fileContent);
- $disposition = HeaderUtils::makeDisposition(
- HeaderUtils::DISPOSITION_ATTACHMENT,
- 'foo.pdf'
- );
- $response->headers->set('Content-Disposition', $disposition);
Alternatively, if you are serving a static file, you can use aBinaryFileResponse
:
- use Symfony\Component\HttpFoundation\BinaryFileResponse;
- $file = 'path/to/file.txt';
- $response = new BinaryFileResponse($file);
The BinaryFileResponse
will automatically handle Range
andIf-Range
headers from the request. It also supports X-Sendfile
(see for Nginx and Apache). To make use of it, you need to determinewhether or not the X-Sendfile-Type
header should be trusted and calltrustXSendfileTypeHeader()
if it should:
- BinaryFileResponse::trustXSendfileTypeHeader();
Note
The BinaryFileResponse
will only handle X-Sendfile
if the particular header is present.For Apache, this is not the default case.
To add the header use the mod_headers
Apache module and add the following to the Apache configuration:
- <IfModule mod_xsendfile.c>
- # This is already present somewhere...
- XSendFile on
- XSendFilePath ...some path...
- # This needs to be added:
- <IfModule mod_headers.c>
- RequestHeader set X-Sendfile-Type X-Sendfile
- </IfModule>
- </IfModule>
With the BinaryFileResponse
, you can still set the Content-Type
of the sent file,or change its Content-Disposition
:
- // ...
- $response->headers->set('Content-Type', 'text/plain');
- $response->setContentDisposition(
- ResponseHeaderBag::DISPOSITION_ATTACHMENT,
- 'filename.txt'
- );
It is possible to delete the file after the request is sent with thedeleteFileAfterSend()
method.Please note that this will not work when the X-Sendfile
header is set.
If the size of the served file is unknown (e.g. because it's being generated on the fly,or because a PHP stream filter is registered on it, etc.), you can pass a Stream
instance to BinaryFileResponse
. This will disable Range
and Content-Length
handling, switching to chunked encoding instead:
- use Symfony\Component\HttpFoundation\BinaryFileResponse;
- use Symfony\Component\HttpFoundation\File\Stream;
- $stream = new Stream('path/to/stream');
- $response = new BinaryFileResponse($stream);
Note
If you just created the file during this same request, the file may be sentwithout any content. This may be due to cached file stats that return zero forthe size of the file. To fix this issue, call clearstatcache(true, $file)
with the path to the binary file.
Creating a JSON Response
Any type of response can be created via theResponse
class by setting theright content and headers. A JSON response might look like this:
- use Symfony\Component\HttpFoundation\Response;
- $response = new Response();
- $response->setContent(json_encode([
- 'data' => 123,
- ]));
- $response->headers->set('Content-Type', 'application/json');
There is also a helpful JsonResponse
class, which can make this even easier:
- use Symfony\Component\HttpFoundation\JsonResponse;
- // if you know the data to send when creating the response
- $response = new JsonResponse(['data' => 123]);
- // if you don't know the data to send when creating the response
- $response = new JsonResponse();
- // ...
- $response->setData(['data' => 123]);
- // if the data to send is already encoded in JSON
- $response = JsonResponse::fromJsonString('{ "data": 123 }');
The JsonResponse
class sets the Content-Type
header toapplication/json
and encodes your data to JSON when needed.
Caution
To avoid XSSI JSON Hijacking, you should pass an associative arrayas the outer-most array to JsonResponse
and not an indexed array sothat the final result is an object (e.g. {"object": "not inside an array"}
)instead of an array (e.g. [{"object": "inside an array"}]
). Readthe OWASP guidelines for more information.
Only methods that respond to GET requests are vulnerable to XSSI 'JSON Hijacking'.Methods responding to POST requests only remain unaffected.
JSONP Callback
If you're using JSONP, you can set the callback function that the data shouldbe passed to:
- $response->setCallback('handleResponse');
In this case, the Content-Type
header will be text/javascript
andthe response content will look like this:
- handleResponse({'data': 123});
Session
The session information is in its own document: Session Management.
Learn More
- 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
- How to Upload Files
- Making the Locale "Sticky" during a User's Session
- Bridge a legacy Application with Symfony Sessions
- Session Proxy Examples
- Cache Invalidation
- Varying the Response for HTTP Cache
- Working with Edge Side Includes
- HTTP Cache Expiration
- Working with Server Side Includes
- HTTP Cache Validation
- How to Use Varnish to Speed up my Website