Bastion
9.03.2006, 21:05:21
Witam,
dzis przedstawiam klase class5.Debugger - w zalozeniu ma byc klasa wspolna wykorzystywana przez kolejne wersje klass z serii class5 oraz kolejnych . ze wzgledu ze beda z niej korzystac jednoczesnie obiekty jak i nie tylko klasa jest statyczna. Obsluguje takze prosty backtrace z ukrywanymi warstwami
(rozwija sie poprzez [+]). uzywa sie tak :
Jesli chcemy bezposrenio na out :
Live demo :
, $errlevel = 1, $errno = 1, $errstr = 'Something wrong', $skip =0)
{
$colors[1] = '#ff0000'; $str[1] = 'Error';
$colors[2] = '#ee6600'; $str[2] = 'Warning';
$colors[3] = '#00aa00'; $str[3] = 'Notice';
$time_point = (self::get_microtime() - self::$init_time);
if (self::$level > 0)
{
$msg = count(self::$events);
if (self::$level >= $event['errlevel'])
{
self::$events[$msg] = array('time_point' => $time_point,
'caller' => $caller,
'errno' => $errno,
'errstr' => $errstr,
'errlevel' => $errlevel);
}
$out = '<pre style="font-size: 12px; margin:0px;">';
if (self::$backtrace)
{
$backtrace = debug_backtrace();
$count = count($backtrace);
$out.= '<span id="backtracep'.$msg.'" style="display:inline;" onclick="backtraces'.$msg.'.style.display=\'inline\'; backtracem'.$msg.'.style.display=\'inline\'; this.style.display=\'none\';">[+]</span';
$out.= '<span id="backtracem'.$msg.'" style="display:none;" onclick="backtraces'.$msg.'.style.display=\'none\'; backtracep'.$msg.'.style.display=\'inline\'; this.style.display=\'none\';">[-]</span>';
}
$out.= ' ['.number_format($time_point,5).'s] :: ';
$out.= '<strong>'.$caller. '</strong> :: ';
$out.= '<font color="'.$colors[$errlevel].'">'.$str[$errlevel].'</font> - ';
$out.= $errstr.' (errno #'.$errno.')<br />';
if (self::$backtrace)
{
$out.= '<div id="backtraces'.$msg.'" style="display:none;">';
for ($i = 1; $i <= ($count-$skip); $i++)
{
$line = $backtrace[($count-$i)]['line'];
$file = $backtrace[($count-$i)]['file'];
$func = $backtrace[($count-$i)]['function'];
$class = $backtrace[($count-$i)]['class'];
$type = $backtrace[($count-$i)]['type'];
$args = ($func<>'add_event') ? $backtrace[($count-$i)]['args'] : array();
$pargs = '';
$out.= 'Call <font color="#0000aa">'.$class.$type.$func.'(';
foreach ($args as $arg)
{
if (!empty($pargs))
{
$pargs .= ', ';
}
switch (gettype($arg))
{
case 'integer':
case 'double':
$pargs .= $arg;
break;
case 'string':
$pargs .= '"'.htmlspecialchars(substr($arg, 0, 16)).((strlen($arg) > 16) ? '...' : '').'"';
break;
case 'array':
$pargs .= 'Array('.count($arg).')';
break;
case 'object':
$pargs .= 'Object('.get_class($arg).')';
break;
case 'resource':
$pargs .= 'Resource('.strstr($arg, '#').')';
break;
case 'boolean':
$pargs .= $arg ? 'True' : 'False';
break;
case 'NULL':
$pargs .= 'Null';
break;
default:
$pargs .= 'Unknown';
}
}
$out.= $pargs.');</font> in file <font color="#009900">'.$file.'</font>, line '.$line.'<br />';
}
$out.= '<hr /></div>';
}
$out.= '</pre>';
self::$events[$msg]['out'] = $out;
if (self::$messages)
{
print self::$events[$msg]['out'];
}
}
if ($event['errlevel'] == 1 && self::$halt_on_error)
{
die('terminated...');
}
}
public function __construct()
{
global $_inited_modules;
if (empty($_inited_modules[self::className]))
{
$_inited_modules[self::className] = array('version' => self::classVersion,
'relase' => self::classRelase,
'vendor' => self::classVendor,
'vendorURL' => self::classVendorURL,
'description' => self::classDescription,
'copies' => 1);
} else
{
$_inited_modules[self::className]['copies']++;
}
self::$init_time = self::get_microtime();
}
}
$__debugger = new Debugger;
?>ime = 0;
static public $events = array();
static public $halt_on_error = true;
static public $backtrace = true;
static public $level = 1;
static public $messages = true;
static private function get_microtime()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
static public function add_event($caller = 'Undefined', $errlevel = 1, $errno = 1, $errstr = 'Something wrong', $skip =0)
{
$colors[1] = '#ff0000'; $str[1] = 'Error';
$colors[2] = '#ee6600'; $str[2] = 'Warning';
$colors[3] = '#00aa00'; $str[3] = 'Notice';
$time_point = (self::get_microtime() - self::$init_time);
if (self::$level > 0)
{
$msg = count(self::$events);
if (self::$level >= $event['errlevel'])
{
self::$events[$msg] = array('time_point' => $time_point,
'caller' => $caller,
'errno' => $errno,
'errstr' => $errstr,
'errlevel' => $errlevel);
}
$out = '<pre style="font-size: 12px; margin:0px;">';
if (self::$backtrace)
{
$backtrace = debug_backtrace();
$count = count($backtrace);
$out.= '<span id="backtracep'.$msg.'" style="display:inline;" onclick="backtraces'.$msg.'.style.display=\'inline\'; backtracem'.$msg.'.style.display=\'inline\'; this.style.display=\'none\';">[+]</span';
$out.= '<span id="backtracem'.$msg.'" style="display:none;" onclick="backtraces'.$msg.'.style.display=\'none\'; backtracep'.$msg.'.style.display=\'inline\'; this.style.display=\'none\';">[-]</span>';
}
$out.= ' ['.number_format($time_point,5).'s] :: ';
$out.= '<strong>'.$caller. '</strong> :: ';
$out.= '<font color="'.$colors[$errlevel].'">'.$str[$errlevel].'</font> - ';
$out.= $errstr.' (errno #'.$errno.')<br />';
if (self::$backtrace)
{
$out.= '<div id="backtraces'.$msg.'" style="display:none;">';
for ($i = 1; $i <= ($count-$skip); $i++)
{
$line = $backtrace[($count-$i)]['line'];
$file = $backtrace[($count-$i)]['file'];
$func = $backtrace[($count-$i)]['function'];
$class = $backtrace[($count-$i)]['class'];
$type = $backtrace[($count-$i)]['type'];
$args = ($func<>'add_event') ? $backtrace[($count-$i)]['args'] : array();
$pargs = '';
$out.= 'Call <font color="#0000aa">'.$class.$type.$func.'(';
foreach ($args as $arg)
{
if (!empty($pargs))
{
$pargs .= ', ';
}
switch (gettype($arg))
{
case 'integer':
case 'double':
$pargs .= $arg;
break;
case 'string':
$pargs .= '"'.htmlspecialchars(substr($arg, 0, 16)).((strlen($arg) > 16) ? '...' : '').'"';
break;
case 'array':
$pargs .= 'Array('.count($arg).')';
break;
case 'object':
$pargs .= 'Object('.get_class($arg).')';
break;
case 'resource':
$pargs .= 'Resource('.strstr($arg, '#').')';
break;
case 'boolean':
$pargs .= $arg ? 'True' : 'False';
break;
case 'NULL':
$pargs .= 'Null';
break;
default:
$pargs .= 'Unknown';
}
}
$out.= $pargs.');</font> in file <font color="#009900">'.$file.'</font>, line '.$line.'<br />';
}
$out.= '<hr /></div>';
}
$out.= '</pre>';
self::$events[$msg]['out'] = $out;
if (self::$messages)
{
print self::$events[$msg]['out'];
}
}
if ($event['errlevel'] == 1 && self::$halt_on_error)
{
die('terminated...');
}
}
public function __construct()
{
global $_inited_modules;
if (empty($_inited_modules[self::className]))
{
$_inited_modules[self::className] = array('version' => self::classVersion,
'relase' => self::classRelase,
'vendor' => self::classVendor,
'vendorURL' => self::classVendorURL,
'description' => self::classDescription,
'copies' => 1);
} else
{
$_inited_modules[self::className]['copies']++;
}
self::$init_time = self::get_microtime();
}
}
$__debugger = new Debugger;
?>
sobstel
9.03.2006, 21:28:05
sorry, ale nie mam za bardzo czasu teraz wsyzstko przegladac. rzucilo mi sie jedno po pobieznym przeglądzie. dlaczego uzywasz wlasnych poziomow debuggera (Debugger::$level). mysle, ze uzycie tych dostepnych w php (E_ERROR, E_NOTICE, itp.) w zupelnosci by wystraczylo, a i moglbys odczytywac ten level na podstawie ustawien error_reporting.
Mi się podoba, chociaż to raczej pisałes dla ćwiczeń, bo używać tego nikt raczej nie będzie

