* @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 = "
\n" ;
          $theString .= "| Array ( | 
\n" ;
          foreach ($theOutput as $theIndex => $theVariableOutput)
            {
              $theString .= "\n| $theIndex = > | \n$theVariableOutput\n\n | 
\n" ;
            }
          $theString .= "| ) | 
\n" ;
          $theString .= "
\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 = "\n\n" ;
          foreach ($theClasses as $theClass)
            {
              $theString .= "| \n$theClass\n\n" ;
            }
          $theString .= "\n | \n" ;
          foreach ($theClasses as $theClass)
            {
              $theString .= "| \n \n" ;
            }
          $theString .= "\n" ;
              foreach ($theOutput[$theClass] as $theVariableOutput)
                {
                  $theString .= "\n\n\n" ;
                }
              $theString .= "| \n$theVariableOutput\n\n |  | 
\n
\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) == '')
                {
                  $xxx = '' . $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("
", 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 "" . preg_replace('|<|s', '<', var_export($theVariable, true)) . "" ;
        }
      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 ;
    }
}
?>