IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

5. diel - Štandardy jazyka PHP - Implementácia PSR-3

V minulej lekcii, Štandardy jazyka PHP - PSR-3 a špecifikácie loggeru , sme si predstavili základné myšlienky špecifikácia PSR-3, ktorá sa zaoberá logovaním. Dnešná lekcia je venovaná jej implementáciu.

Balíček

Rozhrania a opísané triedy sú rovnako ako príslušnej triedy výnimiek a testovacie prostredie poskytnuté ako časť balíčka psr / log pre kontrolu vašej implementácie. Nižšie je rozhranie s českými komentármi.

Rozhranie Psr \ Log \ LoggerInterface

<?php

namespace Psr\Log;

/**
 * Popisuje instanci loggeru
 *
 * Zpráva MUSÍ být string nebo objekt, který implementuje __toString().
 *
 * Zpráva MŮŽE obsahovat zástupné identifikátory ve formě {foo}, kde foo
 * bude nahrazeno kontextovými daty pod klíčem "foo".
 *
 * Kontextové pole může obsahovat libovolná data, jediným předpokladem
 * implementátora je, že pokud je předávána instance Exception za účelem
 * produkování stack trace, MUSÍ být v klíči jménem "exception".
 *
 * Pro plnou specifikaci rozhraní navštivte
 * https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 */
interface LoggerInterface
{
    /**
     * Systém je nepoužitelný.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function emergency($message, array $context = array());

    /**
     * Je nutné ihned provést akci.
     *
     * Příklad: Celá stránka je mimo provoz, databáze nedostupná a podobně. Metoda by
     * měla spustit SMS upozornění a vzbudit vás.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function alert($message, array $context = array());

    /**
     * Kritické podmínky.
     *
     * Příklad: Komponenta aplikace je nedostupná, neočekávaná výjimka.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function critical($message, array $context = array());

    /**
     * Běhové chyby, které nevyžadují okamžitou akci, ale měly by být typicky
     * logovány a sledovány.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function error($message, array $context = array());

    /**
     * Výjimečné události, které nejsou chybami.
     *
     * Příklad: Použití zastaralého API, nesprávné použití API, nevhodné věci,
     * které nemusí být nutně špatně.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function warning($message, array $context = array());

    /**
     * Normální, ale podstatné události.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function notice($message, array $context = array());

    /**
     * Zajímavé události.
     *
     * Příklad: Uživatelská přihlášení, SQL logy.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function info($message, array $context = array());

    /**
     * Detailní ladící informace.
     *
     * @param string $message
     * @param array $context
     * @return null
     */
    public function debug($message, array $context = array());

    /**
     * Zaloguje s libovolnou úrovní.
     *
     * @param mixed $level
     * @param string $message
     * @param array $context
     * @return null
     */
    public function log($level, $message, array $context = array());
}

Rozhranie Psr \ Log \ LoggerAwareIn­terface

<?php

namespace Psr\Log;

/**
 * Popisuje instanci, která používá logger
 */
interface LoggerAwareInterface
{
    /**
     * Nastaví objektu instanci loggeru
     *
     * @param LoggerInterface $logger
     * @return null
     */
    public function setLogger(LoggerInterface $logger);
}

Trieda Psr \ Log \ LogLevel

<?php

namespace Psr\Log;

