Symfony po krůčkách – HTTP fundamentalista

Stěžejní Symfony komponentou je HttpFoundation. Bere si na mušku naprostý základ webových aplikací – HTTP protokol. Je skvěle napsaná a snadno se používá. V dnešním díle si ukážeme, jak nám s použitím HttpFoundation knihovny aplikace pěkně prokoukne.

Seriál: Symfony po krůčkách (18 dílů)

  1. Symfony po krůčkách – Event Dispatcher 30.11.2015
  2. Symfony Console jako první rande se Symfony 7.12.2015
  3. Symfony po krůčkách – Filesystem a Finder 14.12.2015
  4. Symfony po krůčkách – Paralýza možností? OptionsResolver tě zachrání 21.12.2015
  5. Symfony po krůčkách – spouštíme procesy 4.1.2016
  6. Symfony po krůčkách – Translation – překlady jednoduše 11.1.2016
  7. Symfony po krůčkách – Validator (1) 18.1.2016
  8. Symfony po krůčkách – Validator (2) 25.1.2016
  9. Symfony po krůčkách – Routing 1.2.2016
  10. Symfony po krůčkách – MicroKernel 9.2.2016
  11. Konfigurujeme Symfony pomocí YAMLu 16.2.2016
  12. Symfony po krůčkách – oblékáme MicroKernel 23.2.2016
  13. Symfony po krůčkách – ClassLoader 29.2.2016
  14. Symfony po krůčkách – Twig 8.3.2016
  15. Symfony po krůčkách – Twig II. 15.3.2016
  16. Symfony po krůčkách – DomCrawler a CssSelector 23.3.2016
  17. Symfony po krůčkách – HTTP fundamentalista 12.4.2016
  18. Symfony po krůčkách – ušli jsme pořádný kus 19.4.2016

Od PHP k Symfony

Napsat webovou aplikaci v čístém PHP je snadné, ale bohužel nepohodlné a často se u toho naseká mnoho chyb. Data od uživatele musíme číst z několika superglobálních polí $_GET, $_POST, $_COOKIE, $_FILES a $_SESSION . K poslání odpovědi je třeba vypsat text a nastavit HTTP hlavičky

echo sprintf('Hello %s!', $_GET['name']);
header('Content-Type: text/html; charset=utf-8');

Pokud si zkusíme takto jednoduchý příklad, moc dobře nepořídíme. PHP zobrazí chybové hlášení Warning: Cannot modify header information - headers already sent, protože HTTP hlavičky se musí nastavit dříve než cokoliv vypíšeme. Další chyby a problémy vyvstanou ve chvíli, kdy začneme zpracovávat soubory, parsovat HTTP hlavičky a budeme chtít psát čistý a testovatelný kód.

Všechny uvedené problémy řeší Symfony komponenta HttpFoundation.  Je tak dobrá, že ji používá nejen Symfony framework, ale také Drupal, Laravel, eZ Publish a další. Nabízí skvělé objektové rozhraní, snadno se používá, přesně implementuje HTTP protokol a dokonce způsobila malou revoluci. Díky ní se Symfony neoznačuje jako MVC či MVP framework, ale honosí se titulem HTTP framework. Princip fungování Symfony aplikace je znázorněno na následujícím obrázku:

Komunikace mezi klientem a serverem pomocí HTTP protokolu

První příklad

Pojďme si komponentu HttpFoundation vyzkoušet. Nainstalujeme ji přes Composer

composer require symfony/http-foundation

Jak přepsat úvodní příklad pomocí komponenty HttpFoundation? Velmi jednoduše. Uděláme jen přesně to, co po nás chce HTTP protokol a co je uvedeno na obrázku.

<?php

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

require_once __DIR__.'/vendor/autoload.php';

$request = Request::createFromGlobals();

$response = new Response(sprintf('Hello %s!', $request->query->get('name')));
$response->send();

Vytvoříme objekt $request, který reprezentuje HTTP požadavek od klienta. Potom vytvoříme objekt $response, který představuje HTTP odpověď, a zavoláme na něm metodu send(), která tuto odpověď pošle klientovi. A to je vše!

Request – HTTP požadavek

Objekt třídy Request zapouzdřuje všechna superglobální PHP pole s uživatelskými vstupy. Nejjednoduší způsob, jak jej vytvořit, je pomocí helperu

$request = Request::createFromGlobals();

Můžeme použít i zápis, z kterého na první pohled vidíme, co se přesně stane

$request = new Request($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);

A jelikož Symfony myslí na všechno, tak objekt $request lze vytvořit i z obyčejné URL adresy a parametrů

$request = Request::create(
    'https://spazef0rze:1234@example.org:443/admin?action=edit', 
    'POST', 
    ['title' => 'Test']
);

Všechna data jsou dostupná přes veřejné atributy třídy Request

$request->query;   // $_GET
$request->request; // $_POST
$request->cookies; // $_COOKIE
$request->files;   // $_FILES
$request->server;  // $_SERVER
$request->headers; // $_SERVER['HTTP_...']

Symfony nejde jen o skvěle použitelné API, ale také o rychlost. S těmito daty pracujeme v aplikaci nejčastěji, a proto jsou dostupné jako třídní atributy, ke kterým PHP přistupuje daleko rychleji než k metodám.

