PSR-11: Standard pro DI kontejner

Publikováno PHP

Je to pár dní, co byl oficiálně akceptován návrh dalšího ze standardů skupiny PHP-FIG a to interface pro DI (dependecy injection) kontejner. Oficiální znění naleznete na GitHubu a já jsem se rozhodl, že jej zde trochu popíšu, a že se ho budu v komponentě gephart/dependency-injection držet.

Co standard říká?

  1. Identifikátor, podle kterého je položka (objekt) vytahován z kontejneru, musí být řetězec o délce alespoň jednoho znaku. Neměl by mít žádný sémantický význam.
  2. Kontejner musí mít dvě veřejné metody:
    1. get() – má jeden povinný parametr a to identifikátor položky
      – pokud identifikátoru neodpovídá žádná z položek, musí být vyhozena výjimka implementující Psr\Container\NotFoundExceptionInterface (taktéž součástí standardu)
    2. has() – má opět jeden povinný parametr – identifikátor položky
      – vrátí false v případě, kdy by metoda get() vyhodila zmíněnou výjimku
  3. Výjimky vyhozené kontejnerem by měli vyhazovat výjimku implementující Psr\Container\ContainerExceptionInterface.
  4. Doporučuje nepoužívat kontejner jako service locator (doporučuji článek od Davida Grudla). Ve zkratce to znamená, že obecné doporučení je nemít závislost na samotném kontejneru, jen na jednotlivých službách. (Pozor na to, že třeba třída Router, která je běžně závislá na DI kontejneru a vytahuje z něj controller, tak opravdu není závislá na samotném controlleru, a proto to zde nevadí.)

Pokud jste se rozhodli, že se budete držet standardu, je doporučené implementovat interface (kontejneru a výjimek) z toho balíčku: psr/container-implementation.

Číst celé

PHP: Jak předávám konfiguraci objektům?

Publikováno PHP

Jestli jste četli jeden z mých předchozích článků, kde jsem publikoval komponentu pro Dependency Injection container (který počítá s tím, že třída v konstruktoru definuje veškeré závislosti), asi nejednoho z vás napadlo, jak předat například konfiguraci pro připojení k databázi.

Mé řešení je takové, že jako závislost požaduji specifický typ konfigurace jako objekt, který je závislý na obecné konfiguraci. Pokud to uvedu na konkrétním případu, že máme třídu Database, která (v rámci příkladu) požaduje v konstruktoru parametry $server, $user, $password, $port a potřebuji, aby byla použitelná v rámci DI kontejeneru, musí záviset na objektu DatabaseConfiguration.

Tedy kód přepíši z:

class Database {
    public function __contructor($server, $user, $password, $port) {
        // ...
    }
}

Na:

class Database {
    public function __contructor(DatabaseConfiguration $database_configuration) {
        $server = $database_configuration->get("server");
        $user = $database_configuration->get("user");
        $password = $database_configuration->get("password");
        $port = $database_configuration->get("port");
        // ...
    }
}

Je to na první pohled více psaní, ale získám tím navíc možnost, měnit počet potřebných parametrů, aniž bych musel měnit závislosti konstruktoru i veškeré inicializace třídy, kde parametry běžně předávám.

Na třídu DatabaseConfiguration nyní přešla zodpovědnost za to, že konfigurace obsahuje všechny potřebné parametry.

class DatabaseConfiguration {
    private $database_configuration;

    public function __contructor(Configuration $configuration) {
        $must_have_parameters = ["server", "user", "password", "port"];
        $database_configuration = $configuration->get("database");

        foreach ($must_have_parameters as $must_have_parameter) {
            if (!isset($database_configuration[$must_have_parameter]) {
                throw new \Exception("Missing parameter in database configuration: '" . $must_have_parameter . "'.");
            }
        }

        $this->database_configuration = $database_configuration;
    }
   
    public function get(string $key) {
        return isset($database_configuration[$key]) ? $database_configuration[$key] : false;
    }
}

Třída Configuration je v mojí nové komponentě, které se stará o to, že zpracuje konfigurační soubory. Podrobnější dokumentace o tom, jak Configuration použít je popsaná na github.com/gephart/configuration.

Číst celé

PHP: Event Manager

Publikováno PHP

Další z dlouhého seznamu komponent, které postupně publikuji je Event Manager. De facto se jedná o implementaci návrhu standardu, který vytváří skupina PHP-FIG.

Z principu jde o navázání různých volání na určitou událost. Je to velmi užitečná věc, díky které lze velmi zpřehlednit kód. Uvedu známý případ, kdy máme objednávku a po jejím dokončení se odešle potvrzující e-mail. Místo, abychom e-mail odeslali přímo v kódu, kde se objednávka dokončuje, vyvoláme událost „košík odeslán“. Event Manager se podívá, jaké listenery jsou na tuto událost registrovány a následně se zavolají.
Continue reading “PHP: Event Manager” »

Číst celé

Vlastní DI container s autowiringem

Publikováno PHP

Už to slyším… „Panebože, proč vytváříš něco, co tu je už hotové stokrát!“ Ale počkejte, mám k tomu pár důvodů.

Programátorům užívajících si pokročilejších frameworků jsou pojmy Dependency Injection a autowiring známý, a třeba jej i používají. Pro ty, co je neznají, doporučuji načíst :)

V rámci svého (doma provozovaného) sebevzdělávacího programu jsem se rozhodl, pokusit se, vytvořit si vlastní framework. Jedním z hlavních důvodů, proč se do něčeho takového vůbec pouštím je: naučit se něco nového, nikoli přijít s něčím naprosto revolučním.

Jakýsi návrh a první verzi mám již hotovou a přišlo mi přínosné si popsat, jak jednotlivé komponenty fungují a zveřejnit je, abych se okolní kritikou mohl něco přiučit. Začínám tedy s komponentou Dependency Injection kontejneru s automatickým autowiringem.

Continue reading “Vlastní DI container s autowiringem” »

Číst celé