image
PHPixie Debug был создан чтобы улучшить отладку при разработке в любой среде. Конечно если вы уже используете веб фреймворк то скорее всего проблем с отладкой нет, но вот когда вы пишете какую-то библиотеку, решаете задачку или даже работаете с Wordpress, то удобной отладки сильно недостает. Даже для такого базового функционала как превратить все ошибки в исключения приходится регистрировать свой хендлер. В таких случаях PHPixie Debug в всего лишь две строки кода создаст удобную среду для разработки.

Исключения и трейсы


Debug предоставляет в консоли то удобство работы с исключениями к которому мы уже привыкли в веб фреймворках. При написании библиотек мне очень не хватало красивых информативных трейсов c выводом той части кода в которой произошло исключение. Большая проблема трассирования в PHP в том что если просто вызвать print_r(debug_backtrace()) и одним из аргументов какого-либо вызова являлся объект то результат превратится в гору текста. Есть конечно debug_print_backtrace(), но во-первых массивы он все равно выводит полностью, во-вторых чтобы получить его в виде строки приходится использовать буфер. А вот как обработает трейс исключения PHPixie Debug:

<?php
require_once('vendor/autoload.php');
$debug = new \PHPixie\Debug();

try{
    throw new \Exception("test");
    
}catch(\Exception $e) {
    //Вывод исключения
    $debug->exceptionMessage($e);
}

echo "\n-------\n";

//Автоматический вывод исключений
//Также при исключении будет выведен весь лог
//(о нем речь пойдет позже)
$debug->registerHandlers();

class Test
{
    public function a($string)
    {
        $array = array(1, 2);
        $this->b($string, $array);
    }
    
    public function b($string, $array)
    {
        $object = (object) array('t' => 1);
        $this->c($string, $array, $object);
    }
    
    public function c()
    {
        substr();
    }
}

$test = new Test();
$test->a("pixie");


Exception: test                                                       
                                                                      
5                                  
6 try{                                                                 
>     throw new \Exception("test");                                    
8                                                                      
9 }catch(\Exception $e) {                                              
                                                                      
#0 D:\debug\examples\exceptions.php:7                                
                                                                      
-------                                                               
                                                                                                                                       
ErrorException: substr() expects at least 2 parameters, 0 given       
                                                                      
36     public function c()                                             
37     {                                                               
>>         substr();                                                   
39     }                                                               
40 }                                                                   
                                                                      
#0 D:\debug\examples\exceptions.php:38                               
#1 D:\debug\examples\exceptions.php:38                               
    substr()                                                          
#2 D:\debug\examples\exceptions.php:33                               
    Test->c('pixie', array[2], stdClass)                              
#3 D:\debug\examples\exceptions.php:27                               
    Test->b('pixie', array[2])                                        
#4 D:\debug\examples\exceptions.php:43                               
    Test->a('pixie')                                                  
                                                                      
Logged items:                                                                                                                              

Кстати обратите внимание что в трейсе не видно хендлера который превратил PHP ошибку в исключение, много других библиотек часто забывают его спрятать тем самым засоряя трейс. PHPixie прячет все такие вмешательства в стек вызова.

Вывод переменных


Вывод сделан через статическую функцию \PHPixie\Debug::dump(), это кстати первая статическая функция в PHPixie. Причина этому то, что по замыслу функции отладки не являются частью вашего приложения и вы и так удалите их как только все заработает. Поэтому нет смысла передавать класс Debug как зависимость через конструктор. Тут кстати стоит отметить что эти функции будут работать только если сам объект Debug уже был создан, таким образом они всего лишь прокси и ни какой статической логики в PHPixie никогда не будет.

<?php
require_once('vendor/autoload.php');

use PHPixie\Debug;
$debug = new Debug();

Debug::dump("Array dump:");
Debug::dump(array(1));

Debug::dump("Short array dump:");
//Короткий дамп выводит минимум информации. 
//Это удобно например для вывода количества
//элементов массива или имя класса объекта
Debug::dump(array(1), true);

