8. diel - Štandardy jazyka PHP - PSR-7 (Všeobecná špecifikácie a prúdy)
V predchádzajúcej lekcii, Štandardy jazyka PHP - PSR-6 (cachovací rozhranie) , sme si vysvetlili podstatu cachovacího rozhranie a niektoré dôležité pojmy s ním súvisiace.
PSR-7, ktorý sa zaoberá rozhraním pre HTTP dotazy a odpovede, je trochu obsiahlejší a v niekoľkých nasledujúcich lekciách sa budeme venovať práve jemu.
Pre lepšie pochopenie štandardu si môžeme prezrieť nasledujúce RFC:
Štruktúra
HTTP protokol je bezpochyby základom webu. Webové prehliadače a HTTP klienti (napríklad cURL) vytvára HTTP otázky, na ktoré odpovedá server pomocou HTTP odpovede. Tieto správy sú pre koncového užívateľa pre jednoduchosť a prehľadnosť zovšeobecnené, ale ak nejakú aplikáciu vyvíjame, spravidla potrebujeme presne poznať ich štruktúru a tiež spôsob, ako s nimi manipulovať.
Každý HTTP dotaz má špecifickú štruktúru:
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
Prvý riadok obsahuje:
- metódu dotazu,
- cieľ (väčšinou absolútnu URI cestu alebo cestu na web) a
- verziu HTTP protokolu
Za ním nasleduje jedna alebo viac hlavičiek, prázdny riadok a potom telo dotazu.
Podobná je aj štruktúra HTTP odpovede:
HTTP/1.1 200 OK Content-Type: text/plain Toto je tělo odpovědi
Prvý čiže "stavový" riadok obsahuje v tomto poradí:
- verziu HTTP protokolu,
- stavový kód a
- tzv. reason phrase
Reason phrase je slovná hodnota a pre človeka stráviteľnejšie vysvetlenie daného stavového kódu.
Podobne ako v HTTP dotaze nasleduje aj v odpovedi hlavička, prázdny riadok a telo odpovede.
Špecifikácia
Uvedieme si špecifikácie správ, HTTP hlavičiek a prúdov (streamov).
Správy
Pod pojmom HTTP správa je myslený buď požiadavka zo strany
klienta na server, alebo odpoveď servera klientovi.
Oba druhy majú definované svoje vlastné rozhranie a síce
Psr\Http\Message\RequestInterface
a
Psr\Http\Message\ResponseInterface
.
Obe tieto rozhrania rozširujú
Psr\Http\Message\MessageInterface
. Zatiaľ čo toto predvolené
rozhranie MÔŽE byť implementované priamo, implementátori (tí, čo
rozhranie implementujú) BY MALI implementovať nadväzujúce
Psr\Http\Message\RequestInterface
a
Psr\Http\Message\ResponseInterface
.
Tu sme si uviedli plnú cestu k rozhraním požiadaviek i odpovedí. Ďalej
budeme Psr\Http\Message
pre prehľadnosť vynechávať.
HTTP hlavičky
Ukážeme si hlavičky case-insensitive, s viacerými hodnotami a tiež hosť header.
Case-insensitive hlavičky
HTTP správy obsahujú case-insensitive hlavičky. Tie sú
vyhľadávané pomocou názvu z tried implementujúcich
MessageInterface
nehľadiac na veľkosť písmen. Výsledok je
rovnaký, či už sa snažíme vyhľadať foo
alebo napríklad
FoO
. Rovnako tak sa hlavička foo
prepíše, ak
neskôr 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“
Aj keď hlavičky môžu byť hľadané bez dôrazu na veľkosť
písmen, pôvodný zápis MUSÍ byť implementácií zachovaný, konkrétne pri
volaní getHeaders().
Niektoré (nie celkom vyhovujúce) HTTP aplikácie môžu závisieť od
veľkosti písmen, takže je pre užívateľov užitočné, aby ju mohol pri
vytváraní HTTP požiadavky presne definovať.
Hlavičky s viacerými hodnotami
Aby bolo možné pracovať s viacerými hodnotami a zároveň zachovať
výhody práce s hlavičkami ako s reťazcami, hlavičky môžu byť získané z
inštancie MessageInterface
ako pole alebo string
.
Použitím metódy getHeaderLine()
získame všetky
(case-insensitive) hodnoty hlavičky zreťazené pomocou
čiarky. Pomocou funkcie getHeader()
získame pole
všetkých hodnôt (opäť case-insensitive):
$message = $message ->withHeader('foo', 'bar') ->withAddedHeader('foo', 'baz'); $header = $message->getHeaderLine('foo'); // $header obsahuje: 'bar,baz' $header = $message->getHeader('foo'); // ['bar', 'baz']
Nie všetky hodnoty odosielané v hlavičkách môžu byť zlúčia pomocou
čiarky, napríklad Set-Cookie
.
Ak pracujeme s týmito konkrétnymi hlavičkami, ako používatelia tried založených naHosť headerMessageInterface
by sme MALI spoliehať skôr nagetHeader()
namiestogetHeaderLine()
.
V otázkach hosť header typicky napodobňuje hosť komponent URI rovnako ako hosť používaný pri nadväzovaní TCP spojenia. Špecifikácie HTTP ale umožňuje tieto dve situácie od seba navzájom rozlíšiť.
Ak pri konštrukcii nie je dodaný hosť header, implementácia sa MUSÍ pokúsiť tento header nastaviť podľa URI.
RequestInterface::withUri()
v predvolenom nastavení nahradí
hosť header požiadavke hosť header zodpovedajúcim hosť komponente
odovzdaného UriInterface.
Je možné zvoliť, aby bol pôvodný stav hosť header zachovaný pomocou
druhého argumentu funkcie ($preserveHost
). Pokiaľ tento argument
nastavíme na TRUE
a správa už nejaký hosť header obsahuje,
požiadavku ho aktualizovať nebude.
Nasledujúca tabuľka ilustruje, čo getHeaderLine('Host')
v
rôznych situáciách vráti na požiadavku vrátený pomocou
withUri()
s argumentom $preserveHost
nastaveným ako
TRUE
.
REQUEST HOST HEADER ★ 1 | REQUEST HOST Komponenty ★ 2 | URI HOST Komponenty ★ 3 | VÝSLEDOK |
---|---|---|---|
"" | "" | "" | "" |
"" | foo.com | "" | foo.com |
"" | foo.com | bar.com | foo.com |
foo.com | "" | bar.com | foo.com |
foo.com | bar.com | baz.com | foo.com |
★ 2 - hosť komponenta URI vytvorená v požiadavke pred operáciou
★ 3 - hosť komponent URI vytvorená pomocou withUri ()
Prúdy (streams)
HTTP správy sa skladajú z tzv. Start-line, hlavičiek a tela. Telo správy môže byť rôznych veľkostí, od jedného riadku až po extrémne obsiahla dáta.
Pokus o reprezentáciu dlhého tela ako reťazce môže jednoducho vyústiť vo väčšej obsadenie pamäti, než bolo pôvodne zamýšľané, pretože v nej musí byť uložené úplne celé telo. Pri takomto pokuse uložiť do pamäti, či už požiadavky alebo odpovede, je znemožnená práca implementácia s telom.
Keď zapisujeme do prúdu dát alebo z neho čítame, sú pomocou rozhrania
StreamInterface
detaily o implementácii schované. V situáciách,
kedy je pre nás implementácia pomocou reťazca výhodná, môžu byť
použité vstavané prúdy ako php://memory
a
php://temp
.
StreamInterface
vystavuje svoje schopnosti pomocou troch
metód:
isReadable()
,isWritable()
aisSeekable()
.
Tieto metódy môžu byť použité ďalšími funkciami (metódami, rozhraniami, ...) pre zistenie, či prúd vyhovuje všetkým nárokom a podmienkam.
Každá inštancia prúdu má rôznorodé schopnosti a môže byť:
- read-only (len pre čítanie)
- write-only (len pre zápis)
- read-write (čítanie aj zápis)
Tiež môžu povoliť náhodný prístup (hľadanie spredu i zozadu), alebo iba sekvenčný prístup (napríklad v prípade socketu, pipe (rúry) alebo callback-based prúdu).
Rozhranie StreamInterface
definuje ešte jednu metódu, a to
__toString()
. Je určená k zjednodušeniu získania alebo
emitovanie celého obsahu tela naraz.
Na rozdiel od ostatných rozhrania týkajúcich sa HTTP správ
StreamInterface
nezabezpečuje nemennosť dát
(immutability).
Ak sú použité tzv. Wrappovací funkcie, nemennosť je nemožné zaistiť, pretože akýkoľvek kód, ktorý pracuje so zdrojom, môže teoreticky zmeniť jeho stav (a to vrátane polohy kurzora, obsahu a mnohých ďalšieho). Autor odporúča, aby používatelia pri implementácii rozhrania používali iba read-only streamy pre serverové požiadavky a otázky klienta.
Používatelia by si mali byť vedomí, že inštancie prúdu môže byť vymeniteľné (mutable) a ako taká môže zmeniť stav správy. Ak sme na pochybách, je vhodné vytvoriť novú inštanciu a priložiť ju k správe, aby sme stav vynútili.
V ďalšej lekcii, Štandardy jazyka PHP - PSR-7 (Rozhranie HTTP správ a prúdov) , sa budeme zaoberať rozhraniami pre dátové prúdy a HTTP správy, jednotlivými funkciami a ich použitím.