7. diel - Štandardy jazyka PHP - PSR-6 (cachovací rozhranie)
Cache (vyslovujeme [keš], v češtine cache) je bežne využívaná k zrýchleniu všetkých aplikácií a projektov. Vďaka tomu sú cachovací systémy jednou z najbežnejších súčasťou všetkých frameworkov. Mnoho knižníc si tak tvoria aj svoje vlastné cachovací systémy s rôznym stupňom funkcionality. Kvôli rozdielom potom vývojári musia mať znalosti o viacerých systémoch súčasne, z ktorých niektoré môžu, ale aj nemusia podporovať zrovna tie funkcie, ktoré potrebujú. Zároveň samotní developeri cachovacích knižníc musí voliť medzi podporou obyčajné časti frameworkov a veľkým množstvom nadbytočných tried umožňujúcich kompatibilitu.
PSR-6: Caching Interface
Tieto problémy by malo vyriešiť práve spoločné rozhranie, ktoré PSR-6 definuje. Vývojári potom môžu očakávať rovnaké výsledky od rôznych systémov a tie naopak musia implementovať len niekoľko rozhraní namiesto celej škály prídavných tried.
Kľúčové koncepty
Najskôr začneme u kľúčových konceptov.
Pool
Pool alebo fond reprezentuje kolekciu prvkov v cachovacím systéme. Všetky cachovatelné prvky sú z poolu vrátené ako objekty a akákoľvek interakcie s týmito objektmi musí prebiehať za pomocou poolu.
Items
Item (prvok) reprezentuje objekt alebo hodnotu v poole, ktorému zodpovedá kľúč. Tento kľúč je primárnym a unikátnym identifikátorom a MUSÍ byť nezmeniteľný. Hodnota ale MÔŽE byť zmenená.
Definícia
V štandarde sú použité pojmy, ktoré si najskôr vysvetlíme.
Calling Library / Volajúci knižnica
Calling Library je knižnica alebo časť kódu, ktorá využíva cache, ale o vnútorné implementácii cachovacích systémov nemá žiadne informácie.
Implementing Library / národné implementačné knižnica
Implementing Library implementuje štandard na účely dodania cachovacích
systémov pre volajúcich knižnice. MUSÍ dodať rozhranie
Cache\CacheItemPoolInterface
a
Cache\CacheItemInterface
a MUSÍ podporovať aspoň TTL
funkcionalitu. Čo jednotlivé rozhrania definujú si vysvetlíme na konci
článku v samostatnej kapitole.
TTL (The Time To Live) / Doba životnosti
TTL je čas, po ktorý je objekt braný ako platný. Ide o dobu od vzniku objektu až po jeho zánik.
Expirácie
Expirácie je naozajstný čas, kedy vyprší doba životnosti objektu. Väčšinou sa počíta pridaním TTL k času, kedy prvok vznikol, ale môže byť zmenený. Národné implementačné knižnice MÔŽU dobu životnosti objektu skrátiť, ale MUSÍ sa k prvku správať ako k neplatnému, ak príslušný čas už vypršal. Národné implementačné knižnice MÔŽU použiť predvolené nastavenie TTL, ak ale žiadne neexistuje, MUSÍ prvok uložiť do cache navždy (kým je to možné).
Kľúč
Kľúč je reťazec o minimálne jednom znaku, ktorý
je unikátny pre daný prvok vo vyrovnávacej pamäti.
Národné implementačné knižnice MUSÍ podporovať kľúče v
UTF-8
kódovanie až do dĺžky 64 znakov, ktoré
obsahujú veľké a malé písmená, celé čísla, podčiarkovník a bodku v
akomkoľvek poradí. Podporu ďalších znakov alebo dlhších reťazcov MÔŽU
pridať. NEMÔŽU však pridať podporu znakov {}
, ()
,
/
, \
, @
a :
, pretože tie
sú vyhradené pre budúce potreby a rozšírenia. Zároveň sú národné
implementačné knižnice zodpovedné za svoje vlastné escape sekvencie. MUSÍ
byť ale schopné vrátiť pôvodné kľúč bez
modifikácií.
Hit a miss
Ak sa volajúci knižnica pokúsi vyhľadať prvok podľa kľúča a je
nájdený, má platnú dobu životnosti a dáta nie sú žiadnym spôsobom
poškodená, jedná sa o tzv. Cache hit. V opačnom prípade sa
jedná o tzv. Cache miss, dokonca aj keď je len prekročená
doba životnosti. Volajúci knižnice by MALI overiť isHit()
v
každom volaní get()
.
Odloženie (deferred)
Odložené uloženie do vyrovnávacej pamäte poukazuje na skutočnosť, že
prvok nemusí byť ihneď permanentne uložený poolom (napríklad do
databázy). Pool MÔŽE pozdržať uloženie prvku, aby mohli byť využité
hromadné operácie podporované niektorými pamäťovými modulmi. Pool MUSÍ
zabezpečiť, že akýkoľvek pozdržané prvok je nakoniec uložený a dáta
nie sú stratené. MÔŽE je tiež uložiť predtým, ako o to požiada
volajúci knižnica. Keď volajúci knižnica použije metódu
commit()
, všetky zostávajúce odložené prvky MUSÍ byť
uložené. Národné implementačné knižnica MÔŽE použiť akúkoľvek
logiku, aby určila, kedy odložené prvky uložiť (napríklad destruktor,
vytvorenie všetkých naraz pri zavolaní save()
, maximálneho
počtu prvkov). Žiadosť o prvok vo vyrovnávacej pamäti, ktorá bola
odložená, MUSÍ vrátiť odložený, ale ešte neuložený, prvok.
Dáta
Všetky knižnice MUSÍ podporovať všetky serializovatelný dátové typy (tie, ktoré je možné ukladať a prenášať priamo):
String
- reťazca znakov o náhodné dĺžke v akomkoľvek formátovania, ktoré PHP podporujeInteger
- všetky celé čísla až do 64-bit signed (= záporné aj kladné čísla)Float
- čísla s desatinnou čiarkou až do veľkosti 64-bit signed (= záporné aj kladné čísla)Boolean
- pravda / nepravda (true
/false
)Null
- prázdna hodnotaArray
- indexované polia, vrátane multidimenzionálnych polí o ľubovoľnej dimenziiObject
- akýkoľvek objekt, ktorý podporuje bezstratovú (de) serializáciu, napr.$o == unserialize(serialize($o)
. Objekty MÔŽU využívať dva tzv. Magických metód__sleep()
a__wakeup()
alebo podobných funkcionalít jazyka, ak je potreba .
Ak nie je možné presne vrátiť uloženú hodnotu z akéhokoľvek dôvodu, tak MUSÍ knižnice odpovedať pomocou cache miss namiesto poškodenými dátami.
Zachytávanie chýb
Hoci cachovanie nemalým dielom prispieva k výkonnosti aplikácie, nemalo by nikdy byť nepostrádateľnou súčasťou. Preto by chyba v cachovacím systéme NEMALA vyústiť v zlyhaní aplikácie. Z tohto dôvodu NESMIE národné implementačné knižnice hádzať iné výnimky, než aké sú definované v rozhraní. Tiež BY MALI zachytiť akékoľvek chyby alebo výnimky a neumožniť im "vybuble".
Národné implementačné knižnice BY MALI tieto chyby logovať alebo je inak odovzdať na spracovanie administrátorom.
Ak volajúci knižnica požiada o zmazanie jedného alebo viacerých prvkov, alebo o zmazanie celého poolu, NESMIE vyústiť v chybu, ak hľadaný prvok neexistuje. Koncová podmienka je rovnaká (pool je prázdny a prvok je zmazaný - kľúč neexistuje), a preto nie je dôvod na vyvolanie chyby.
Rozhranie
V priebehu bola spomenutá rôzne rozhrania. Čo vlastne pre štandard znamenajú?
CacheItemInterface
Definuje prvok v cachovacím systéme. Každý prvok MUSÍ byť spojený so
špecifickým kľúčom, ktorý môže byť priradený na základe
implementujícího systému a je zvyčajne odovzdávaný pomocou
Cache\CacheItemPoolInterface
objektu.
Objekt Cache\CacheItemInterface
zapuzdruje ukladanie a
vyvolávanie nacachovaných prvkov a je generovaný pomocou objektu
Cache\CacheItemPoolInterface
, ktorý je zároveň zodpovedný za
prvotné nastavenie a priradenie kľúčov. MUSÍ byť schopný uložiť a
vrátiť akýkoľvek serializovatelný dátový typ (viď sekcia dáta na
začiatku článku).
Volajúci knižnice NESMIE vytvárať inštancie prvkov. Môžu je len volať
z poolu pomocou metódy getItem()
. Zároveň by NEMALI
predpokladať, že prvok vytvorený pomocou jednej národné implementačné
knižnice je kompatibilný s poolom iné.
Rozhranie CacheItemInterface
môže vyzerať napríklad
takto:
<?php namespace Psr\Cache; interface CacheItemInterface { public function getKey(); /** * Vrací klíč pro cachovaný prvek * * Klíč je poskytnut implementující knihovnou, ale měl by být * k dispozici ostatním volajícím, pokud je zapotřebí * * @return string * Klíč */ public function get(); /** * Získá hodnotu prvku z cache spojené s daným klíčem * * Vrácená hodnota musí být identická s hodnotou uloženou pomocí * set() * Pokud isHit() vrátí false, tato metoda MUSÍ vracet null * Null však může být uložená hodnota, proto musí rozlišovat * mezi „nic nenalezeno“ a „nalezeno null“ * * @return mixed */ public function isHit(); /** * Potvrdí, jestli hledání skončilo jako cache hit * NESMÍ nastat souběh mezi voláním isHit() a get() * * @return bool * Pokud se jedná o cache hit, vrací true, jinak false */ public function set($value); /** * Uloží hodnotu reprezentovanou cachovaným prvkem * * @param mixed $value * Serializovatelná hodnota, kterou chceme uložit * * @return static */ public function expiresAt($expiration); /** * Nastaví expiraci pro daný prvek NA určitou dobu * * @param \DateTimeInterface|null $expiration * Doba, kdy MUSÍ být prvek považován za expirovaný * Pokud nastavíme null, MŮŽE být použita výchozí hodnota * nebo prvek uložíme po maximální možnou dobu * * @return static * Volaný objekt */ public function expiresAfter($time); /** * Nastaví expiraci, ale PO určité době * * @param int|\DateInterval|null $time * Předáváme integer, který reprezentuje čas ve vteřinách * nebo null jako v předchozí funkci * * @return static */ }
CacheItemPoolInterface
Hlavným účelom tohto rozhrania je prijímať kľúče od volajúci knižnice a vracať objekty s nimi spojené. Zároveň funguje ako vstupný bod pre interakciu s celou cache kolekcií. Akákoľvek inicializácia a konfigurácia poolu je prenechaná národné implementačné knižnici.
Ako príklad rozhranie CacheItemPoolInterface
si môžeme
uviesť potrebné toto:
<?php namespace Psr\Cache; /** * CacheItemPoolInterface vytváří CacheItemInterface objekty */ interface CacheItemPoolInterface { public function getItem($key); /** * Vrací prvek reprezentující daný klíč * * MUSÍ vracet CacheItemInterface objekt, a to i v případě * cache miss. NESMÍ vracet null * * @param string $key * Klíč hledaného objektu * * @throws InvalidArgumentException * V případě neplatného klíče MUSÍ být vyhozena * \Psr\Cache\InvalidArgumentException * * @return CacheItemInterface */ public function getItems(array $keys = array()); /** * Vrací sadu traversable (možné iterovat pomocí foreach) prvků * @param string[] $keys * Pole klíčů * * @throws InvalidArgumentException * Pokud i jeden klíč je neplatný, MUSÍ být hozena * \Psr\Cache\InvalidArgumentException * * @return array|\Traversable * Traversable kolekce cachovaných prvků i s jejich klíči * Cachovaný prvek bude vrácen pro každý klíč, i když nebyl * nalezen * Pokud nespecifikujeme žádný klíč, MUSÍ být vrácena prázdná * traversable kolekce */ public function hasItem($key); /** * Potvrdí, jestli cache obsahuje hledaný prvek * * Tato metoda může kvůli výkonu pozdržet získání hodnoty. * To by mohlo vyústit v souběh s metodou get(). * Abychom se této situaci vyhnuli, je doporučeno používat * spíš isHit(). * * @param string $key * * @return bool * True, pokud prvek existuje, jinak false */ public function clear(); /** * Odstraní všechny prvky v poolu * * @return bool * True, jestliže byla operace úspěšná */ public function deleteItem($key); /** * Odstraní prvek z poolu * * @param string $key * Klíč prvku * * @throws InvalidArgumentException * Pokud klíč není platný, MUSÍ být vyhozena * \Psr\Cache\InvalidArgumentException * * @return bool * True, pokud byl prvek úspěšně odstraněn */ public function deleteItems(array $keys); /** * Odstraní více prvků z poolu * * @param string[] $keys * Pole klíčů prvků, které mají být odstraněny * * @throws InvalidArgumentException * Pokud jakýkoliv klíč není platný * * @return bool */ public function save(CacheItemInterface $item); /** * Okamžitě uloží cachovaný prvek (př. do databáze) * * @param CacheItemInterface $item * Prvek, který chceme uložit * * @return bool * True, pokud byla operace úspěšná, jinak false */ public function saveDeferred(CacheItemInterface $item); /** * Určí, že má být prvek uložen později * * @param CacheItemInterface $item * Prvek, který má být uložen * * @return bool * False, pokud prvek nemohl být zařazen do fronty, nebo pokud pokus o uložení selhal. Jinak true. */ public function commit(); /** * Uloží pozdržené prvky * * @return bool * True, pokud byly všechny prvky úspěšně uloženy, * nebo nebyly žádné, které by na uložení čekaly. */ }
CacheException
Je určené pre zachytávanie kritických chýb ako napríklad pri pripojení ku cache servera alebo overovania. Akákoľvek výnimka hodená národné implementačné knižnicou MUSÍ mať toto rozhranie.
InvalidArgumentException
Rozhranie pre neplatné cache argumenty implementuje rozhranie
CacheException
:
<?php namespace Psr\Cache; /** * Rozhraní pro neplatné cache argumenty * * Kdykoliv je metodám předán neplatný argument, musí být hozena * výjimka, kterou implementuje * Psr\Cache\InvalidArgumentException. */ interface InvalidArgumentException extends CacheException { }
V ďalšej lekcii, Štandardy jazyka PHP - PSR-7 (Všeobecná špecifikácie a prúdy) , sa zameriame na štandard PSR-7, ktorý sa zaoberá rozhraním pre HTTP komunikáciu.