$object = (object) array('t' => 1);
Debug::dump("Object dump:");
Debug::dump($object);

Debug::dump("Short object dump:");
Debug::dump($object, true);

Debug::dump("Dump trace with parameters");
class Test
{
    public function a($string)
    {
        $array = array(1, 2);
        $this->b($string, $array);
    }
    
    public function b($string, $array)
    {
        $object = (object) array('t' => 1);
        $this->c($string, $array, $object);
    }
    
    public function c()
    {
        Debug::trace();
    }
}

$t = new Test();
$t->a("test");


'Array dump:'

Array
(
    [0] => 1
)


'Short array dump:'

array[1]

'Object dump:'

stdClass Object
(
    [t] => 1
)


'Short object dump:'

stdClass

'Dump trace with parameters'

#0 D:\debug\examples\dumping.php:37
    PHPixie\Debug::trace()
#1 D:\debug\examples\dumping.php:32
    Test->c('test', array[2], stdClass)
#2 D:\debug\examples\dumping.php:26
    Test->b('test', array[2])
#3 D:\debug\examples\dumping.php:42
    Test->a('test')


Лог


Для того чтобы разделить сообщения отладки от вывода самой программы часто сообщения записывают в массив который выводят в конце исполнения программы. Единственная проблема в том, что если возникает ошибка или где-то используется exit() то после этого сообщения уже не выведутся. PHPixie Debug выводит лог при исключениях и может также зарегистрировать хендлер для обязательного вывода лога сообщений по завершении работы скрипта. Вот два примера:

use PHPixie\Debug;
$debug = new Debug();

Debug::log("test");
Debug::log(array(3));

class Test
{
    public function a($string, $num)
    {
        Debug::logTrace();
    }
}
$t = new Test();
$t->a("test", 5);

//Ручной вывод сообщений
$debug->dumpLog();


Logged items:

[0] D:\debug\examples\logging.php:7
'test'

[1] D:\debug\examples\logging.php:8
Array
(
    [0] => 3
)


[2] D:\debug\examples\logging.php:16
#0 D:\debug\examples\logging.php:16
    PHPixie\Debug::logTrace()
#1 D:\debug\examples\logging.php:20
    Test->a('test', 5)


И с автоматическим выводом:

<?php

use PHPixie\Debug;
$debug = new Debug();

//Передав 'true' при регистрации хендлеров
//мы также включаем вывод лога в конце
//исполнения
$debug->registerHandlers(true);

Debug::log("test");

echo("Logged messages will be printed below");


Logged messages will be printed now

Logged items:

#0 D:\debug\examples\log_handler.php:13
'test'


В заключение


Главной целью библиотеки является не сам вывод трейсов и лога, а скорее удобное представление их в виде классов, что позволит в будущем построит веб версию дебага для PHPixie, ведь фактически осталось только добавить красивый темплейт для рендеринга. Но главное преимущество Debug как самостоятельного инструмента в том, что всего в две строчки дополнительного кода можно получить удобную среду для разработки не веб приложений. Надеюсь в следующий раз когда работодатель предложит вам решить задачку про коммивояжера или вам понадобится трассировка в Wordpress, вы вспомните эту библиотеку и сэкономите время какое потратили бы на debug_backtrace().

Демо


Чтобы попробовать Debug своими руками достаточно:

git clone https://github.com/phpixie/debug
cd debug/examples
 
#если у вас еще нет Композера
curl -sS https://getcomposer.org/installer | php
 
php composer.phar install
php exceptions.php
php logging.php
php log_handler.php
php dumping.php


И кстати как и у всех других библиотеках от PHPixie вас ждет 100% покрытие кода тестами и работа под любой версией PHP старше 5.3 (включая новую 7 и HHVM).

