<?php /** * Dump structured data, i.e., Objects and Arrays, in either plain text or * html. This is a class wrapper for a couple of utility routines that I use * all the time. It's handier to have them as a class. * * Its also the class interface for logging functions that I use in developing * web enabled applications. * * @author Dick Munroe <munroe@csworks.com> * @copyright copyright @ by Dick Munroe, 2004 * @license http://opensource.org/licenses/gpl-license.php GNU Public License * @package StructuredDataDumper * @version 1.0.4 */ // // Edit History: // // Dick Munroe munroe@cworks.com 04-Dec-2004 // Initial version created. // // Dick Munroe munroe@csworks.com 08-Dec-2004 // Translate < to < for html output. // // Dick Munroe munroe@csworks.com 23-Dec-2004 // Add interface for writing "stuff". Extend SDD // to get things "written". // // Dick Munroe munroe@csworks.com 25-Dec-2004 // If a class extends a base class, but doesn't add // data members, a warning winds up appearing when // printing. // Added a memeber to fetch the state of the logging // flag. // // Dick Munroe munroe@csworks.com 11-Mar-2006 // The test for html flag should have assumed that // $this can be defined for objects calling SDD::dump. // // Dick Munroe (munroe@csworks.com) 22-Mar-2006 // Add a function to generate "newlines". // class SDD { /** * HTML to be generated flag. */ var $m_htmlFlag ; /** * logging flag. */ var $m_logging = false ; /** * In memory log file. */ var $m_log = array() ; /** * Constructor. * * @access public * @param boolean $theHTMLFlag [optional] True if HTML is to be generated. * If omitted, $_SERVER is used to "guess" the state of * the HTML flag. Be default, HTML is generated when * accessed by a web server. * @param boolean $theLoggingFlag [optional] the state of logging for * this object. By default, logging is off. */ function SDD($theHtmlFlag=null, $theLoggingFlag=false) { if ($theHtmlFlag === null) { $theHtmlFlag = (!empty($_SERVER['DOCUMENT_ROOT'])) ; } $this->m_htmlFlag = $theHtmlFlag ; $this->m_logging = $theLoggingFlag ; } /** * Close the log file. * * @access public * @abstract */ function close() { } /** * Dump a structured variable. * * @static * @param mixed $theVariable the variable to be dumped. * @param boolean $theHtmlFlag [optional] true if HTML is to be generated, * false if plain text is to be generated, null (default) if * dump is to guess which to display. * @return string The data to be displayed. * @link http://www.php.net/manual/en/reserved.variables.php#reserved.variables.server Uses $_SERVER */ function dump(&$theVariable, $theHtmlFlag=null) { if ($theHtmlFlag === null) { if (empty($this)) { $theHtmlFlag = (!empty($_SERVER['DOCUMENT_ROOT'])) ; } else { if (is_subclass_of($this, "sdd")) { $theHtmlFlag = $this->m_htmlFlag ; } else { $theHtmlFlag = (!empty($_SERVER['DOCUMENT_ROOT'])) ; } } } switch (gettype($theVariable)) { case 'array': { return SDD::dArray($theVariable, $theHtmlFlag) ; } case 'object': { return SDD::dObject($theVariable, $theHtmlFlag) ; } default: { return SDD::scalar($theVariable, $theHtmlFlag) ; } } } /** * Dump the contents of an array. * * @param array $theArray the array whose contents are to be displayed. * @param boolean $theHTMLFlag True if an HTML table is to be generated, * false otherwise. * @param string $theIndent [optional] Used by SDD::dArray during recursion * to get indenting right. * @return string The display form of the array. */ function dArray(&$theArray, $theHTMLFlag, $theIndent = "") { $theOutput = array() ; foreach($theArray as $theIndex => $theValue) { if (is_array($theValue)) { $theString = SDD::dArray($theValue, $theHTMLFlag, $theIndent . " ") ; $theOutput[$theIndex] = substr($theString, 0, strlen($theString) - 1) ; } else if (is_object($theValue)) { $theOutput[$theIndex] = SDD::dObject($theValue, $theHTMLFlag) ; } else { $theOutput[$theIndex] = ($theHTMLFlag ? preg_replace('|<|s', '<', var_export($theValue, true)) : var_export($theValue, true)) ; } } if ($theHTMLFlag) { $theString = "<table border=1>\n" ; $theString .= "<tr><td align=left>Array (</td></tr>\n" ; foreach ($theOutput as $theIndex => $theVariableOutput) { $theString .= "<tr>\n<td align=right>$theIndex = ></td><td align=left>\n$theVariableOutput\n</td>\n</tr>\n" ; } $theString .= "<tr><td align=left>)</td></tr>\n" ; $theString .= "</table>\n" ; } else { $theString = "Array\n$theIndent(\n" ; foreach ($theOutput as $theIndex => $theVariableOutput) { $theString .= "$theIndent [$theIndex] => " . $theVariableOutput . "\n" ; } $theString .= "$theIndent)\n" ; } return $theString ; } /** * Dump the contents of an object. * * Provide a structured display of an object and all the * classes from which it was derived. The contents of * the object is displayed from most derived to the base * class, in order. * * @param object $theObject the object to be dumped. * @param boolean $theHTMLFlag true if HTML is to be generated. * @return string the display form of the object. */ function dObject(&$theObject, $theHTMLFlag) { $theObjectVars = get_object_vars($theObject) ; // // Get the inheritance tree starting with the object and going // through all the parent classes from there. // $theClass = get_class($theObject) ; $theClasses[] = $theClass ; while ($theClass = get_parent_class($theClass)) { $theClasses[] = $theClass ; } // // Get all the class variables for each class in the inheritance // tree. There will be some duplication, but we'll sort that out // in the output process. // foreach($theClasses as $theClass) { $theClassVars[$theClass] = get_class_vars($theClass) ; } // // Put the inheritance tree from base class to most derived order // (this is how we get rid of duplication of the variable names) // Go through the object variables starting with the base class, // capture the output and delete the variable from the object // variables. // $theClasses = array_reverse($theClasses) ; $theOutput = array() ; foreach ($theClasses as $theClass) { $theOutput[$theClass] = array() ; foreach ($theClassVars[$theClass] as $theVariable => $value) { if (array_key_exists($theVariable, $theObjectVars)) { if (is_array($theObjectVars[$theVariable])) { $theOutput[$theClass][] = $theVariable . " = " . SDD::dArray($theObjectVars[$theVariable], $theHTMLFlag) ; } else if (is_object($theObjectVars[$theVariable])) { $theOutput[$theClass][] = $theVariable . " = " . SDD::dObject($theObjectVars[$theVariable], $theHTMLFlag) ; } else { $theOutput[$theClass][] = $theVariable . " = " . ($theHTMLFlag ? preg_replace('|<|s', '<', var_export($theObjectVars[$theVariable], true)) : var_export($theObjectVars[$theVariable], true)) ; } unset($theObjectVars[$theVariable]) ; } } } // // Put the classes back in most derived order for generating printable // output. // $theClasses = array_reverse($theClasses) ; if ($theHTMLFlag) { $theString = "<table>\n<thead>\n" ; foreach ($theClasses as $theClass) { $theString .= "<th>\n$theClass\n</th>\n" ; } $theString .= "</thead>\n<tr valign=top>\n" ; foreach ($theClasses as $theClass) { $theString .= "<td>\n<table border=1>\n" ; foreach ($theOutput[$theClass] as $theVariableOutput) { $theString .= "<tr>\n<td>\n$theVariableOutput\n</td>\n</tr>\n" ; } $theString .= "</table>\n</td>\n" ; } $theString .= "</tr>\n</table>\n" ; } else { $classIndent = "" ; $classDataIndent = " " ; $theString = "" ; foreach ($theClasses as $theClass) { $theString .= "{$classIndent}class $theClass\n\n" ; foreach ($theOutput[$theClass] as $theVariableOutput) { $theString .= "$classDataIndent$theVariableOutput\n" ; } $theString .= "\n" ; $classIndent .= " " ; $classDataIndent .= " " ; } } return $theString ; } /** * Write a debugging value to a log file. * * @access public * @abstract * @param mixed Data to be logged. * @param string $theHeader [optional] string to be emitted prior to * logging the data. By default it is a date/time * stamp. */ function log(&$theData, $theHeader=null) { $theHeader = date('[Y-m-d H:i:s]: ') . $theHeader ; if ($this->m_logging) { if ($this->m_htmlFlag) { $xxx = $this->dump($theData) ; if (substr($xxx, 0, 5) == '<pre>') { $xxx = '<pre>' . $theHeader . substr($xxx, 5) ; } else { $xxx = $theHeader . $xxx ; } $this->writeLog($xxx) ; } else { $xxx = $theHeader . $this->dump($theData) ; $this->writeLog($xxx) ; } } } /** * @desc Generate context specific new line equivalents. * @param integer [optional] the number of newlines. * @param boolean [optional] true if generating html newlines. * @return string newlines. * @access public */ function newline($theCount=1, $theHtmlFlag=null) { if ($theHtmlFlag === null) { if (empty($this)) { $theHtmlFlag = (!empty($_SERVER['DOCUMENT_ROOT'])) ; } else { if (is_subclass_of($this, "sdd")) { $theHtmlFlag = $this->m_htmlFlag ; } else { $theHtmlFlag = (!empty($_SERVER['DOCUMENT_ROOT'])) ; } } } if ($theHtmlFlag) { return str_repeat("<br />", max($theCount, 0)) . "\n" ; } else { return str_repeat("\n", max($theCount, 0)) ; } } /** * Dump any scalar value * * @param mixed $theVariable the variable to be dumped. * @param boolean $theHtmlFlag true if html is to be generated. */ function scalar(&$theVariable, $theHtmlFlag) { if ($theHtmlFlag) { return "<pre>" . preg_replace('|<|s', '<', var_export($theVariable, true)) . "</pre>" ; } else { return var_export($theVariable, true) ; } } /** * Write data to the log file. * * @access public * @abstract * @parameter string $theData [by reference] the data to be written * into the log file. * @return integer the number of bytes written into the log file. */ function writeLog(&$theData) { return strlen($this->m_log[] = $theData) ; } /** * Return the state of the logging flag. * * @access public * @return boolean */ function getLogging() { return $this->m_logging ; } /** * Set the state of the logging flag. * * @access public * @return boolean */ function setLogging($theLogging=false) { $this->m_logging = $theLogging ; } } ?>