Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 7 - Standardy jazyka PHP - PSR-6 (cachovací rozhraní)

Předchozí lekce, Standardy jazyka PHP - PSR-4 a autoloader, byla věnována specifikaci PSR-4, která se týka autoloaderu.

Cache (vyslovujeme [keš], v češtině mezipaměť) je běžně využívána ke zrychlení všech aplikací a projektů. Díky tomu jsou cachovací systémy jednou z nejběžnějších součástí všech frameworků. Mnoho knihoven si tak tvoří i své vlastní cachovací systémy s různým stupněm funkcionality. Kvůli rozdílům pak vývojáři musí mít znalosti o více systémech zároveň, z nichž některé můžou, ale také nemusí podporovat zrovna ty funkce, které potřebují. Zároveň samotní developeři cachovacích knihoven musí volit mezi podporou pouhé části frameworků a velkým množstvím nadbytečných tříd umožňujících kompatibilitu.

PSR-6: Caching Interface

Tyto problémy by mělo vyřešit právě společné rozhraní, které PSR-6 definuje. Vývojáři pak můžou očekávat stejné výsledky od různých systémů a ty naopak musí implementovat jenom několik rozhraní namísto celé škály přídavných tříd.

Klíčové koncepty

Nejdříve začneme u klíčových konceptů.

Pool

Pool nebo fond reprezentuje kolekci prvků v cachovacím systému. Všechny cachovatelné prvky jsou z poolu vraceny jako objekty a jakákoliv interakce s těmito objekty musí probíhat za pomocí poolu.

Items

Item (prvek) reprezentuje objekt nebo hodnotu v poolu, kterému odpovídá klíč. Tento klíč je primárním a unikátním identifikátorem a MUSÍ být nezměnitelný. Hodnota ale MŮŽE být změněna.

Definice

Ve standardu jsou použity pojmy, které si nejdříve vysvětlíme.

Calling Library / Volající knihovna

Calling Library je knihovna nebo část kódu, která využívá cache, ale o vnitřní implementaci cachovacích systémů nemá žádné informace.

Implementing Library / Implementující knihovna

Implementing Library implementuje standard za účelem dodání cachovacích systémů pro volající knihovny. MUSÍ dodat rozhraní Cache\CacheItemPoolInterface a Cache\CacheItemInterface a MUSÍ podporovat alespoň TTL funkcionalitu. Co jednotlivá rozhraní definují si vysvětlíme na konci článku v samostatné kapitole.

TTL (The Time To Live) / Doba životnosti

TTL je čas, po který je objekt brán jako platný. Jedná se o dobu od vzniku objektu až po jeho zánik.

Expirace

Expirace je opravdový čas, kdy vyprší doba životnosti objektu. Většinou se počítá přidáním TTL k času, kdy prvek vznikl, ale může být změněn. Implementující knihovny MOHOU dobu životnosti objektu zkrátit, ale MUSÍ se k prvku chovat jako k neplatnému, pokud příslušný čas již vypršel. Implementující knihovny MOHOU použít výchozí nastavení TTL, pokud ale žádné neexistuje, MUSÍ prvek uložit do cache napořád (dokud je to možné).

Klíč

Klíč je řetězec o minimálně jednom znaku, který je unikátní pro daný prvek v mezipaměti. Implementující knihovny MUSÍ podporovat klíče v UTF-8 kódování až do délky 64 znaků, které obsahují malá a velká písmena, celá čísla, podtržítko a tečku v jakémkoliv pořadí. Podporu dalších znaků nebo delších řetězců MOHOU přidat. NEMOHOU však přidat podporu znaků {}, (), /, \, @ a :, protože ty jsou vyhrazeny pro budoucí potřeby a rozšíření. Zároveň jsou implementující knihovny zodpovědné za své vlastní escape sekvence. MUSÍ být ale schopny vrátit původní klíč bez modifikací.

Hit a miss

Pokud se volající knihovna pokusí vyhledat prvek podle klíče a je nalezen, má platnou dobu životnosti a data nejsou žádným způsobem poškozena, jedná se o tzv. cache hit. V opačném případě se jedná o tzv. cache miss, dokonce i když je pouze překročena doba životnosti. Volající knihovny by MĚLY ověřit isHit() v každém volání get().

Odložení (deferred)

Odložené uložení do mezipaměti poukazuje na skutečnost, že prvek nemusí být ihned permanentně uložen poolem (například do databáze). Pool MŮŽE pozdržet uložení prvku, aby mohly být využity hromadné operace podporované některými paměťovými moduly. Pool MUSÍ zajistit, že jakýkoliv pozdržený prvek je nakonec uložen a data nejsou ztracena. MŮŽE je také uložit předtím, než o to požádá volající knihovna. Když volající knihovna použije metodu commit(), všechny zbývající odložené prvky MUSÍ být uloženy. Implementující knihovna MŮŽE použít jakoukoliv logiku, aby určila, kdy odložené prvky uložit (například destruktor, vytvoření všech najednou při zavolání save(), maximálního počtu prvků). Žádost o prvek v mezipaměti, která byla odložena, MUSÍ vrátit odložený, ale ještě neuložený, prvek.

