From c7129106c358566a94ab550e041161b50bab00e2 Mon Sep 17 00:00:00 2001 From: Andrey Kutejko Date: Sun, 17 Aug 2014 16:54:03 +0300 Subject: [PATCH] rework debug page for 500 --- composer.lock | 8 +- ipf/http/request.php | 13 + ipf/http/response/servererror.php | 2 +- ipf/http/response/servererrordebug.php | 709 ++++++++++++------------- ipf/orm/collection.php | 9 + ipf/orm/pager.php | 12 +- ipf/orm/pager/layout.php | 8 + ipf/orm/query/abstract.php | 6 + ipf/orm/record/abstract.php | 8 + ipf/router.php | 14 +- 10 files changed, 412 insertions(+), 377 deletions(-) diff --git a/composer.lock b/composer.lock index acf293f..87cb631 100644 --- a/composer.lock +++ b/composer.lock @@ -12,7 +12,7 @@ "source": { "type": "git", "url": "git://git.andy128k.net/ipf-template.git", - "reference": "ecb0223b97848beb7aa5841934de45b9c97bfd9f" + "reference": "ce8150e469804e5b547cda1f4ab51da22d1ce116" }, "require-dev": { "phpunit/phpunit": "3.7.*" @@ -37,7 +37,7 @@ } ], "description": "Template Engine extracted from IPF Web Framework", - "time": "2013-10-06 15:58:13" + "time": "2014-08-17 06:31:33" }, { "name": "andy128k/migrations", @@ -74,7 +74,7 @@ "source": { "type": "git", "url": "git://git.andy128k.net/missing-tools.git", - "reference": "a37bcfc615daa8682efffb59fbe685aab93d19aa" + "reference": "a7f7bd0891d1cfe8f6ce418af5b7f2998a145e1f" }, "require": { "andy128k/pegp": "0.1.*@dev", @@ -99,7 +99,7 @@ } ], "description": "Miscellaneous utilities", - "time": "2014-07-22 11:53:58" + "time": "2014-08-17 13:46:37" }, { "name": "andy128k/pegp", diff --git a/ipf/http/request.php b/ipf/http/request.php index 78fa4e5..622706f 100644 --- a/ipf/http/request.php +++ b/ipf/http/request.php @@ -51,5 +51,18 @@ class IPF_HTTP_Request { return ($this->is_secure ? 'https://' : 'http://') . $this->http_host . $this->query; } + + public function getHeaders() + { + if (function_exists('getallheaders')) + return getallheaders(); + else + return false; + } + + public function getBody() + { + return file_get_contents('php://input'); + } } diff --git a/ipf/http/response/servererror.php b/ipf/http/response/servererror.php index 3c9717b..c61f345 100644 --- a/ipf/http/response/servererror.php +++ b/ipf/http/response/servererror.php @@ -2,7 +2,7 @@ class IPF_HTTP_Response_ServerError extends IPF_HTTP_Response { - function __construct($e, $mimetype=null) + function __construct($request, $e, $mimetype=null) { parent::__construct('

500 Server Error

Our apologies…

Please return later

', $mimetype); $this->status_code = 500; diff --git a/ipf/http/response/servererrordebug.php b/ipf/http/response/servererrordebug.php index 2075721..3fc7264 100644 --- a/ipf/http/response/servererrordebug.php +++ b/ipf/http/response/servererrordebug.php @@ -1,11 +1,14 @@ status_code = 500; - $this->content = IPF_HTTP_Response_ServerErrorDebug_Pretty($e); + $page = new IPF_HTTP_ServerErrorDebugPage($request, $exception); + $this->content = $page->html(); } } @@ -13,51 +16,13 @@ function debug_print_r($v, $indent=0) { $result = array(); - if ($v instanceof IPF_HTTP_Request) { - return 'IPF_HTTP_Request'; - } elseif ($v instanceof IPF_ORM_Query_Abstract) { - $result = array( - get_class($v), - '{', - ' sql: ' . $v->getQuery(), - '}'); - } elseif ($v instanceof IPF_ORM_Record_Abstract) { - $result[] = get_class($v); - $result[] = '{'; - foreach ($v->getTable()->getColumnNames() as $column) { - $result[] = ' ' . $column . ': ' . debug_print_r($v->get($column), $indent+2); - } - $result[] = '}'; - } elseif ($v instanceof IPF_ORM_Collection) { - $result[] = get_class($v); - $result[] = '{'; - foreach ($v as $key => $value) { - $result[] = ' ' . $key . ': ' . debug_print_r($value, $indent+2); - } - $result[] = '}'; - } elseif ($v instanceof IPF_Template_Context) { - $result[] = get_class($v); + if (is_object($v) && method_exists($v, '__debugInfo')) { + $result[] = get_class($v) . ' Object'; $result[] = '{'; - foreach ($v->_vars as $var => $value) { - $result[] = ' ' . $var . ': ' . debug_print_r($value, $indent+2); + foreach ($v->__debugInfo() as $key => $val) { + $result[] = ' [' . $key . '] => ' . debug_print_r($val, $indent+2); } $result[] = '}'; - } elseif ($v instanceof IPF_Template_ContextVars) { - $result[] = get_class($v); - $result[] = '{'; - foreach ($v as $var => $value) { - $result[] = ' ' . $var . ': ' . debug_print_r($value, $indent+2); - } - $result[] = '}'; - } elseif ($v instanceof IPF_ORM_Pager_Layout) { - $pager = $v->getPager(); - $result = array( - 'IPF_ORM_Pager_Layout', - '{', - ' page: ' . $pager->getPage(), - ' size: ' . $pager->getMaxPerPage(), - ' query: ' . debug_print_r($pager->getQuery(), $indent+2), - '}'); } elseif ($v instanceof stdClass) { $result[] = 'stdClass'; $result[] = '{'; @@ -69,7 +34,7 @@ function debug_print_r($v, $indent=0) $result[] = 'Array'; $result[] = '{'; foreach ($v as $k => $v) { - $result[] = ' ' . $k . ' => ' . debug_print_r($v, $indent+2); + $result[] = ' [' . $k . '] => ' . debug_print_r($v, $indent+2); } $result[] = '}'; } elseif (is_bool($v)) { @@ -84,338 +49,354 @@ function debug_print_r($v, $indent=0) return ltrim(implode("\n", $result)); } -function IPF_HTTP_Response_ServerErrorDebug_Pretty($e) +class IPF_HTTP_ServerErrorDebugPage { - $o = create_function('$in','return htmlspecialchars($in);'); - $sub = create_function('$f','$loc="";if(isset($f["class"])){ - $loc.=$f["class"].$f["type"];} - if(isset($f["function"])){$loc.=$f["function"];} - if(!empty($loc)){$loc=htmlspecialchars($loc); - $loc="$loc";}return $loc;'); - $parms = create_function('$f','$params=array();if(isset($f["function"])){ - try{if(isset($f["class"])){ - $r=new ReflectionMethod($f["class"]."::".$f["function"]);} - else{$r=new ReflectionFunction($f["function"]);} - return $r->getParameters();}catch(Exception $e){}} - return $params;'); - $src2lines = create_function('$file','$src=nl2br(highlight_file($file,TRUE)); - return explode("
",$src);'); - $clean = create_function('$line','return trim(strip_tags($line));'); - $desc = get_class($e)." making ".$_SERVER['REQUEST_METHOD']." request to ". - $_SERVER['REQUEST_URI']; - $out = ' - - - - - - '.$o($desc).' - - - - - -
-

'.$o($desc).'

-

'; - if ($e->getCode()) { - $out .= $o($e->getCode()). ' : '; + + function RequestRawBody() + { + $body = $this->request->getBody(); + if (!$body) + return ''; + + return array( + Tag::h4('Body'), + Tag::p(array('class' => 'req', 'style' => 'padding-bottom: 2em'), + Tag::code(null, $body)), + ); } - $out .= ' '.$o($e->getMessage()).'

- - - - - - - - - -
PHP'.$o($e->getFile()).', line '.$o($e->getLine()).'
URI'.$o($_SERVER['REQUEST_METHOD'].' '. - $_SERVER['REQUEST_URI']).'
-
- -
-

Stacktrace - - ▶

-
'; - } else { - $out .= '
No src available
'; - } - $out .= ''; - } - $out .= ' - - - - -
-

Request - - ▶

-
'; - if ( function_exists('apache_request_headers') ) { - $out .= '

Request (raw)

'; - $req_headers = apache_request_headers(); - $out .= '

HEADERS

'; - if ( count($req_headers) > 0 ) { - $out .= '

'; - foreach ($req_headers as $req_h_name => $req_h_val) { - $out .= $o($req_h_name.': '.$req_h_val); - $out .= '
'; - } - $out .= '

'; - } else { - $out .= '

No headers.

'; - } - $req_body = file_get_contents('php://input'); - if ( strlen( $req_body ) > 0 ) { - $out .=' -

Body

-

- '.$o($req_body).' -

'; - } - } - $out .= ' -

Request (parsed)

'; - $superglobals = array('$_GET','$_POST','$_COOKIE','$_SERVER','$_ENV'); - foreach ( $superglobals as $sglobal ) { - $sfn = create_function('','return '.$sglobal.';'); - $out .= '

'.$sglobal.'

'; - if ( count($sfn()) > 0 ) { - $out .= ' - - - - - - - - '; - foreach ( $sfn() as $k => $v ) { - $out .= ' - - - '; + + + function RequestSuperlobal($var) + { + if (count($var)) { + $table = Tag::table(array('class' => 'req')); + + $thead = Tag::thead() + ->append(Tag::tr() + ->append(Tag::th(null, 'Variable')) + ->append(Tag::th(null, 'Value'))); + + $tbody = Tag::tbody(); + foreach ($var as $k => $v) { + $tbody->append(Tag::tr() + ->append(Tag::td() + ->append($k)) + ->append(Tag::td(array('class' => 'code')) + ->append(Tag::div()->append(print_r($v, true))))); } - $out .= ' - -
VariableValue
'.$o($k).' -
'.$o(print_r($v,TRUE)).'
-
'; - } else { - $out .= ' -

No data

'; - } - } - $out .= ' - -
-
'; - if ( function_exists('headers_list') ) { - $out .= ' -
- -

Response - - ▶

- -
- -

Headers

'; - $resp_headers = headers_list(); - if (count($resp_headers) > 0) { - $out .= ' -

'; - foreach ( $resp_headers as $resp_h ) { - $out .= $o($resp_h); - $out .= '
'; + + return $table->append($thead)->append($tbody); + } else { + return Tag::p(array('class' => 'whitemsg'), 'No data'); + } + } + + + function SectionResponse() + { + return $this->section('response', 'Response', + $this->ResponseHeaders()); + } + + function ResponseHeaders() + { + $r = array( + Tag::h3('Headers'), + ); + $headers = headers_list(); + if ($headers) { + $h = Tag::p(array('class' => 'headers')); + foreach ($headers as $header) { + $h->append($header)->append(Tag::br()); } - $out .= '

'; + $r[] = $h; } else { - $out .= ' -

No headers.

'; - } - $out .= ' -
'; - } - $out .= ' - - -'; - return $out; + $r[] = Tag::p(null, 'No headers.'); + } + return $r; + } + + + function css() + { + return Tag::style()->raw('html * { padding:0; margin:0; } +body * { padding:10px 20px; } +body * * { padding:0; } +body { font:small sans-serif; background: #70DBFF; } +body>div { border-bottom:1px solid #ddd; } +h1 { font-weight:normal; } +h2 { margin-bottom:.8em; } +h2 span { font-size:80%; color:#666; font-weight:normal; } +h2 a { text-decoration:none; } +h3 { margin:1em 0 .5em 0; } +h4 { margin:0.5em 0 .5em 0; font-weight: normal; font-style: italic; } +table { border:1px solid #ccc; border-collapse: collapse; background:white; } +tbody td, tbody th { vertical-align:top; padding:2px 3px; } +thead th { padding:1px 6px 1px 3px; background:#70FF94; text-align:left; font-weight:bold; font-size:11px; border:1px solid #ddd; } +tbody th { text-align:right; color:#666; padding-right:.5em; } +.vars table { margin:5px 0 2px 40px; } +.vars table td, table.req td { font-family:monospace; } +table td { background: #70FFDB; } +table td.code { width:95%;} +table td.code div { overflow:hidden; } +table.source th { color:#666; } +table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; } +ul.traceback { list-style-type:none; } +ul.traceback li.frame { margin-bottom:1em; } +div.context { margin:5px 0 2px 40px; background: #fff; border: 1px dashed #666; } +div.context ol { padding-left:10px; margin:0 10px; list-style-position: inside; } +div.context ol li { font-family:monospace; white-space:pre; color:#666; cursor:pointer; } +div.context li.current-line { color:black; background-color:#70FF94; } +div.commands { margin-left: 40px; } +div.commands a { color:black; text-decoration:none; } +p.headers { background: #70FFDB; font-family:monospace; padding: 2px; } +#summary { background: #00B8F5; } +#summary h2 { font-weight: normal; color: #666; } +#traceback { background:#eee; } +#request { background:#f6f6f6; } +#response { background:#eee; } +#summary table { border:none; background:#00B8F5; } +#summary td { background:#00B8F5; } +.switch { text-decoration: none; } +.whitemsg { background:white; color:black;}'); + } + + function js() + { + return Tag::script()->raw(' +window.onload = function() { + function onClick(e, c) { + var args = Array.prototype.slice.call(arguments, 2); + if (e.addEventListener) + e.addEventListener("click", function(ev) { c.apply(this, args); ev.preventDefault(); }, false); + else + e.attachEvent("onclick", function(ev) { c.apply(this, args); ev.returnValue = false; }); + } + + var i, bd, sections = document.querySelectorAll(".section"); + for (i = 0; i < sections.length; ++i) { + bd = sections[i].querySelector(".section-body"); + bd.style.display = \'none\'; + onClick(sections[i].querySelector(".section-toggle"), function (sw, bd) { + bd.style.display = bd.style.display == \'none\' ? \'block\' : \'none\'; + + var uarr = String.fromCharCode(0x25b6); + var darr = String.fromCharCode(0x25bc); + sw.innerHTML = sw.innerHTML == uarr ? darr : uarr; + }, + sections[i].querySelector(".section-switch"), + bd); + } +}'); + } } diff --git a/ipf/orm/collection.php b/ipf/orm/collection.php index be683a5..9bf5e52 100644 --- a/ipf/orm/collection.php +++ b/ipf/orm/collection.php @@ -544,4 +544,13 @@ class IPF_ORM_Collection extends IPF_ORM_Access implements Countable, IteratorAg { return $this->relation; } + + public function __debugInfo() + { + $r = array(); + foreach ($this->data as $item) + $r[] = $item; + return $r; + } } + diff --git a/ipf/orm/pager.php b/ipf/orm/pager.php index bf143d0..744f44e 100644 --- a/ipf/orm/pager.php +++ b/ipf/orm/pager.php @@ -252,4 +252,14 @@ class IPF_ORM_Pager } return $this->getQuery()->execute($params, $hydrationMode); } -} \ No newline at end of file + + public function __debugInfo() + { + return array( + 'page' => $this->getPage(), + 'size' => $this->getMaxPerPage(), + 'query' => $this->getQuery(), + ); + } +} + diff --git a/ipf/orm/pager/layout.php b/ipf/orm/pager/layout.php index 1d83ebb..a5c1a32 100644 --- a/ipf/orm/pager/layout.php +++ b/ipf/orm/pager/layout.php @@ -225,4 +225,12 @@ class IPF_ORM_Pager_Layout return strtr($str, $replacements); } + + public function __debugInfo() + { + return array( + 'pager' => $this->getPager() + ); + } } + diff --git a/ipf/orm/query/abstract.php b/ipf/orm/query/abstract.php index 025ac44..6e0f48a 100644 --- a/ipf/orm/query/abstract.php +++ b/ipf/orm/query/abstract.php @@ -989,4 +989,10 @@ abstract class IPF_ORM_Query_Abstract { return $this->getSqlQuery($params); } + + public function __debugInfo() + { + return array('sql' => $this->getQuery()); + } } + diff --git a/ipf/orm/record/abstract.php b/ipf/orm/record/abstract.php index 3d9a91a..07aab0d 100644 --- a/ipf/orm/record/abstract.php +++ b/ipf/orm/record/abstract.php @@ -48,5 +48,13 @@ abstract class IPF_ORM_Record_Abstract extends IPF_ORM_Access } return $this; } + + public function __debugInfo() + { + $r = array(); + foreach ($this->getTable()->getColumnNames() as $column) + $r[$column] = $this->get($column); + return $r; + } } diff --git a/ipf/router.php b/ipf/router.php index ffe2dac..2c85ab5 100644 --- a/ipf/router.php +++ b/ipf/router.php @@ -24,13 +24,13 @@ class IPF_Router } } - public static function response500($e) + public static function response500($request, $exception) { - error_log($e); + error_log($exception); if (IPF::get('debug')) - return new IPF_HTTP_Response_ServerErrorDebug($e); + return new IPF_HTTP_Response_ServerErrorDebug($request, $exception); else - return new IPF_HTTP_Response_ServerError($e); + return new IPF_HTTP_Response_ServerError($request, $exception); } public function dispatch($req) @@ -68,7 +68,7 @@ class IPF_Router } return array($req, $response); } catch (IPF_Exception $e) { - $response = self::response500($e); + $response = self::response500($req, $e); $response->render(); } } @@ -88,13 +88,13 @@ class IPF_Router try { $r = IPF::callFunction($func, array($req, $match)); if (!is_a($r, 'IPF_HTTP_Response')) { - return self::response500(new IPF_Exception('function '.$func.'() must return IPF_HTTP_Response instance')); + return self::response500($req, new IPF_Exception('function '.$func.'() must return IPF_HTTP_Response instance')); } return $r; } catch (IPF_HTTP_Error404 $e) { return new IPF_HTTP_Response_NotFound($req); } catch (IPF_Exception $e) { - return self::response500($e); + return self::response500($req, $e); } } -- 2.49.0