/**
 * Popisuje logovací úrovně
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}

Ukážková implementácia

Preklad špecifikácia je síce pekná vec, však chcelo by to nejaký konkrétny príklad, že? Vytvorme si jednoduchý logger, ktorý zodpovedá špecifikácii PSR-3 a následne si ukážme aj jeho použitie. V kóde nižšie použijeme rovno aj PSR-4, ktorú máme v pláne na nabudúce. Tá udáva akým spôsobom pomenovávať menné priestory, implementovať autoloader a ako členiť triedy do priečinkov. Túto časť kódu teda plne pochytíte až nabudúce.

Ako prvý si založíme nový projekt. Vytvoríme si v ňom zložku vendor, v nej podpriečinok Psr a v nej podpriečinok Log. Práve sem vložíme 3 súbory s vyššie uvedenými zdrojovými kódmi: LoggerAwareIn­terface.php, LoggerInterfa­ce.php, LogLevel.php. Vendor je zložka pre triedy, ktorá sa ďalej vetví podľa ich výrobcov (od toho názov vendor). Rozhranie máme teda pripravená, teraz je implementujeme.

V priečinku vendor vytvoríme podpriečinok ItNetwork (pretože ItNetwork je ich výrobca), kam pridáme 2 triedy:

Logger.php

Logger je samotná implementácia nášho loggeru. Z kódu som odstránil komentáre, aby sa článok kompaktnejší, okomentovanou verziu si môžete stiahnuť v prílohe. Kód je extrémne jednoduchý, každá z 8mi metód zatiaľ len zapisuje riadku do textového súboru. Pripísania riadky je atomická operácie, takže sa nemusíme starať o lockování. Metóda log () potom podľa typu úrovne spustí danú metódu. Tie kritickejší metódy by sa v praxi doplnili o nejakej odosielanie emailov, SMSiek, spúšťanie sirén a podobne :)

namespace ItNetwork;


use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;

class Logger implements LoggerInterface
{
    const FILENAME = 'errors.log';

    public function emergency($message, array $context = array())
    {
        $this->appendLine(LogLevel::EMERGENCY, $message, $context);
    }

    public function alert($message, array $context = array())
    {
        $this->appendLine(LogLevel::ALERT, $message, $context);
    }

    public function critical($message, array $context = array())
    {
        $this->appendLine(LogLevel::CRITICAL, $message, $context);
    }

    public function error($message, array $context = array())
    {
        $this->appendLine(LogLevel::ERROR, $message, $context);
    }

    public function warning($message, array $context = array())
    {
        $this->appendLine(LogLevel::WARNING, $message, $context);
    }

    public function notice($message, array $context = array())
    {
        $this->appendLine(LogLevel::NOTICE, $message, $context);
    }

    public function info($message, array $context = array())
    {
        $this->appendLine(LogLevel::INFO, $message, $context);
    }

    public function debug($message, array $context = array())
    {
        $this->appendLine(LogLevel::DEBUG, $message, $context);
    }

    private function interpolate($message, array $context = array())
    {
        // vytvoří nahrazovací pole se závorkami okolo kontextových klíčů
        $replace = array();
        foreach ($context as $key => $val) {
            $replace['{' . $key . '}'] = $val;
        }
        // interpoluje nahrazovací hodnoty do zprávy a vrátí je
        return strtr($message, $replace);
    }

    private function appendLine($level, $message, $context)
    {
        $message = $this->interpolate($message, $context);
        file_put_contents(self::FILENAME, $level . ': ' . $message . PHP_EOL, FILE_APPEND);
    }

    public function log($level, $message, array $context = array())
    {
        switch ($level)
        {
            case LogLevel::EMERGENCY:
                $this->emergency($message, $context);
                break;
            case LogLevel::ALERT:
                $this->alert($message, $context);
                break;
            case LogLevel::CRITICAL:
                $this->critical($message, $context);
                break;
            case LogLevel::ERROR:
                $this->error($message, $context);
                break;
            case LogLevel::WARNING:
                $this->error($message, $context);
                break;
            case LogLevel::NOTICE:
                $this->notice($message, $context);
                break;
            case LogLevel::INFO:
                $this->info($message, $context);
                break;
            case LogLevel::DEBUG:
                $this->debug($message, $context);
                break;
        }
    }

}

EmailSender.php

Následne vytvorme objekt, ktorý logger používa. Pôjde o jednoduchý kódový emailov, ktorý zaloguje notice ak sa email nepodarí odoslať. Objekt bude implementovať rozhranie LoggerAwareIn­terface.

namespace ItNetwork;

use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;

class EmailSender implements LoggerAwareInterface
{

    private $logger;

    public function send($address, $subject, $message, $from)
    {
        $header = "From: " . $from;
        $header .= "\nMIME-Version: 1.0\n";
        $header .= "Content-Type: text/html; charset=\"utf-8\"\n";
        if (!mb_send_mail($address, $subject, $message, $header))
            $this->logger->notice('Email na adresu {address} se nepodařilo odeslat.', array('address' => $address));
    }

    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
}

No a konečne do koreňového priečinka (nad vendor) pridáme index.php, kam umiestnime autoloader, vytvoríme Logger a EmailSender, EmailSenderu nastavíme Logger a pokúsime sa odoslať email na nejakú neexistujúcu adresu:

use ItNetwork\EmailSender;
use ItNetwork\Logger;

function autoloader($class)
{
    $class = 'vendor\\' . $class;
    $path = str_replace('\\', '/', $class) . '.php';
    if (!include($path))
        throw new Exception('Autoloader Error'); // Vyhodíme výjimku, abychom zjistili kde nastala chyba
}

spl_autoload_register("autoloader");

$logger = new Logger();
$emailSender = new EmailSender();
$emailSender->setLogger($logger);
$emailSender->send('neexistuje', 'Žádný', 'Ahoj, toto nikdy nebudeš číst.', '[email protected]');

Skript niekoľkokrát spustíme. Výsledkom je vytvorenie súboru errors.txt, ktorý má nasledujúci obsah:

Logovanie podľa špecifikácie PSR-3 v PHP - Štandardy jazyka PHP

Máme univerzálny logger, ktorý môžeme centrálne používať nielen zo svojich tried, ale dokonca ho budú vďaka rozhraniu Psr \ Log \ LoggerInterface vedieť používať aj komponenty tretích strán, ktoré ho nikdy nevideli :)

Budúci lekcie, Štandardy jazyka PHP - PSR-4 a autoloader , je venovaná špecifikáciu PSR-4, ktorá sa týka Autoloader.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 99x (6.64 kB)
Aplikácia je vrátane zdrojových kódov v jazyku PHP

 

Predchádzajúci článok
Štandardy jazyka PHP - PSR-3 a špecifikácie loggeru
Všetky články v sekcii
Štandardy jazyka PHP
Preskočiť článok
(neodporúčame)
Štandardy jazyka PHP - PSR-4 a autoloader
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity