Location: PHPKode > scripts > Syndication Classes > SC_XML.class.php
<?php

/**
* SC_XML.class.php
*
* Copyright (c) 2005, James Logsdon
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author James Logsdon
* @version 1.1
* @package SC_XML
*/

/**
 * This class does the basic XML Formating. Error checking is done, for the most
 * part by this class.
 *
 * @author James Logsdon
 * @package XML
 * @version 1.1
 */
class SC_XML
{

	/**
	 * Tracks what tags are open
	 */
	var $_openTags = array();

	/**
	 * Current tag tree
	 */
	var $_currentTag = '';

	/**
	 * Current tag name
	 */
	var $_currentTagName = '';

	/**
	 * The actual XML data
	 */
	var $_xml = '';

	/**
	 * Indentation level
	 */
	var $_indent = 0;

	/**
	 * Debug Mode
	 */
	var $debug = false;

	/**
	 * Debug Messages
	 */
	var $_debug;

	/**
	 * Debug Indentation Level
	 */
	var $_debugIndent = 0;

	/**
	 * Is this a syndication feed?
	 */
	var $_isFeed = false;

	/**
	 * Start the XML document with a version attribute.
	 *
	 * @param string $version  The version of the XML document [1.0]
	 * @return void
	 */
	function SC_XML ( $version = "1.0" )
	{
		if ( isset ( $_GET['debug'] ) )
		{
			$this->debug = true;
		}
		$this->addToXml ( '<?xml version="' . $version . '"?>' );
	}

	/**
	 * Open an XML tag
	 *
	 * $attributes has to following format:
	 *
	 * array ( attributeName => attributeValue );
	 *
	 * @param string $tag  The tag to open
	 * @param array|null $attributes  An array of attributes to add to $tag
	 * @param bool $single  If true, then the tag has no closing tag (IE. <tag />)
	 * @param bool $newLine  Passed to XML::addToXml
	 * @param bool $indent  Passed to XML::addToXml
	 * @see XML::addToXml()
	 * @return void
	 */
	function openTag ( $tag, $attributes = null, $single = false, $newLine = true, $indent = true )
	{

		// If it's a feed, check for valid tags
		if ( $this->_isFeed AND isset ( $this->tags ) )
		{
			if ( $this->getTag ( $this->_currentTag . '[\'' . $tag . '\']' ) === false )
			{
				$this->Error ( $tag . ' is not a valid ' . $this->version() . ' tag.' );
			}
		}

		$this->Debug ( 'Opening tag ' . $tag );
		$this->_debugIndent++;
		$attr = '';
		if ( is_array ( $attributes ) )
		{
			foreach ( $attributes as $key=>$val )
			{
				$attr .= ' ' . $key . '="' . $this->Encode ( $val ) . '"';
			}
			$this->Debug ( 'Created Attribute String: ' . $attr );
		}

		// This is a tag that can be closed later
		if ( !$single )
		{
			$this->setCurrentTag ( $tag );
			$this->addToXml ( '<' . $tag . $attr . '>', $newLine, $indent );
			$this->_indent++;
			$this->Debug ( 'Opened non-single tag' );
		}
		// Close it now
		else
		{
			$this->addToXml ( '<' . $tag . $attr . ' />', $newLine, $indent );
			$this->Debug ( 'Opened single tag' );
		}
	}

	/**
	 * Add $val to the current open tag
	 *
	 * @param string $val  The value to add
	 * @param bool $newLine  Passed to XML::addToXml
	 * @param bool $indent  Passed to XML::addToXml
	 * @return void
	 */
	function addValue ( $val, $newLine = false, $indent = false )
	{
		$this->addToXml ( $this->Encode ( $val ), $newLine, $indent );
		$this->Debug ( 'Adding value to ' . $this->_currentTagName );
	}

	/**
	 * Close the current open tag
	 *
	 * @param bool $indent  Passed to XML::addToXml
	 * @see XML::removeCurrentTag()
	 * @return void
	 */
	function closeTag ( $indent = true )
	{
		$this->_indent--;
		$this->addToXml ( '</' . $this->_currentTagName . '>', true, $indent );
		$this->_debugIndent--;
		$this->Debug ( 'Closing current tag ' . $this->_currentTagName );
		$this->_debugIndent++;
		$this->removeCurrentTag ( );
		$this->_debugIndent--;
	}

	/**
	 * Set the current tag to $tag
	 *
	 * @param string $tag  The tag name
	 * @see XML::_currentTag
	 * @return void
	 */
	function setCurrentTag ( $tag )
	{
		$this->_currentTag .= '[\'' . $tag . '\']';
		eval ( "\$this->_openTags{$this->_currentTag} = array();" );
		
		// Uncomment to show the openTags array in Debug Mode
		//$this->Debug ( '<blockquote>' . print_r ( $this->_openTags, true ) . '</blockquote>' );
		$this->Debug ( 'Adding ' . $tag . ' to _currentTagName' );
		$this->setCurrentTagName ( $tag );
	}

	/**
	 * Set XML::_currentTagName
	 *
	 * @param string $tag  The tag name
	 * @see XML::setCurrentTag(), XML::_currentTagName
	 * @return void
	 */
	function setCurrentTagName ( $tag )
	{
		$this->_currentTagName = $tag;
		$this->Debug ( '_currentTagName set to ' . $tag );
	}

	/**
	 * Removes the current tag from usage
	 *
	 * @see XML::closeTag(), XML::_currentTag, XML::_currentTagName
	 * @return void
	 */
	function removeCurrentTag ( )
	{
		$this->Debug ( 'Removing tags from _currentTag' );
		$this->_debugIndent++;

		eval ( "unset ( \$this->_openTags{$this->_currentTag} );" );
		$last = strrpos ( $this->_currentTag, '[' );
		$this->_currentTag = substr ( $this->_currentTag, 0, $last );
		$this->Debug ( 'Removed last tag from string' );

		$tags = explode ( "']['", $this->_currentTag );
		$tag  = $tags[count ( $tags ) - 1];
		$tag  = str_replace ( '\']', '', $tag );
		$tag  = str_replace ( '[\'', '', $tag );
		$this->_currentTagName = $tag;

		$this->_debugIndent--;
	}

	/**
	 * Add a string to the document
	 *
	 * @param string $str  The string to add
	 * @param bool $newLine  If true, append \n to the string
	 * @param bool $indent  If true, indent the string
	 */
	function addToXml ( $str, $newLine = true, $indent = true )
	{
		if ( $indent )
		{
			$this->_xml .= $this->getIndent();
		}
		$this->_xml .= $str;
		if ( $newLine )
		{
			$this->_xml .= "\n";
		}
	}

	/**
	 * Encode special characters
	 */
	function Encode ( $text )
	{
		$this->Debug ( 'Encoding ' . $text );
		$search = array('\'','"','&','<','>');
		$replace = array('&apos;','&quot;','&amp;','&lt;','&gt;');
		return str_replace ( $search, $replace, $text );
	}

	/**
	 * Get the string of tabs to indent with
	 *
	 * @see XML::_indent
	 * @return string  The string of tabs
	 */
	function getIndent ( )
	{
		if ( $this->_indent == 0 )
		{
			return '';
		}
		$toReturn = "";
		for ( $i = 0; $i < $this->_indent; $i++ )
		{
			$toReturn .= "\t";
		}
		return $toReturn;
	}

	/**
	 * Add a message for debugging
	 *
	 * @param string $msg  The message
	 */
	function Debug ( $msg )
	{
		$indent = "";
		if ( $this->_debugIndent > 0 )
		{
			for ( $i = 0; $i < $this->_debugIndent; $i++ )
			{
				$indent .= "\t";
			}
		}

		$this->_debug .= $indent . $msg . "\n";
	}

	/**
	* Watered down version of http://php5.girsbrain.org/get.php
	*
	* @param string $var The string of array keys
	* @return mixed  The contents of the eval
	*/
	function getTag ( $var = null )
	{
		$this->Debug ( 'Getting tags[\'' . $this->version . '\']' . $var );
		// Get the array string (['0']['0']['0'] for example)
		$varArray = 'tags[\'' . $this->version . '\']' . $var;

		// Get the value of the array
		$toEval = "if(isset(\$this->{$varArray})){return \$this->{$varArray};}else{return false;}";
		$val = eval ( $toEval );

		return $val;
	}

	/**
	 * Output an error then exit the application.
	 *
	 * If debugging is enabled, output the Debug Messages
	 *
	 * @param string $msg  The error message
	 */
	function Error ( $msg )
	{
		echo '<b>Error:</b> ' . $msg;
		if ( $this->debug )
		{
			echo '<pre>' . $this->_debug . '</pre>';
			unset ( $this->_debug );
			echo '<pre>' . print_r ( $this, true ) . '</pre>';
		}
		exit;
	}

	/**
	 * Check for open tags. If everything is clean, return the document.
	 *
	 * Also, if SC_XML::$debug is set to (bool) true return debug output.
	 *
	 * @see XML::debug,XML::_currentTag
	 * @return string  The XML Document
	 */
	function Go ( )
	{
		if ( $this->_currentTag != '' )
		{
			$tags = str_replace ( '[\'', '', $this->_currentTag );
			$tags = str_replace ( '\']', ', ', $tags );
			$tags = substr ( $tags, 0, strlen ( $tags ) - 2 );
			$this->Error ( 'You have un-closed tags! Tag Tree: ' . $tags );
		}

		if ( !$this->debug )
		{
			return $this->_xml;
		}
		else
		{
			$DebugReturn = '<pre>' . $this->_debug . '</pre>';
			unset ( $this->_debug );
			$DebugReturn .= '<pre>' . print_r ( $this, true ) . '</pre>';
			$DebugReturn .= '<pre>' . htmlspecialchars ( $this->_xml ) . '</pre>';
			return $DebugReturn;
		}
	}
}

?>
Return current item: Syndication Classes