Комментарии (15)


  1. REZ1DENT3
    10.05.2015 16:46
    +1

    Как я понимаю библиотека независимая и её можно подключить к PHPixie 2, Yii, Symfony?


    1. jigpuzzled Автор
      10.05.2015 17:00

      Да, как и все другие библиотеки 3й версии =)
      Правда у PHPixie 2, Yii и Symfony уже есть инструменты для подобного, кроме как вывода части кода где произошло исключение при работе в консоли.

      Это больше для людей которые пишут без фреймворка, или для тех кто хочет сделать свой красивый веб отладчик типа Whoops. Если объединить PHPixie Template и Debug, то такой Whoops можно сделать за день =)


      1. vshemarov
        10.05.2015 18:29

        Как-то не вполне очевидны плюсы деланья своего Woops, пусть и за день, если можно просто взять оригинальный Woops и прикрутить за несколько минут.


        1. jigpuzzled Автор
          10.05.2015 20:16
          +1

          ну это ведь только пример использования


  1. Lachezis
    10.05.2015 18:57
    +1

    Не нашел в зависимостях PSR-3, Logger, получается, несовместим с Monolog и т.д.?


    1. jigpuzzled Автор
      10.05.2015 19:30

      Это не совсем тот лог что монолог. Это лог для дебага, подразумевается что вы удалите вызовы к нему перед комитом куда либо )


      1. Lachezis
        10.05.2015 19:37
        +2

        Честно говоря я не вижу смысла не делать его совместимым, ваш логгер может быть враппером над PSR-3 и в таком случае не придется путаться какой из логгеров использовать если возникнет необходимость для дополнительного логирования. Благо PSR-3 простой как палка.


        1. jigpuzzled Автор
          10.05.2015 22:57

          В PSR-7 есть много всего чего не надо было в этой имплементации, например уровни логирования. Но главная проблема в том что логер должен быть классом а никак не статическим методом.

          С разных уровней логирования смысла в данном случае нет, так как назначением является только отладка и как раз для простоты отладки все делается через статический вызов. Я дума вам бы не хотелось чтобы при исключении выбрасывался все что было записано в реальный логгер.

          Но если вам интересно, то в PHPixie будет свой PSR-3 совместимый логгер с гораздо большим функционалом чем просто дамп переменных =)


          1. Lachezis
            10.05.2015 23:15

            > Но главная проблема в том что логер должен быть классом а никак не статическим методом.

            class LoggerFacade
            {
                /**
                 * @var LoggerInterface
                 */
                protected static $logger = null;
            
                public static function setLogger(LoggerInterface $logger)
                {
                    self::$logger = $logger;
                }
            
                public function log($message)
                {
                    self::$logger->debug($message);
                }
            }
            


            При исключении можно выбрасывать все что в LogLevel::DEBUG.


            1. Lachezis
              10.05.2015 23:20

              > public static function log($message)
              Static конечно же.


              1. jigpuzzled Автор
                11.05.2015 00:11

                Тогда весь смысл пропадает, так как ваш кок тогда зависит от LoggerFacade а не от PSR-3 самого. Если же идя в том чтобы логгер дебага мог писать в PSR-3 лог, то это конечно хорошая идея. Но если у вас уже есть PSR-3 логгер, то зачем вам использовать его через Debug вообще? Можно ведь сразу в него положыть


  1. OnYourLips
    11.05.2015 20:01
    -1

    > Тут кстати стоит отметить что эти функции будут работать только если сам объект Debug уже был создан, таким образом они всего лишь прокси и ни какой статической логики в PHPixie никогда не будет.

    Я надеюсь, что с подобным по качеству кодом мне не придётся сталкиваться.


    1. jigpuzzled Автор
      11.05.2015 20:39
      +1

      А чем вам не понравилось качество кода?


  1. maxru
    12.05.2015 10:35

    Xdebug, Карл!


    1. jigpuzzled Автор
      12.05.2015 13:11

      Ну у него трейс не такой лаконичный, и параметров не видно. И ООП интерфейса к трейсам нет, и лога, и ошыбки он в експешн не прверащает. XDebug конечно хорош, можно настроить IDE и построчно дебагить, но имхо он не является прямым конкурентом библиотеки, немного сфера другая