Data

Všechny knihovny MUSÍ podporovat všechny serializovatelné datové typy (ty, které lze ukládat a přenášet přímo):

  • String – řetězce znaků o nahodilé délce v jakémkoliv formátování, které PHP podporuje
  • Integer – všechna celá čísla až do 64-bit signed (= záporné i pozitivní čísla)
  • Float – čísla s desetinnou čárkou až do velikosti 64-bit signed (= záporné i pozitivní čísla)
  • Boolean – pravda/nepravda (true/false)
  • Null – prázdná hodnota
  • Array – indexované pole, včetně multidimenzio­nálních polí o libovolné dimenzi
  • Object – jakýkoliv objekt, který podporuje bezeztrátovou (de)serializaci, např. $o == unserialize(serialize($o). Objekty MOHOU využívat dvou tzv. magických metod __sleep() a __wakeup() nebo podobných funkcionalit jazyka, pokud je potřeba.

Pokud není možné přesně vrátit uloženou hodnotu z jakéhokoliv důvodu, tak MUSÍ knihovny odpovědět pomocí cache miss namísto poškozenými daty.

Zachytávání chyb

Ačkoliv cachování nemalým dílem přispívá k výkonnosti aplikace, nemělo by nikdy být nepostradatelnou součástí. Proto by chyba v cachovacím systému NEMĚLA vyústit v selhání aplikace. Z tohoto důvodu NESMÍ implementující knihovny házet jiné výjimky, než jaké jsou definovány v rozhraní. Také BY MĚLY zachytit jakékoliv chyby nebo výjimky a neumožnit jim „vybublat“.

Implementující knihovny BY MĚLY tyto chyby logovat anebo je jinak předat ke zpracování administrátorům.

Pokud volající knihovna zažádá o smazání jednoho nebo více prvků, nebo o smazání celého poolu, NESMÍ vyústit v chybu, pokud hledaný prvek neexistuje. Koncová podmínka je stejná (pool je prázdný a prvek je smazán – klíč neexistuje), a proto není důvod k vyvolání chyby.

Rozhraní

V průběhu byla zmíněna různá rozhraní. Co vlastně pro standard znamenají?

CacheItemInterface

Definuje prvek v cachovacím systému. Každý prvek MUSÍ být spojen se specifickým klíčem, který může být přiřazen na základě implementujícího systému a je obvykle předáván pomocí Cache\CacheItemPoolInterface objektu.

Objekt Cache\CacheItemInterface zapouzdřuje ukládání a vyvolávání nacachovaných prvků a je generován pomocí objektu Cache\CacheItemPoolInterface, který je zároveň zodpovědný za prvotní nastavení a přiřazení klíčů. MUSÍ být schopen uložit a vrátit jakýkoliv serializovatelný datový typ (viz sekce data na začátku článku).

Volající knihovny NESMÍ vytvářet instance prvků. Mohou je pouze volat z poolu pomocí metody getItem(). Zároveň by NEMĚLY předpokládat, že prvek vytvořený pomocí jedné implementující knihovny je kompatibilní s poolem jiné.

Rozhraní CacheItemInterface může vypadat napří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
     */
}

CacheItemPoolIn­terface

Hlavním účelem tohoto rozhraní je přijímat klíče od volající knihovny a vracet objekty s nimi spojené. Zároveň funguje jako vstupní bod pro interakci s celou cache kolekcí. Jakákoliv inicializace a konfigurace poolu je přenechána implementující knihovně.

Jako příklad rozhraní CacheItemPoolInterface si můžeme uvést třeba 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čeno pro zachytávání kritických chyb jako například při připojení ke cache serveru nebo ověřování. Jakákoliv výjimka hozená implementující knihovnou MUSÍ mít toto rozhraní.

InvalidArgumen­tException

Rozhraní pro neplatné cache argumenty implementuje rozhraní 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 další lekci, Standardy jazyka PHP - PSR-7 (Obecná specifikace a proudy), se zaměříme na standard PSR-7, který se zabývá rozhraním pro HTTP komunikaci.


 

Předchozí článek
Standardy jazyka PHP - PSR-4 a autoloader
Všechny články v sekci
Standardy jazyka PHP
Přeskočit článek
(nedoporučujeme)
Standardy jazyka PHP - PSR-7 (Obecná specifikace a proudy)
Článek pro vás napsala Miroslava Škutová
Avatar
Uživatelské hodnocení:
1 hlasů
.
Aktivity