Lepszy var_dump czyli przyjemniejsze debugowanie PHP

Podziel się z innymi!

    bigWeb/Debug/Dumper to narzędzie funkcjonalnie odpowiadające funkcji var_dump
    Jego przewagą jest sposób prezentacji danych, a także dodatkowe informacje
    ułatwiające debugowanie aplikacji.

    Najpoważniejszą wadą Dumpera jest to, że jest on dość obciążający dla aplikacji
    gdyż uzyskanie informacji o pliku i linii, w której dump został wywołany
    wymaga każdorazowo rzucenia wyjątku. Dlatego też w wersji produkcyjnej
    Dumper powinien być wyłączony

    UWAGA! Biblioteka zaprezentowana w przykładach wymaga min PHP 5.3
    z uwagi na użycie przestrzeni nazw.
    Ponieważ jednak nie na wszystkich serwerach jest już PHP w wersji obsługującej
    przestrzenie nazw przygotowałem także wersję Dumpera nie wymagającą ich użycia.
    w takim przypadku wywołanie bigWeb\Debug\Dumper::factory(); należy zastąpić
    poprzez wywołanie bigWeb_Debug_Dumper::factory(); i analogicznie w przypadku
    innych klas. Wersja Dumpera dla PHP < 5.3 zawarta jest w archiwum zip bigWeb\Debug\Dumper do ściągnięcia.

    Instalacja

    include_once('./Debug.php');

    Można też użyć autoloadera. Wszystkie klasy potrzebne do działania Dumpera
    zdefiniowane są w pliku „Debug.php”. Wyjątkiem jest FirePHP, który należy
    dodać osobno jeśli chcemy wyświetlać dane w konsoli javascript

    include_once('./FirePHP.php');

    Podstawowe użycie

    dump('some data');

    w wyniku otrzymamy:

    TIME: 20:46:44 FILE: /home/www/bigWeb/Debug/example.php LINE: 37 ET: 0 MU: 1.44 mb MPU: 1.67 mb

    ‚some data’
    string(9) "some data"
    

    Dumpa można wywołać z wieloma parametrami o różnych wartościach równocześnie.

    $str = 'text';
    $int = 7;
    $arr = array('foo', 'bar');
    $ob = new ArrayObject();
    $bool = FALSE;
     
    dump($str, $int, $arr, $ob, $bool);
    TIME: 20:46:44 FILE: /home/www/bigWeb/Debug/example.php LINE: 47 ET: 0.000904 MU: 1.47 mb MPU: 1.67 mb

    $str
    string(4) "text"
    
    $int
    int(7)
    
    $arr
    array(2) {
      [0]=>
      string(3) "foo"
      [1]=>
      string(3) "bar"
    }
    
    $ob
    object(ArrayObject)#3 (1) {
      ["storage":"ArrayObject":private]=>
      array(0) {
      }
    }
    
    $bool
    bool(false)
    

    Ukrywanie komunikatów

    Aby zapobiec wyświetlaniu jakichkolwiek komunikatów przez dumpera należy go wyłączyć.

    bigWeb\Debug\Dumper::setEnabled(FALSE);
    dump('it should not show');

    Aby ponownie włączyć:

    bigWeb\Debug\Dumper::setEnabled(TRUE);

    Definiowanie alternatywnych logerów

    Domyślnie dumper do zrzutu danych używa wbudowanej funkcji var_dump jednak
    klasa Dumpera jest zbudowana w oparciu o wzorzec projektowy Obserwator, gdzie
    obserwatorami są wyspecjalizowane klasy do logowania i prezentacji zrzucanych danych.
    Dzięki temu możemy wybierać sposób logowania informacji.

    Możemy zapisywać dane w pliku – przydatne np. przy testowaniu przekierowań

    $d = bigWeb\Debug\Dumper::factory();
    $o = new bigWeb\Debug\Dumper\FileDump();

    Koniecznym jest wskazanie katalogu w którym będą zapisywane logi.
    Katalog ten musi mieć oczywiście ustawione prawa do zapisu

    $o->setDir(dirname(__FILE__));
    $d->attach($o);
    $d->notify('foo');

    wynik zostanie zapisany w pliku /home/www/bigWeb/Debug/2012-01-12.log.php

    TIME: 20:46:18 FILE: /home/zh/www/Debug/example.php LINE: 71 ET: 0.001143 MU: 1.55 mb MPU: 1.67 mb
    ----------| 'foo' |----------
    foo
    .----------------------------
    

    Można też skorzystać z dobrodziejstw FireBuga – dodatku do Firefoxa i
    wyświetlać dane w konsoli javascript. W tym przypadku musimy najpierw
    załadować bibliotekę FirePHP będącej „pomostem” pomiędzy PHP i FireBugiem.

    include_once('./FirePHP.php');
     
    $d = bigWeb\Debug\Dumper::factory();
    $d->attach(new bigWeb\Debug\Dumper\FireDump());
    $d->notify('bar');

    wynik pokarze nam się w konsoli FireBug-a

    Wynik Dumpera w konsoli FireBug

    UWAGA! Ponieważ informacje do FireBuga są przesyłane za pomocą nagłówków HTTP
    biblioteka FirePHP wymaga buforowania wyjścia (output bufering). Koniecznym
    jest zatem użycie funkcji ob_start() w przeciwnym razie można się spotkać
    z błędem „headers already sent error”

    Łańcuch wywołań

    Niekiedy chcemy wiedzieć jakie funkcje i metody zostały wywołane nim został
    wykonany kod w danym miejscu. Aby zobaczyć cały łańcuch wywołań należy
    ustawić flagę show_trace na TRUE.

    function foo() {
        $d = bigWeb\Debug\Dumper::factory();
        $d->attach(new bigWeb\Debug\Dumper\VarDump());
        $d->setShowTrace(TRUE);
        $d->notify('Show chain requests');
    }
     
    function bar() {
        foo();
    }
     
    bar();
    TIME: 20:46:44 FILE: /home/www/bigWeb/Debug/example.php LINE: 97 ET: 0.005149 MU: 1.57 mb MPU: 1.67 mb

    $d->notify(‚Show chain requests’);
    string(19) "Show chain requests"
    
    Array
    (
        [0] => Array
            (
                [file] => /home/www/bigWeb/Debug/example.php
                [line] => 97
                [function] => notify
                [class] => bigWeb\Debug\Dumper
                [type] => ->
                [args] => Array
                    (
                        [0] => Show chain requests
                    )
    
            )
    
        [1] => Array
            (
                [file] => /home/www/bigWeb/Debug/example.php
                [line] => 101
                [function] => foo
                [args] => Array
                    (
                    )
    
            )
    
        [2] => Array
            (
                [file] => /home/www/bigWeb/Debug/example.php
                [line] => 104
                [function] => bar
                [args] => Array
                    (
                    )
    
            )
    
    )
    

    Praca na serwerze produkcyjnym

    W zasadzie Dumper powinien być wyłączony na serwerze produkcyjnym bo obciąża
    aplikację, a ponadto może wyświetlać dane wrażliwe. Nie mniej w pewnych
    wyjątkowych okolicznościach możemy chcieć go użyć. Musimy wcześniej przewidzieć
    taką sytuację i dodatkowo skonfigurować przynajmniej jedną z instancji Dumpera
    ustawiając tak zwany secret_key

    $d = bigWeb\Debug\Dumper::factory();
    $d->attach(new bigWeb\Debug\Dumper\FireDump());
    $d->setSecretKey('verysicretkey');

    Aby te dane były widoczne należy wywołać url metodą GET z parametrem
    secret_key=1. W tym przypadku będzie to

    example.php?verysicretkey=1
    
    bigWeb\Debug\Dumper::setEnabled(FALSE);
    $d->notify('Visible when set secret_key');

    aby ponownie włączyć:

    bigWeb\Debug\Dumper::setEnabled(TRUE);

    Jeśli wywołamy url metodą GET z sekretnym kluczem, Dumper ustawia ciasteczko
    debugcookie o wartości wywiedzionej z secret_key. Od tej pory nie trzeba już dodawać do
    adresu żadnego specjalnego parametru. Jeśli jednak chcielibyśmy wyłączyć
    debugowanie należy wywołać url z secret_key=0

    example.php?verysicretkey=0
    

    Tworzenie funkcji pomocniczych

    Tworzenie obiektu i dodawanie obserwatorów nie jest zbyt wygodnym rozwiązaniem.
    Narzędzie do debugowania powinno być extremalnie proste i szybkie w użyciu.
    Dlatego warto sobie zdefiniować funkcję pomocniczą – podobną do „debug();”

    function dump_all() {
        $_args = func_get_args();
        static $d = null;
        if ( $d === null )
        {
            // Proszę zwrócić uwagę na wywołanie metody factory z parametrem $level = 3
            $d = bigWeb\Debug\Dumper::factory(3);
            $o = new bigWeb\Debug\Dumper\FileDump();
            $o->setDir(dirname(__FILE__));
            $d->attach($o);
            $d->attach(new bigWeb\Debug\Dumper\FireDump());
            $d->attach(new bigWeb\Debug\Dumper\VarDump());
     
        }
        call_user_func_array(array($d, 'notify'), $_args);
    }
     
    dump_all('foo bar');

    wynik zostanie zapisany w pliku

    TIME: 20:46:44 FILE: /home/zh/Praca/bigWeb/Debug/example.php LINE: 155 ET: 0.006137 MU: 1.59 mb MPU: 1.67 mb
    ----------| 'foo bar' |----------
    foo bar
    .--------------------------------
    

    w konsoli FireBug-a

    Wynik Dumpera w konsoli FireBug

    oraz wyświetlony w przeglądarce:

    TIME: 20:46:44 FILE: /home/www/bigWeb/Debug/example.php LINE: 155 ET: 0.006137 MU: 1.59 mb MPU: 1.67 mb

    ‚foo bar’
    string(7) "foo bar"
    

    UWAGA! Od wartości level zależy prawidłowe wskazanie linii oraz pliku, w którym
    wywołano dumpa. Domyślnie level = 1. Jeżeli metoda „notify” jest zagnieżdżona
    w funkcji pomocniczej to wartość level powinna być inkrementowana (level = 2).
    Jeśli dodatkowo metoda „notify” jest wywoływana za pośrednictwem funkcji
    „call_user_func_array” to należy ustawić level = 3

    $d = bigWeb\Debug\Dumper::factory(1);
     
    function d1($param) {
        $d = bigWeb\Debug\Dumper::factory(2);
        // ...
        $d->notify($param);
    }
     
    function d2($param) {
        $_args = func_get_args();
        $d = bigWeb\Debug\Dumper::factory(3);
        // ...
        call_user_func_array(array($d, 'notify'), $_args);
    }

    UWAGA! Jeśli masz już zdefiniowaną funkcję „dump” w swojej aplikacji
    to po dołączeniu kodu Dumpera otrzymasz wszystkomówiący wyjątek. Poinformuje
    Cię on, że musisz zdefiniować sobie funkcję pomocniczą o innej nazwie.
    Należy zakomentować kod wywołujący wyjątek lub też dołączyć kod Dumpera
    w sposób umożliwiający przechwycenie wyjątka i utworzyć funkcję pomocniczą
    np. o nazwie „d”.

    try { include_once("Debug.php"); } catch (bigWeb\Debug\Exception $e) {
        function d() {
            $_args = func_get_args();
            static $d = null;
            if ( $d === null )
            {
                $d = bigWeb\Debug\Dumper::factory(3);
                $d->attach(new bigWeb\Debug\Dumper\VarDump());
     
            }
            call_user_func_array(array($d, "notify"), $_args);
        }
    }

    Napisana przeze mnie klasa nie zastąpi zaawansowanych i rozbudowanych narzędzi debugowania i profilowania aplikacji jednak jest prosta w użyciu nie związana stricte z żadnym frameworkiem, przez co łatwo ją zaadaptować zarówno do pracy z Zend Frameworkiem, Symfony, Kohaną czy jakimkolwiek innym – napisanym w PHP – skryptem. Biblioteka zwraca wyniki w postaci pokolorowanego kodu przy okazji pokazując czas wykonania oraz wielkość użytych zasobów. Zapomniany var_dump potrafi zmusić programistę do przeszukiwania plików projektu. W przypadku Dumpera nie ma takiego zagrożenia gdyż każdorazowo wskazuje on ścieżkę do pliku oraz nr linii, w której funkcja robiąca zrzut danych została wywołana.

    Serdecznie zapraszam wszystkich do wypróbowania Dumpera oraz wszelkich uwag na temat wdrożeń i ewentualnych błędów.

    Podziel się z innymi!