NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Lekce 8 - Standardy jazyka PHP - PSR-7 (Obecná specifikace a proudy)

V předchozí lekci, Standardy jazyka PHP - PSR-6 (cachovací rozhraní), jsme si vysvětlili podstatu cachovacího rozhraní a některé důležité pojmy s ním související.

PSR-7, který se zabývá rozhraním pro HTTP dotazy a odpovědi, je poněkud obsáhlejší a v několika následujících lekcích se budeme věnovat právě jemu.

Pro lepší pochopení standardu si můžeme prohlédnout následující RFC:

Struktura

HTTP protokol je bezesporu základem webu. Webové prohlížeče a HTTP klienti (například cURL) vytváří HTTP dotazy, na které odpovídá server pomocí HTTP odpovědi. Tyto zprávy jsou pro koncového uživatele pro jednoduchost a přehlednost zobecněné, ale pokud nějakou aplikaci vyvíjíme, zpravidla potřebujeme přesně znát jejich strukturu a také způsob, jak s nimi manipulovat.

Každý HTTP dotaz má specifickou strukturu:

POST /path HTTP/1.1
Host: example.com

foo=bar&baz=bat

První řádek obsahuje:

  • metodu dotazu,
  • cíl (většinou absolutní URI cestu nebo cestu na web) a
  • verzi HTTP protokolu

Za ním následuje jedna nebo více hlaviček, prázdný řádek a poté tělo dotazu.

Podobná je i struktura HTTP odpovědi:

HTTP/1.1 200 OK
Content-Type: text/plain

Toto je tělo odpovědi

První neboli „stavový“ řádek obsahuje v tomto pořadí:

  • verzi HTTP protokolu,
  • stavový kód a
  • tzv. reason phrase

Reason phrase je slovní hodnota a pro člověka stravitelnější vysvětlení daného stavového kódu.

Podobně jako v HTTP dotazu následuje i v odpovědi hlavička, prázdný řádek a tělo odpovědi.

Specifikace

Uvedeme si specifikace zpráv, HTTP hlaviček a proudů (streamů).

Zprávy

Pod pojmem HTTP zpráva je myšlen buď požadavek ze strany klienta na server, nebo odpověď serveru klientovi. Oba druhy mají definováno své vlastní rozhraní a sice Psr\Http\Message\RequestInterface a Psr\Http\Message\ResponseInterface.

Obě tato rozhraní rozšiřují Psr\Http\Message\MessageInterface. Zatímco toto výchozí rozhraní MŮŽE být implementováno přímo, implementátoři (ti, co rozhraní implementují) BY MĚLI implementovat navazující Psr\Http\Message\RequestInterface a Psr\Http\Message\ResponseInterface.

Zde jsme si uvedli plnou cestu k rozhraním požadavků i odpovědí. Dále budeme Psr\Http\Message pro přehlednost vynechávat.

HTTP hlavičky

Ukážeme si hlavičky case-insensitive, s více hodnotami a také host header.

Case-insensitive hlavičky

HTTP zprávy obsahují case-insensitive hlavičky. Ty jsou vyhledávány pomocí názvu z tříd implementujících MessageInterface nehledě na velikost písmen. Výsledek je stejný, ať už se snažíme vyhledat foo nebo třeba FoO. Stejně tak se hlavička foo přepíše, pokud později nastavíme Foo:

$message = $message->withHeader('foo', 'bar');

echo $message->getHeaderLine('foo');
// Vypíše se „bar“

echo $message->getHeaderLine('FOO');
// Vypíše se „bar“

$message = $message->withHeader('fOO', 'baz');
echo $message->getHeaderLine('foo');
// Vypíše se „baz“

I když hlavičky mohou být hledány bez důrazu na velikost písmen, původní zápis MUSÍ být implementací zachován, konkrétně při volání getHeaders().

Některé (ne zcela vyhovující) HTTP aplikace mohou záviset na velikosti písmen, takže je pro uživatele užitečné, aby ji mohl při vytváření HTTP požadavku přesně definovat.

Hlavičky s více hodnotami

Aby bylo možné pracovat s více hodnotami a zároveň zachovat výhody práce s hlavičkami jako s řetězci, hlavičky mohou být získány z instance MessageInterface jako pole nebo string. Použitím metody getHeaderLine() získáme všechny (case-insensitive) hodnoty hlavičky zřetězené pomocí čárky. Pomocí funkce getHeader() získáme pole všech hodnot (opět case-insensitive):

$message = $message
    ->withHeader('foo', 'bar')
    ->withAddedHeader('foo', 'baz');

$header = $message->getHeaderLine('foo');
// $header obsahuje: 'bar,baz'

$header = $message->getHeader('foo');
// ['bar', 'baz']

Ne všechny hodnoty odesílané v hlavičkách mohou být zřetězeny pomocí čárky, například Set-Cookie.

Pokud pracujeme s těmito konkrétními hlavičkami, jako uživatelé tříd založených na MessageInterface bychom MĚLI spoléhat spíše na getHeader() namísto getHeaderLine().

Host header

V dotazech host header typicky napodobuje host komponentu URI stejně jako host používaný při navazování TCP spojení. Specifikace HTTP ale umožňuje tyto dvě situace od sebe navzájem rozlišit.

Pokud při konstrukci není dodán host header, implementace se MUSÍ pokusit tento header nastavit podle URI.

RequestInterface::withUri() ve výchozím nastavení nahradí host header požadavku host headerem odpovídajícím host komponentě předaného UriInterface.

Je možné zvolit, aby byl původní stav host headeru zachován pomocí druhého argumentu funkce ($preserveHost). Pokud tento argument nastavíme na TRUE a zpráva už nějaký host header obsahuje, požadavek ho aktualizovat nebude.

Následující tabulka ilustruje, co getHeaderLine('Host') v různých situacích vrátí na požadavek vrácený pomocí withUri() s argumentem $preserveHost nastaveným jako TRUE.

REQUEST HOST HEADER ★1 REQUEST HOST KOMPONENTA ★2 URI HOST KOMPONENTA ★3 VÝSLEDEK
"" "" "" ""
"" foo.com "" foo.com
"" foo.com bar.com foo.com
foo.com "" bar.com foo.com
foo.com bar.com baz.com foo.com

1 – hodnota host headeru před operací

2 – host komponenta URI vytvořena v požadavku před operací

3 – host komponenta URI vytvořena pomocí withUri()

Proudy (streams)

HTTP zprávy se skládají z tzv. start-line, hlaviček a těla. Tělo zprávy může být různých velikostí, od jednoho řádku až po extrémně obsáhlá data.

Pokus o reprezentaci dlouhého těla jako řetězce může jednoduše vyústit ve větší obsazení paměti, než bylo původně zamýšleno, protože v ní musí být uloženo úplně celé tělo. Při takovémto pokusu o uložení do paměti, ať už požadavku nebo odpovědi, je znemožněna práce implementace s tělem.

Když zapisujeme do proudu dat nebo z něj čteme, jsou pomocí rozhraní StreamInterface detaily o implementaci schovány. V situacích, kdy je pro nás implementace pomocí řetězce výhodná, mohou být použity vestavěné proudy jako php://memory a php://temp.

StreamInterface vystavuje své schopnosti pomocí tří metod:

  • isReadable(),
  • isWritable() a
  • isSeekable().

Tyto metody mohou být použity dalšími funkcemi (metodami, rozhraními, …) pro zjištění, jestli proud vyhovuje všem nárokům a podmínkám.

Každá instance proudu má různorodé schopnosti a může být:

  • read-only (pouze pro čtení)
  • write-only (pouze pro zápis)
  • read-write (čtení i zápis)

Také mohou povolit nahodilý přístup (hledání zepředu i zezadu), nebo pouze sekvenční přístup (třeba v případě socketu, pipe (roury) nebo callback-based proudu).

Rozhraní StreamInterface definuje ještě jednu metodu, a to __toString(). Je určena ke zjednodušení získání nebo emitování celého obsahu těla najednou.

Na rozdíl od ostatních rozhraní týkajících se HTTP zpráv StreamInterface nezajišťuje neměnnost dat (immutability).

Pokud jsou použity tzv. wrappovací funkce, neměnnost je nemožné zajistit, protože jakýkoliv kód, který pracuje se zdrojem, může teoreticky změnit jeho stav (a to včetně polohy kurzoru, obsahu a mnoha dalšího). Autor doporučuje, aby uživatelé při implementaci rozhraní používali pouze read-only streamy pro serverové požadavky a dotazy klienta.

Uživatelé by si měli být vědomi, že instance proudu může být změnitelná (mutable) a jako taková může změnit stav zprávy. Pokud jsme na pochybách, je vhodné vytvořit novou instanci a přiložit ji ke zprávě, abychom stav vynutili.

V další lekci, Standardy jazyka PHP - PSR-7 (Rozhraní HTTP zpráv a proudů), se budeme zabývat rozhraními pro datové proudy a HTTP zprávy, jednotlivými funkcemi a jejich použitím.


 

Předchozí článek
Standardy jazyka PHP - PSR-6 (cachovací rozhraní)
Všechny články v sekci
Standardy jazyka PHP
Přeskočit článek
(nedoporučujeme)
Standardy jazyka PHP - PSR-7 (Rozhraní HTTP zpráv a proudů)
Článek pro vás napsala Miroslava Škutová
Avatar
Uživatelské hodnocení:
1 hlasů
.
Aktivity