dBug/dBug.php
Marcus Puchalla 07c3deaeb5 Added 4th parameter to dBug constructor.
With this optional parameter, that is false by default, you can trigger
the init of the JS and CSS. This is needed if you want to use dBug
multiple time to e.g. create multiple files (using ob_start etc. ).
Until now it would only generate those sections on the first constructor
call.
2014-12-16 13:44:35 +01:00

544 lines
17 KiB
PHP
Executable file

<?php
/*********************************************************************************************************************\
* LAST UPDATE
* ============
* August 6th, 2012
*
*
* AUTHOR
* =============
* Kwaku Otchere
* ospinto@hotmail.com
*
* AFTERMARKET HACKER
* ==================
* Josh Sherman
* josh@crowdsavings.com
*
* Thanks to Andrew Hewitt (rudebwoy@hotmail.com) for the idea and suggestion
*
* All the credit goes to ColdFusion's brilliant cfdump tag
* Hope the next version of PHP can implement this or have something similar
* I love PHP, but var_dump BLOWS!!!
*
* FOR DOCUMENTATION AND MORE EXAMPLES: VISIT http://dbug.ospinto.com
*
*
* PURPOSE
* =============
* Dumps/Displays the contents of a variable in a colored tabular format
* Based on the idea, javascript and css code of Macromedia's ColdFusion cfdump tag
* A much better presentation of a variable's contents than PHP's var_dump and print_r functions
*
*
* USAGE
* =============
* new dBug ( variable [,forceType] );
* example:
* new dBug ( $myVariable );
*
*
* if the optional "forceType" string is given, the variable supplied to the
* function is forced to have that forceType type.
* example: new dBug( $myVariable , "array" );
* will force $myVariable to be treated and dumped as an array type,
* even though it might originally have been a string type, etc.
*
* NOTE!
* ==============
* forceType is REQUIRED for dumping an xml string or xml file
* new dBug ( $strXml, "xml" );
*
\*********************************************************************************************************************/
class dBug {
var $xmlDepth=array();
var $xmlCData;
var $xmlSData;
var $xmlDData;
var $xmlCount=0;
var $xmlAttrib;
var $xmlName;
var $arrType=array("array","object","resource","boolean","NULL");
var $bInitialized = false;
var $bCollapsed = false;
var $arrHistory = array();
//constructor
function dBug($var,$forceType="",$bCollapsed=false, $reinit = false) {
//include js and css scripts
if(!defined('BDBUGINIT') || $reinit) {
if(!defined('BDBUGINIT')){
define("BDBUGINIT", TRUE);
}
$this->initJSandCSS();
}
$arrAccept=array("array","object","xml"); //array of variable types that can be "forced"
$this->bCollapsed = $bCollapsed;
if(in_array($forceType,$arrAccept))
$this->{"varIs".ucfirst($forceType)}($var);
else
$this->checkType($var);
}
//get variable name
function getVariableName() {
$arrBacktrace = debug_backtrace();
//possible 'included' functions
$arrInclude = array("include","include_once","require","require_once");
//check for any included/required files. if found, get array of the last included file (they contain the right line numbers)
for($i=count($arrBacktrace)-1; $i>=0; $i--) {
$arrCurrent = $arrBacktrace[$i];
if(array_key_exists("function", $arrCurrent) &&
(in_array($arrCurrent["function"], $arrInclude) || (0 != strcasecmp($arrCurrent["function"], "dbug"))))
continue;
$arrFile = $arrCurrent;
break;
}
if(isset($arrFile)) {
$arrLines = file($arrFile["file"]);
$code = $arrLines[($arrFile["line"]-1)];
//find call to dBug class
preg_match('/\bnew dBug\s*\(\s*(.+)\s*\);/i', $code, $arrMatches);
return $arrMatches[1];
}
return "";
}
//create the main table header
function makeTableHeader($type,$header,$colspan=2) {
if(!$this->bInitialized) {
$header = $this->getVariableName() . " (" . $header . ")";
$this->bInitialized = true;
}
$str_i = ($this->bCollapsed) ? "style=\"font-style:italic\" " : "";
echo "<table cellspacing=2 cellpadding=3 class=\"dBug_".$type."\">
<tr>
<td ".$str_i."class=\"dBug_".$type."Header\" colspan=".$colspan." onClick='dBug_toggleTable(this)'>".$header."</td>
</tr>";
}
//create the table row header
function makeTDHeader($type,$header) {
$str_d = ($this->bCollapsed) ? " style=\"display:none\"" : "";
echo "<tr".$str_d.">
<td valign=\"top\" onClick='dBug_toggleRow(this)' class=\"dBug_".$type."Key\">".$header."</td>
<td>";
}
//close table row
function closeTDRow() {
return "</td></tr>\n";
}
//error
function error($type) {
$error="Error: Variable cannot be a";
// this just checks if the type starts with a vowel or "x" and displays either "a" or "an"
if(in_array(substr($type,0,1),array("a","e","i","o","u","x")))
$error.="n";
return ($error." ".$type." type");
}
//check variable type
function checkType($var) {
switch(gettype($var)) {
case "resource":
$this->varIsResource($var);
break;
case "object":
$this->varIsObject($var);
break;
case "array":
$this->varIsArray($var);
break;
case "NULL":
$this->varIsNULL();
break;
case "boolean":
$this->varIsBoolean($var);
break;
default:
$var=($var=="") ? "[empty string]" : $var;
echo "<table cellspacing=0><tr>\n<td>".$var."</td>\n</tr>\n</table>\n";
break;
}
}
//if variable is a NULL type
function varIsNULL() {
echo "NULL";
}
//if variable is a boolean type
function varIsBoolean($var) {
$var=($var==1) ? "TRUE" : "FALSE";
echo $var;
}
//if variable is an array type
function varIsArray($var) {
$var_ser = serialize($var);
array_push($this->arrHistory, $var_ser);
$this->makeTableHeader("array","array");
if(is_array($var)) {
foreach($var as $key=>$value) {
$this->makeTDHeader("array",$key);
//check for recursion
if(is_array($value)) {
$var_ser = serialize($value);
if(in_array($var_ser, $this->arrHistory, TRUE))
$value = "*RECURSION*";
}
if(in_array(gettype($value),$this->arrType))
$this->checkType($value);
else {
$value=(trim($value)=="") ? "[empty string]" : $value;
echo $value;
}
echo $this->closeTDRow();
}
}
else echo "<tr><td>".$this->error("array").$this->closeTDRow();
array_pop($this->arrHistory);
echo "</table>";
}
//if variable is an object type
function varIsObject($var) {
$var_ser = serialize($var);
array_push($this->arrHistory, $var_ser);
$this->makeTableHeader("object","object");
if(is_object($var)) {
$arrObjVars=get_object_vars($var);
foreach($arrObjVars as $key=>$value) {
$value=(!is_object($value) && !is_array($value) && trim($value)=="") ? "[empty string]" : $value;
$this->makeTDHeader("object",$key);
//check for recursion
if(is_object($value)||is_array($value)) {
$var_ser = serialize($value);
if(in_array($var_ser, $this->arrHistory, TRUE)) {
$value = (is_object($value)) ? "*RECURSION* -> $".get_class($value) : "*RECURSION*";
}
}
if(in_array(gettype($value),$this->arrType))
$this->checkType($value);
else echo $value;
echo $this->closeTDRow();
}
$arrObjMethods=get_class_methods(get_class($var));
foreach($arrObjMethods as $key=>$value) {
$this->makeTDHeader("object",$value);
echo "[function]".$this->closeTDRow();
}
}
else echo "<tr><td>".$this->error("object").$this->closeTDRow();
array_pop($this->arrHistory);
echo "</table>";
}
//if variable is a resource type
function varIsResource($var) {
$this->makeTableHeader("resourceC","resource",1);
echo "<tr>\n<td>\n";
switch(get_resource_type($var)) {
case "fbsql result":
case "mssql result":
case "msql query":
case "pgsql result":
case "sybase-db result":
case "sybase-ct result":
case "mysql result":
$db=current(explode(" ",get_resource_type($var)));
$this->varIsDBResource($var,$db);
break;
case "gd":
$this->varIsGDResource($var);
break;
case "xml":
$this->varIsXmlResource($var);
break;
default:
echo get_resource_type($var).$this->closeTDRow();
break;
}
echo $this->closeTDRow()."</table>\n";
}
//if variable is a database resource type
function varIsDBResource($var,$db="mysql") {
if($db == "pgsql")
$db = "pg";
if($db == "sybase-db" || $db == "sybase-ct")
$db = "sybase";
$arrFields = array("name","type","flags");
$numrows=call_user_func($db."_num_rows",$var);
$numfields=call_user_func($db."_num_fields",$var);
$this->makeTableHeader("resource",$db." result",$numfields+1);
echo "<tr><td class=\"dBug_resourceKey\">&nbsp;</td>";
for($i=0;$i<$numfields;$i++) {
$field_header = "";
for($j=0; $j<count($arrFields); $j++) {
$db_func = $db."_field_".$arrFields[$j];
if(function_exists($db_func)) {
$fheader = call_user_func($db_func, $var, $i). " ";
if($j==0)
$field_name = $fheader;
else
$field_header .= $fheader;
}
}
$field[$i]=call_user_func($db."_fetch_field",$var,$i);
echo "<td class=\"dBug_resourceKey\" title=\"".$field_header."\">".$field_name."</td>";
}
echo "</tr>";
for($i=0;$i<$numrows;$i++) {
$row=call_user_func($db."_fetch_array",$var,constant(strtoupper($db)."_ASSOC"));
echo "<tr>\n";
echo "<td class=\"dBug_resourceKey\">".($i+1)."</td>";
for($k=0;$k<$numfields;$k++) {
$tempField=$field[$k]->name;
$fieldrow=$row[($field[$k]->name)];
$fieldrow=($fieldrow=="") ? "[empty string]" : $fieldrow;
echo "<td>".$fieldrow."</td>\n";
}
echo "</tr>\n";
}
echo "</table>";
if($numrows>0)
call_user_func($db."_data_seek",$var,0);
}
//if variable is an image/gd resource type
function varIsGDResource($var) {
$this->makeTableHeader("resource","gd",2);
$this->makeTDHeader("resource","Width");
echo imagesx($var).$this->closeTDRow();
$this->makeTDHeader("resource","Height");
echo imagesy($var).$this->closeTDRow();
$this->makeTDHeader("resource","Colors");
echo imagecolorstotal($var).$this->closeTDRow();
echo "</table>";
}
//if variable is an xml type
function varIsXml($var) {
$this->varIsXmlResource($var);
}
//if variable is an xml resource type
function varIsXmlResource($var) {
$xml_parser=xml_parser_create();
xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0);
xml_set_element_handler($xml_parser,array(&$this,"xmlStartElement"),array(&$this,"xmlEndElement"));
xml_set_character_data_handler($xml_parser,array(&$this,"xmlCharacterData"));
xml_set_default_handler($xml_parser,array(&$this,"xmlDefaultHandler"));
$this->makeTableHeader("xml","xml document",2);
$this->makeTDHeader("xml","xmlRoot");
//attempt to open xml file
$bFile=(!($fp=@fopen($var,"r"))) ? false : true;
//read xml file
if($bFile) {
while($data=str_replace("\n","",fread($fp,4096)))
$this->xmlParse($xml_parser,$data,feof($fp));
}
//if xml is not a file, attempt to read it as a string
else {
if(!is_string($var)) {
echo $this->error("xml").$this->closeTDRow()."</table>\n";
return;
}
$data=$var;
$this->xmlParse($xml_parser,$data,1);
}
echo $this->closeTDRow()."</table>\n";
}
//parse xml
function xmlParse($xml_parser,$data,$bFinal) {
if (!xml_parse($xml_parser,$data,$bFinal)) {
die(sprintf("XML error: %s at line %d\n",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
//xml: inititiated when a start tag is encountered
function xmlStartElement($parser,$name,$attribs) {
$this->xmlAttrib[$this->xmlCount]=$attribs;
$this->xmlName[$this->xmlCount]=$name;
$this->xmlSData[$this->xmlCount]='$this->makeTableHeader("xml","xml element",2);';
$this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlName");';
$this->xmlSData[$this->xmlCount].='echo "<strong>'.$this->xmlName[$this->xmlCount].'</strong>".$this->closeTDRow();';
$this->xmlSData[$this->xmlCount].='$this->makeTDHeader("xml","xmlAttributes");';
if(count($attribs)>0)
$this->xmlSData[$this->xmlCount].='$this->varIsArray($this->xmlAttrib['.$this->xmlCount.']);';
else
$this->xmlSData[$this->xmlCount].='echo "&nbsp;";';
$this->xmlSData[$this->xmlCount].='echo $this->closeTDRow();';
$this->xmlCount++;
}
//xml: initiated when an end tag is encountered
function xmlEndElement($parser,$name) {
for($i=0;$i<$this->xmlCount;$i++) {
eval($this->xmlSData[$i]);
$this->makeTDHeader("xml","xmlText");
echo (!empty($this->xmlCData[$i])) ? $this->xmlCData[$i] : "&nbsp;";
echo $this->closeTDRow();
$this->makeTDHeader("xml","xmlComment");
echo (!empty($this->xmlDData[$i])) ? $this->xmlDData[$i] : "&nbsp;";
echo $this->closeTDRow();
$this->makeTDHeader("xml","xmlChildren");
unset($this->xmlCData[$i],$this->xmlDData[$i]);
}
echo $this->closeTDRow();
echo "</table>";
$this->xmlCount=0;
}
//xml: initiated when text between tags is encountered
function xmlCharacterData($parser,$data) {
$count=$this->xmlCount-1;
if(!empty($this->xmlCData[$count]))
$this->xmlCData[$count].=$data;
else
$this->xmlCData[$count]=$data;
}
//xml: initiated when a comment or other miscellaneous texts is encountered
function xmlDefaultHandler($parser,$data) {
//strip '<!--' and '-->' off comments
$data=str_replace(array("&lt;!--","--&gt;"),"",htmlspecialchars($data));
$count=$this->xmlCount-1;
if(!empty($this->xmlDData[$count]))
$this->xmlDData[$count].=$data;
else
$this->xmlDData[$count]=$data;
}
function initJSandCSS() {
echo <<<SCRIPTS
<script language="JavaScript">
/* code modified from ColdFusion's cfdump code */
function dBug_toggleRow(source) {
var target = (document.all) ? source.parentElement.cells[1] : source.parentNode.lastChild;
dBug_toggleTarget(target,dBug_toggleSource(source));
}
function dBug_toggleSource(source) {
if (source.style.fontStyle=='italic') {
source.style.fontStyle='normal';
source.title='click to collapse';
return 'open';
} else {
source.style.fontStyle='italic';
source.title='click to expand';
return 'closed';
}
}
function dBug_toggleTarget(target,switchToState) {
target.style.display = (switchToState=='open') ? '' : 'none';
}
function dBug_toggleTable(source) {
var switchToState=dBug_toggleSource(source);
if(document.all) {
var table=source.parentElement.parentElement;
for(var i=1;i<table.rows.length;i++) {
target=table.rows[i];
dBug_toggleTarget(target,switchToState);
}
}
else {
var table=source.parentNode.parentNode;
for (var i=1;i<table.childNodes.length;i++) {
target=table.childNodes[i];
if(target.style) {
dBug_toggleTarget(target,switchToState);
}
}
}
}
</script>
<style type="text/css">
table.dBug_array,table.dBug_object,table.dBug_resource,table.dBug_resourceC,table.dBug_xml
{ font-family:Verdana, Arial, Helvetica, sans-serif; color:#000000; font-size:12px; border-spacing:2px; display:table; border-collapse:separate; }
table.dBug_array td,
table.dBug_object td,
table.dBug_resource td,
table.dBug_resourceC td,
table.dBug_xml td
{ line-height:1.3; padding:3px; vertical-align:top; }
.dBug_arrayHeader,
.dBug_objectHeader,
.dBug_resourceHeader,
.dBug_resourceCHeader,
.dBug_xmlHeader
{ font-weight:bold; color:#FFFFFF; cursor:pointer; }
.dBug_arrayKey,
.dBug_objectKey,
.dBug_xmlKey
{ cursor:pointer; }
/* array */
table.dBug_array { background-color:#006600; }
table.dBug_array td { background-color:#FFFFFF; }
table.dBug_array td.dBug_arrayHeader { background-color:#009900; }
table.dBug_array td.dBug_arrayKey { background-color:#CCFFCC; }
/* object */
table.dBug_object { background-color:#0000CC; }
table.dBug_object td { background-color:#FFFFFF; }
table.dBug_object td.dBug_objectHeader { background-color:#4444CC; }
table.dBug_object td.dBug_objectKey { background-color:#CCDDFF; }
/* resource */
table.dBug_resourceC { background-color:#884488; }
table.dBug_resourceC td { background-color:#FFFFFF; }
table.dBug_resourceC td.dBug_resourceCHeader { background-color:#AA66AA; }
table.dBug_resourceC td.dBug_resourceCKey { background-color:#FFDDFF; }
/* resource */
table.dBug_resource { background-color:#884488; }
table.dBug_resource td { background-color:#FFFFFF; }
table.dBug_resource td.dBug_resourceHeader { background-color:#AA66AA; }
table.dBug_resource td.dBug_resourceKey { background-color:#FFDDFF; }
/* xml */
table.dBug_xml { background-color:#888888; }
table.dBug_xml td { background-color:#FFFFFF; }
table.dBug_xml td.dBug_xmlHeader { background-color:#AAAAAA; }
table.dBug_xml td.dBug_xmlKey { background-color:#DDDDDD; }
</style>
SCRIPTS;
}
}
?>