Data nejsou uložena v obyčejném poli, ale jsou zapouzdřena v objektech třídy ParameterBag. Ta obsahuje spoustu užitečných metod, které nám ušetří psaní zbytečného kódu:

Uložení HTTP dat v třídě Request z HttpFoundation komponenty

$request->query->all(); // $_GET
$request->query->get('action', 'list'); // array_key_exists($_GET, 'action') ? $_GET['action'] : 'list'
$request->cookies->has('PWID'); // isset($_COOKIE['PWID']);
$request->request->keys(); // array_keys($_POST)

Navíc můžeme uživatelská data snadno filtrovat:

$request->query->getInt('id'); // převede $_GET['id'] na číslo
$request->query->getAlpha('action'); // z $_GET['action'] odstraní znaky, které nejsou v abecedě
$request->query->getDigits('order'); // z $_GET['order'] odstraní nečíselné znaky
$request->request->filter('email', null, FILTER_VALIDATE_EMAIL);
// vrátí hodnotu z $_POST['email'] pokud obsahuje validní email

Kdo v čistém PHP psal REST API, ví, jak složitě se dostávájí z POST pořadavku data (např. JSON řetězec). Třída Request na to má metodu

$content = $request->getContent();

Práci s daty máme skvěle zvládnutou. Tím možnosti komponenty HttpFoundation nekončí. O HTTP požadavku poskytuje veškeré informace. Pro představu zmíním pár metod:

$request->isSecure(); // HTTPS protokol?
$request->getMethod(); // název HTTP metody
$request->isMethod('POST'); // odesláno přes POST?
$request->isMethodSafe(); // posláno přes GET nebo HEAD?
$request->isXmlHttpRequest(); // AJAX
$request->isNoCache(); // je nastavena hlavička no-cache?
$request->getLanguages(); // podporované jazyky prohlížeče
$request->getCharset();
$request->getEncodings();
$request->getAcceptableContentType();
$request->getPathInfo(); // významná část URL adresy nutná pro routování

Response – HTTP odpověď

Objekt třídy Response obsahuje veškeré informace nutné k poslání odpovědi. Zkrátka, nic jiného k vykreslení webové stránky není potřeba. Vytvořit odpověď je snadné

use Symfony\Component\HttpFoundation\Response;

$response = new Response('Hello World!');

// nebo se všemi argumenty
$response = new Response('Hello World!', Response::HTTP_OK, ['content-type' => 'text/html'];

Největší výhodou tohoto přístupu je, že se neovlivňují globální proměnné, nenastavují žádné hlavičky a neposílá se obsah. Takto vytvořenou odpověď můžete kdekoliv v aplikaci zahodit a vytvořit novou. Nic se tím nepokazí. Vytvořenou odpověď lze upravovat:

$response->setContent('Hello Martin!');
$response->setStatusCode(Response::HTTP_NOT_FOUND); // vrátí 404
$response->setCharset('UTF-8');
$response->headers->set('Content-Type', 'text/plain');

Až jsme s odpovědí spokojeni, tak ji pošleme zavoláním metody

$response->send();

Pokud chceme mít jistotu, že vše v odpovědi je nastaveno správně, musíme před samotným odesláním zavolat

$response->prepare($request);
$response->send();

Zatímco třída pro HTTP požadavek byla jen jedna, Pro HTTP odpověď nabízí komponenta HttpFoundation tříd několik. Pokud chceme provést přesměrování, použijeme

use Symfony\Component\HttpFoundation\RedirectResponse;

$response = new RedirectResponse('https://www.zdrojak.cz');

Pro poslání souboru se skvěle hodí

use Symfony\Component\HttpFoundation\BinaryFileResponse;

$response = new BinaryFileResponse('path/to/file.txt');

Tato třída automaticky nastaví HTTP hlavičky RangeIf-Range, a X-Sendfile.

Pokud chceme změnit název souboru, pod kterým nám ho prohlížeč nabídne uložit, stačí zavolat metodu

use Symfony\Component\HttpFoundation\ResponseHeaderBag;

$response->setContentDisposition(
    ResponseHeaderBag::DISPOSITION_ATTACHMENT, 
    'filename.txt'
);

A pro JSON zvolíme

use Symfony\Component\HttpFoundation\JsonResponse;

$response = new JsonResponse(['data' => 123]);

Zase o krok dále

V dnešním díle jsme si ukázali:

  • jak funguje komunikace mezi prohlížečem a serverem,
  • jak nainstalovat a začít používat HttpFoundation,
  • jak místo několika superglobálních PHP polí používat jeden jediný objekt třídy Request,
  • jak vykreslovat stránky či vracet soubory a JSON data pomocí tříd Response,
  • ale hlavně, jak psát webové aplikace čistě pomocí Symfony komponenty HttpFoundation.

Kam jít dál?

Všechny možnosti komponenty HttpFoundation jsou krásně popsány v oficiální dokumentaci. Kapitola Symfony and HTTP Fundamentals podrobně vysvětluje propojení Symfony frameworku s HTTP protokolem.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 1

Přehled komentářů

Marek
Zdroj: https://www.zdrojak.cz/?p=17400