W każdym razie jak zwykle ładnie opisane i jest przykład. Byle dalej
Bastion
9.03.2006, 21:37:47
@sopel : Uzylem swoich trzech bo na dzialanie tego debuggera bez sensu byloby uzywanie dyrektyw E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, E_STRICT ktorych nie bede uzywal poki co ( jesli brac pod uwage wersje 1.0.0 ). Pozatym mysle ze sposob w jaki dziala ten debugger i jego przeznaczenie do tworzenia komunikatow error, wran, notice przez piszacego skrypt (klasa nie przechwutuje bledow parsera php) bylo akurat odpowiednie. Moze moj sposob myslenia jest zly

ale przemysle twoja uwage. Dzieki
@tes : dla cwiczen moze nie, beda z tego korzystac nowe wydania klas class5, na ktorych bedzie oparty miedzy innymi FileSupply Pro 2.0.0 oraz EclipseX
Vomit
10.03.2006, 15:48:31
Debugger to cos w stylu Obsługa Wyjatków?
Bastion
10.03.2006, 16:16:28
W przykladach jakie tu sa, chyba widac po co to jest. Wyobraz sobie ze masz klase obslugujaca np baze danych i ona ma np funkcje ->query('SELECT....') , w wyniku tego zapytania wystapil jakis blad - i teraz zamiast wyrzucac print 'Blad zapytania', dajesz do debuggera, ktory nie dosc ze pokaze w zgrabny sposob kod bledu i przygotowana przez ciebie tresc - to w dodatku przesledzi w ktorym dokladnie miejscu (plik , linia) nastapilo wywolanie ->query(...) i przesledzi co jak i ktoredy - przyjzyj sie dokladnie przykladowi 1 gdie wywolanie add_event nastpilo w funkcji bar() , ktora zostaje wywolana przez foo(), a ta natomiast gdzies w zrodle
Vomit
10.03.2006, 16:19:53
Czyli Debugger == Error Handler.
Bastion
10.03.2006, 16:23:00
nie zupelnie, z tego co sie orientuje error_handler przechwutuje bledy php E_ERROR etc, tutaj sam definiujesz w ktorym miejscu nastapi dodanie watku add_event().
Vomit
10.03.2006, 16:50:46
No tak, ale od "własnych" bledow sa wyjatki.
Bastion
10.03.2006, 16:52:46
nie przewiduje narazie takich :] hyhyh - a na powaznie myslalem o tym - i na pewno pojawi sie w kolejnym relase.
splatch
10.03.2006, 17:19:47
Dobry pomysł ale niestety, w praktyce trudny do wykorzystania. Po pierwsze, nie zastąpi on w pełni debuggera takiego jak ma np. php Eclipse, gdzie krok po kroku można się poruszać pomiędzy breakpointami, gdzie od razu widać zmienne, jakie trafiły do funkcji oraz zrzut tablic superglobalnych.
Coś takiego jest bardzo trudne do uzyskania przy użyciu Twojej klasy i szczerze wątpie by mimo dobrych opini ktoś jej używał przy pisaniu. Mi np. o wiele wygodniej kliknać na marginesie i odpalić debuggera w moim IDE niż wpisywać kolejne linie kodu.
Przykład który podałeś nie sprawdzi się również w praktyce ponieważ na serwerze produkcyjnym nie będziesz chciał takiej informacji pokazywać ze względów bezpieczeństwa. Użytkownik w takiej sytuacji powinien dostać wiadomość, że operacja się nie powiodła a nie stack trace. Takie sytuacje powinno się załatwiać loggerami - w javie wielką popularnością cieszy się pakiet common.logging albo Log4J (powstały również porty do php). Logi odkładają się w jednym miejscu, gdy w gotowej aplikacji wystąpi błąd programista zagląda do logów i analizuje, diagnozuje przyczynę błędu. Oczywiście nie zawsze takie rozwiązanie jest dobre.
Nie wiadomo czym ta klasa ma być - czy kolejnym error handlerem czy czymś innym? Jako exception handler nie zda się, ponieważ po wykonaniu exception handlera php i tak kończy pracę. Kończąc - mimo dobrego wykonania - po prostu nie widzę zastosowania dla tej klasy.
Bastion
10.03.2006, 17:23:41
splatch: ciekawa opinia - ale niestety / na szczescie ja widze zastosowanie dla siebie

