Files
archived-symfony-docs/introduction/http_fundamentals.rst
2025-10-20 13:43:23 +02:00

391 lines
16 KiB
ReStructuredText

.. _symfony2-and-http-fundamentals:
Symfony and HTTP Fundamentals
=============================
Great news! While you're learning Symfony, you're *also* learning the fundamentals
of the *web*. Symfony is closely modeled after the HTTP Request-Response flow: that
*fundamental* paradigm that's behind almost *all* communication on the web.
In this article, you'll walk through the HTTP fundamentals and find out how these
are applied throughout Symfony.
Requests and Responses in HTTP
------------------------------
HTTP (Hypertext Transfer Protocol) is a text language that allows two machines
to communicate with each other. For example, when checking for the latest
`xkcd`_ comic, the following (approximate) conversation takes place:
.. raw:: html
<object data="../_images/http/xkcd-full.svg" type="image/svg+xml"
alt="A sequence diagram showing the browser sending &quot;Can I see today's comic?&quot; to the xkcd server. The server prepares the page's HTML and sends it back to the browser."
></object>
HTTP is the term used to describe this text-based language. The goal of
your server is *always* to understand text requests and return text responses.
Symfony is built from the ground up around that reality. Whether you realize
it or not, HTTP is something you use every day. With Symfony, you'll learn
how to master it.
Step 1: The Client Sends a Request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Every conversation on the web starts with a *request*. The request is a text
message created by a client (e.g. a browser, a smartphone app, etc) in a
special format known as HTTP. The client sends that request to a server,
and then waits for the response.
Take a look at the first part of the interaction (the request) between a
browser and the xkcd web server:
.. raw:: html
<object data="../_images/http/xkcd-request.svg" type="image/svg+xml"
alt="A sequence diagram showing the request from browser to the xkcd server."
></object>
In HTTP-speak, this HTTP request would actually look something like this:
.. code-block:: text
GET / HTTP/1.1
Host: xkcd.com
Accept: text/html
User-Agent: Mozilla/5.0 (Macintosh)
These few lines communicate *everything* necessary about exactly which
resource the client is requesting. The first line of an HTTP request is the
most important, because it contains two important things: the HTTP method (GET)
and the URI (``/``).
The URI (e.g. ``/``, ``/contact``, etc) is the unique address or location
that identifies the resource the client wants. The HTTP method (e.g. ``GET``)
defines what the client wants to *do* with the resource. The HTTP methods (also
known as verbs) define the few common ways that the client can act upon the
resource - the most common HTTP methods are:
**GET**
Retrieve the resource from the server (e.g. when visiting a page);
**POST**
Create a resource on the server (e.g. when submitting a form);
**PUT**/**PATCH**
Update the resource on the server (used by APIs);
**DELETE**
Delete the resource from the server (used by APIs).
With this in mind, you can imagine what an HTTP request might look like to
delete a specific blog post, for example:
.. code-block:: text
DELETE /blog/15 HTTP/1.1
.. note::
There are actually nine HTTP methods defined by the HTTP specification,
but many of them are not widely used or supported. In reality, many
modern browsers only support ``POST`` and ``GET`` in HTML forms. Various
others are however supported in `XMLHttpRequest`_.
In addition to the first line, an HTTP request invariably contains other
lines of information called request **headers**. The headers can supply a wide
range of information such as the host of the resource being requested (``Host``),
the response formats the client accepts (``Accept``) and the application the
client is using to make the request (``User-Agent``). Many other headers exist
and can be found on Wikipedia's `List of HTTP header fields`_ article.
Step 2: The Server Returns a Response
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once a server has received the request, it knows exactly which resource the
client needs (via the URI) and what the client wants to do with that resource
(via the method). For example, in the case of a GET request, the server
prepares the resource and returns it in an HTTP response. Consider the response
from the xkcd web server:
.. raw:: html
<object data="../_images/http/xkcd-full.svg" type="image/svg+xml"
alt="The full sequence diagram with the xkcd server sending the page's HTML back to the browser."
></object>
Translated into HTTP, the response sent back to the browser will look something
like this:
.. code-block:: text
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>
The HTTP response contains the requested resource (the HTML content in this
case), as well as other information about the response. The first line is
especially important and contains the HTTP response status code (200 in this
case).
The status code communicates the overall outcome of the request back to the
client. Was the request successful? Was there an error? Different status codes
exist that indicate success, an error or that the client needs to do something
(e.g. redirect to another page). Check out the `list of HTTP status codes`_.
Like the request, an HTTP response contains additional pieces of information
known as HTTP headers. The body of the same resource could be returned in multiple
different formats like HTML, XML or JSON and the ``Content-Type`` header uses
Internet Media Types like ``text/html`` to tell the client which format is
being returned. You can see a `List of common media types`_ from IANA.
Many other headers exist, some of which are very powerful. For example, certain
headers can be used to create a powerful caching system.
Requests, Responses and Web Development
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This request-response conversation is the fundamental process that drives all
communication on the web.
The most important fact is this: regardless of the language you use, the
type of application you build (web, mobile, JSON API) or the development
philosophy you follow, the end goal of an application is **always** to understand
each request and create and return the appropriate response.
.. seealso::
To learn more about the HTTP specification, read the original `HTTP 1.1 RFC`_
or the `HTTP Bis`_, which is an active effort to clarify the original
specification.
Requests and Responses in PHP
-----------------------------
So how do you interact with the "request" and create a "response" when using
PHP? In reality, PHP abstracts you a bit from the whole process::
$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;
As strange as it sounds, this small application is in fact taking information
from the HTTP request and using it to create an HTTP response. Instead of
parsing the raw HTTP request message, PHP prepares superglobal variables
(such as ``$_SERVER`` and ``$_GET``) that contain all the information from the
request. Similarly, instead of returning the HTTP-formatted text response, you
can use the PHP :phpfunction:`header` function to create response headers and
print out the actual content that will be the content portion of the response
message. PHP will create a true HTTP response and return it to the client:
.. code-block:: text
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
Requests and Responses in Symfony
---------------------------------
Symfony provides an alternative to the raw PHP approach via two classes that
allow you to interact with the HTTP request and response in an easier way.
Symfony Request Object
~~~~~~~~~~~~~~~~~~~~~~
The :class:`Symfony\\Component\\HttpFoundation\\Request` class is an
object-oriented representation of the HTTP request message. With it, you
have all the request information at your fingertips::
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
// the URI being requested (e.g. /about) minus any query parameters
$request->getPathInfo();
// retrieves $_GET and $_POST variables respectively
$request->query->get('id');
$request->getPayload()->get('category', 'default category');
// retrieves $_SERVER variables
$request->server->get('HTTP_HOST');
// retrieves an instance of UploadedFile identified by "attachment"
$request->files->get('attachment');
// retrieves a $_COOKIE value
$request->cookies->get('PHPSESSID');
// retrieves an HTTP request header, with normalized, lowercase keys
$request->headers->get('host');
$request->headers->get('content-type');
$request->getMethod(); // e.g. GET, POST, PUT, DELETE or HEAD
$request->getLanguages(); // an array of languages the client accepts
As a bonus, the ``Request`` class does a lot of work in the background about which
you will never need to worry. For example, the ``isSecure()`` method
checks the *three* different values in PHP that can indicate whether or not
the user is connecting via a secure connection (i.e. HTTPS).
Symfony Response Object
~~~~~~~~~~~~~~~~~~~~~~~
Symfony also provides a :class:`Symfony\\Component\\HttpFoundation\\Response`
class: a PHP representation of an HTTP response message. This allows your
application to use an object-oriented interface to construct the response that
needs to be returned to the client::
use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->setContent('<html><body><h1>Hello world!</h1></body></html>');
$response->setStatusCode(Response::HTTP_OK);
// sets a HTTP response header
$response->headers->set('Content-Type', 'text/html');
// prints the HTTP headers followed by the content
$response->send();
There are also several response *sub-classes* to help you return
:ref:`JSON <component-http-foundation-json-response>`,
:ref:`redirect <redirect-response>`,
:ref:`stream file downloads <component-http-foundation-serving-files>`
and more.
.. tip::
The ``Request`` and ``Response`` classes are part of a standalone component
called :doc:`symfony/http-foundation </components/http_foundation>`
that you can use in *any* PHP project. This also contains classes for handling
sessions, file uploads and more.
If Symfony offered nothing else, you would already have a toolkit for accessing
request information and an object-oriented interface for creating the response.
Even as you learn the many powerful features in Symfony, keep in mind that the
goal of your application is always *to interpret a request and create the
appropriate response based on your application logic*.
The Journey from the Request to the Response
--------------------------------------------
Like HTTP itself, using the ``Request`` and ``Response`` objects is pretty
straightforward. The hard part of building an application is writing what comes in
between. In other words, the real work comes in writing the code that
interprets the request information and creates the response.
Your application probably does many things, like sending emails, handling
form submissions, saving things to a database, rendering HTML pages and protecting
content with security. How can you manage all of this and still keep your
code organized and maintainable? Symfony was created to help you with these
problems.
The Front Controller
~~~~~~~~~~~~~~~~~~~~
Traditionally, applications were built so that each "page" of a site was
its own physical file (e.g. ``index.php``, ``contact.php``, etc.).
There are several problems with this approach, including the inflexibility
of the URLs (what if you wanted to change ``blog.php`` to ``news.php`` without
breaking all of your links?) and the fact that each file *must* manually
include some set of core files so that security, database connections and
the "look" of the site can remain consistent.
A much better solution is to use a front controller: a single PHP file that
handles every request coming into your application. For example:
+------------------------+------------------------+
| ``/index.php`` | executes ``index.php`` |
+------------------------+------------------------+
| ``/index.php/contact`` | executes ``index.php`` |
+------------------------+------------------------+
| ``/index.php/blog`` | executes ``index.php`` |
+------------------------+------------------------+
.. include:: /_includes/_rewrite_rule_tip.rst.inc
Now, every request is handled exactly the same way. Instead of individual URLs
executing different PHP files, the front controller is *always* executed,
and the routing of different URLs to different parts of your application
is done internally.
A small front controller might look like this::
// index.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$path = $request->getPathInfo(); // the URI path being requested
if (in_array($path, ['', '/'])) {
$response = new Response('Welcome to the homepage.');
} elseif ('/contact' === $path) {
$response = new Response('Contact us');
} else {
$response = new Response('Page not found.', Response::HTTP_NOT_FOUND);
}
$response->send();
This is better, but this is still a lot of repeated work! Fortunately, Symfony can
help once again.
The Symfony Application Flow
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A Symfony framework application *also* uses a front-controller file. But inside,
*Symfony* is responsible for handling each incoming request and figuring out what
to do:
.. _request-flow-figure:
.. raw:: html
<object data="../_images/http/request-flow.svg" type="image/svg+xml"
alt="A flow diagram visualizing the request-response flow. Each step is written out in text in the next section."
></object>
Incoming requests are interpreted by the :doc:`Routing component </routing>` and
passed to PHP functions that return ``Response`` objects.
This may not make sense yet, but as you keep reading, you'll learn about :doc:`routes </routing>`
and :doc:`controllers </controller>`: the two fundamental parts to creating a page.
But as you go along, don't forget that no matter *how* complex your app gets, your
job is always the same: read information from the Request and use it to create a
Response.
Summary: The Request-Response Flow
----------------------------------
Here's what we've learned so far:
#. A client (e.g. a browser) sends an HTTP request;
#. Each request executes the same, single file (called a "front controller");
#. The front controller boots Symfony and passes the request information;
#. Internally, Symfony uses *routes* and *controllers* to create the Response for
the page (we'll learn about these soon!);
#. Symfony turns your ``Response`` object into the text headers and content
(i.e. the HTTP response), which are sent back to the client.
.. _`xkcd`: https://xkcd.com/
.. _`XMLHttpRequest`: https://en.wikipedia.org/wiki/XMLHttpRequest
.. _`HTTP 1.1 RFC`: https://www.w3.org/Protocols/rfc2616/rfc2616.html
.. _`HTTP Bis`: https://datatracker.ietf.org/wg/httpbis/
.. _`List of HTTP header fields`: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
.. _`list of HTTP status codes`: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
.. _`List of common media types`: https://www.iana.org/assignments/media-types/media-types.xhtml