i wcale nie sadze badz nie propaguje by uzywal tego ktokolwiek inny

niemniej chcialem oceny - wiec i taka sie liczy . dzieki.
-- added
pozatym gdzie jest powiedziane ze praca tej klasy musi byc jawna, w 2 przykladzie pokazano ze wcale tak nie musi byc
Vomit
10.03.2006, 18:04:13
Poza tym mamy jeszcze funkcje: trigger_error i set_error_handler...
hawk
12.03.2006, 19:52:08
Bardzo dobrze napisany kod, ale zgodzę się ze splatch'em, że z zastosowaniem tego będzie problem. Tak klasa jednocześnie robi za dużo i za mało.
Za dużo, bo w jednej klasie jest łapanie zdarzeń, formatowanie przechwyconej wiadomości, tworzenie backtrace, wyrzucanie HTMLa. Przez to cały system staje się niekonfigurowalny. Mogę chcieć przecież zapisać opis do pliku czy nawet zmienić wygląd tworzonego HTMLa. Dorabianie na tę okoliczność kolejnych parametrów będzie złym pomysłem, bo już te, które są, można zastąpić dobrym rozbiciem na obiekty.
Za mało, bo informacje przedstawione na ekranie są ubogie. Oczywiście, jest ładnie sformatowany backtrace, ale PHP5 potrafi znacznie, znacznie więcej. Cała biblioteka Reflection idealnie nadaje się do takich zastosowań.
Bastion
12.03.2006, 22:37:39
hawk

mysle ze masz duzo racji - po przemysleniu sprawy ze splatchem tez sie zgodze.
klasa jednak powstaje na moj specjalny uzytyek do CMS'a (nastepcy SiteX) i dzieki waszym
opiniom bedzie jeszcze jeszcze jeden relase, ktory pozowoli mi na dobor optymalnych parametrow jej pracy. Jeszcze raz dziekuje
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę
kliknij tutaj.