Location: PHPKode > scripts > QDataObject > QTag.class.php
<?php

/**
 *
 * QTag
 * @package
 * @subpackage
 * @author Thomas Sch&#65533;fer
 * @since 05.07.2008 08:20:08
 * @desc
 */

class QTag {

	const OT = '<';
	const CT = '>';
	const ZT = '</';
	const LB = "\n";

	const eq = "=";
	const plus = "+";
	const minus = "-";
	const hash = "#";
	const colon = ",";
	const dot = ",";
	const semicolon = ";";
	const pipe = "|";
	const ddot = ":";
	const uscore = "_";
	const blank = " ";
	const string = "";
	const ask = "?";
	const spacer = " ";
	const amp = "&amp;";

	protected $charset = "UTF-8";
	protected $tag;
	protected $textNode;
	protected $attributeNode;
	protected $childs;

	public function __construct($tag = 'root', $attributes = null) {
		$this->tag = $tag;
		if($attributes) {
			$this->addAttributes($attributes);
		}
	}

	public static function factory($tag='div') {
		$num = func_num_args();
		switch($num){
			default:
				$args = func_get_args();
				$tag = new QTag($args[0]);
				unset($args[0]);
				if(is_array($args[1])) {
					$tag->addAttributes($args[1]);
				}
				return $tag;
			case 1:
				$args = func_get_args();
				return new QTag($args[0]);
			case 0:
				$exception = new Exception("Factory needs for minimum a tag element string and optionally a second array with attributes.");
				echo $exception->getMessage();
				return false;
		}
	}

	public static function camelize($lower_case_and_underscored_word) {
		$replace = str_replace(self::blank, self::string, ucwords(str_replace(self::uscore, self::blank, $lower_case_and_underscored_word)));
		return $replace;
	}

	public function getTag() {
		return $this->tag;
	}

	public function hasTag() {
		return $this->tag ? true : false;
	}

	public function add($data) {
		if(!$data instanceof QTag and !is_array($data)) {
			$this->textNode = $data;
		} else if(!$data instanceof QTag and is_array($data)) {
			foreach($data as $argument) {
				$this->childs[] = new QTag($argument);
			}
		}
		if($data instanceof QTag) {
			if(is_array($data)) {
				$container = QTag::factory("container")->add($data);
				$this->childs[] = $container;
			} else {
				$this->childs[] = $data;
			}
		}

		return $this;
	}

	public function resetContent() {
		$this->childs = array();
		$arguments = func_get_args();
		call_user_func_array( array(&$this, "add"), $arguments);
		return $this;
	}

	public function countContent( ) {
		return count($this->childs);
	}

	public function countChilds() {
		return $this->countContent();
	}
	
	public function hasChilds($index=null) {
		if(isset($this->childs) and $index) { // works on child objects by index 
			return isset($this->childs[$index]) ? true : false;
		} else {
			return empty($this->childs) ? false : true;
		}
	}

	public function getChilds() {
		return $this->childs;
	}

	public function getChild($index=0) {
		if($this->countChilds() > $index and $this->hasChilds($index)) {
			return $this->childs[$index];
		} else {
			$e = new Exception("Err (".__FILE__.", ".__LINE__."): Index has to be lower than amount of children.");
			echo $e->getMessage();
			return false;
		}
	}

	public function getLastChild() {
		$c = $this->countChilds();
		if($c > 0) {
			return $this->getChild($c-1);
		} else {
			return false;
		}
	}

	public function getFirstChild() {
		$c = $this->countChilds();
		if($c > 0) {
			return $this->childs[0];
		} else {
			return false;
		}
	}

	/**
	 * insert method to add objects into an existing object tree
	 * @param string $type before/after
	 * @param mixed $offset integer value/last/first
	 * @param QTag object injected object
	 * @return self
	 */
	public function insert($type='before', $index, $object) {
		$lc = $this->countChilds();
		$childs = array();
		$added = false;
		switch($index) {
			case is_integer($index): // operate on index number
				foreach($this->getChilds() as $key => $child) {
					if($type == "before" and $index==$key) {
						$childs[] = $object;
						$added=true;
					}
					$childs[] = $child;
					if($type == "last" and $index==$key) {
						$childs[] = $object;
						$added=true;
					}
				}
				break;
			case "first":
			case "last":
				foreach($this->getChilds() as $key => $child) {
					if($type=="before") {
						if(empty($added) and $index=="first" and $key==$lc-1) {
							$childs[] = $object;
							$added=true;
						}
						$childs[] = $child;
						if(empty($added) and $index=="last" and $key==$lc-1) {
							$childs[] = $object;
							$added=true;
						}
					} elseif($type=="after") {
						$childs[] = $child;
						if(empty($added)) {
							$last = $object;
							$added=true;
						}
					} else {
						$childs[] = $child;
					}
				}
				if($last) {
					$childs[] = $last;
				}
				break;
		}
		// add new child set
		$this->childs = $childs;
		return $this;
	}

	public function getAttribute($attributeName) {
		return $this->attributeNode[$attributeName];
	}

	public function setAttribute($attributeName, $attributeValue) {
		$this->attributeNode[$attributeName] = $attributeValue;
		return $this;
	}

	public function addAttributes($attributes) {
		if(is_array($attributes)) {
			foreach($attributes as $attributeName => $attributeValue) {
				$this->setAttribute($attributeName, $attributeValue);
			}
		}
		return $this;
	}

	protected function getAttributes() {
		return $this->attributeNode;
	}

	protected function unsetAttributes() {
		unset($this->attributeNode);
	}

	public function hasAttributes() {
		return empty($this->attributeNode) ? false : true;
	}

	protected function hasNode() {
		return empty($this->textNode) ? false : true;
	}

	public function getNode() {
		return $this->textNode;
	}

	public function setNode($value) {
		$this->textNode = (string) $value;
		return $this;
	}

	protected function unsetNode() {
		unset($this->textNode);
	}

	protected function getCharset() {
		return $this->charset;
	}

	public function setCharset($charset) {
		$this->charset = $charset;
		return $this;
	}

	protected static function indent($level) {
		return str_repeat(" ", $level * 2);
	}

	protected static function cleanContainer($string) {
		$clean = array('<container>'=>'','</container>'=>'');
		return strtr($string, $clean);
	}

	protected function cleanXHTML($string, $doctype='xhtml') {
		$clean = array(
            '></area>' => ' />',
            '></base>' => ' />',
            '></basefont>' => ' />',
            '></br>' => ' />',
            '></col>' => ' />',
            '></frame>' => ' />',
            '></hr>' => ' />',
            '></img>' => ' />',
            '></input>' => ' />',
            '></isindex>' => ' />',
            '></link>' => ' />',
            '></meta>' => ' />',
            '></param>' => ' />',
            'default:' => '',
            '&quot;' => '&#34;',
            '&amp;' =>  '&#38;',
            '&apos;' => '&#39;',
            '&lt;' =>   '&#60;',
            '&gt;' =>   '&#62;',
            '&nbsp;' => '&#160;',
            '&copy;' => '&#169;',
            '&laquo;' => '&#171;',
            '&reg;' =>   '&#174;',
            '&raquo;' => '&#187;',
            '&trade;' => '&#8482;',
			
		);

		switch($doctype) {
			case "xhtml":
				$string = strtr($string,
				array(
        				'<?xml version="1.0"?>' => '<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
						'<html>' => '<html xmlns="http://www.w3.org/1999/xhtml">'        			
						));
						break;
			case "html4loose":
				$string = strtr($string,
				array('<?xml version="1.0"?>' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'));
				break;
		}
		return strtr($string, $clean);
	}

	public function doRender() {
		return $this->render("container");
	}

	public function render($type="xml", $doctype='xhtml') {

		if($type=="container") {
			$xmlstring = '';
			$xmlstring .= $this->_render($this->getChilds(), '', $this->getTag(), 0, $this, $noXML);
			return self::cleanContainer($xmlstring);
		} else {
			$xmlstring = '';
			$xmlstring .= $this->_render($this->getChilds(), $this->getTag());
			$xmlstring .= self::ZT . $this->tag . self::CT;
		}

		$xmlObject = simplexml_load_string($xmlstring, null, LIBXML_NOCDATA);

		$dom = new DomDocument();
		$dom->preserveWhiteSpace = true;
		$dom->formatOutput = true;

		$importXmlObject = $dom->importNode(dom_import_simplexml($xmlObject), true);
		$dom->appendChild($importXmlObject);

		switch($type) {
			default:
			case "xml":
				return self::cleanContainer(self::cleanXHTML($dom->saveXML(null, LIBXML_NOEMPTYTAG), $doctype));
			case "container":
			case "html":
				$domHTML = self::cleanContainer(DOMDocument::loadHTML($dom->saveHTML()));
				return $domHTML->saveHTML();
		}
	}

	protected static function _renderAttributes($object) {
		if(is_object($object) and $object->hasAttributes()) {
			foreach($object->getAttributes() as $attrName => $attrVal)
			{
				$xmlAttributes .= sprintf(' %s="%s"',$attrName,addslashes($attrVal));
			}
			$object->unsetAttributes();
			$check=true;
		}
		return $xmlAttributes;
	}

	protected static function _renderNode($object){
		if(is_object($object) and $object->hasNode())
		{
			$xmlNode = $object->getNode();
			if(strlen($xmlNode))
			{
				$xmlNode = $xmlNode;
			}
			else
			{
				$xmlNode = "";
			}
			$check = true;
			$object->unsetNode();
		}
		return $xmlNode;
	}

	protected function _renderElement($element, $xmlAttributes, $xmlNode, $level) {
		return	self::LB .
		self::indent($level) .
		self::OT .
		$element .
		$xmlAttributes .
		self::CT .
		$xmlNode;
	}

	protected function _render($objArray, $elementName, $element = false, $level=0, $previousObject = null, $noXML=false) {

		$currentLevel = $level + 1;
		$xmlString = ($elementName) ? self::OT . $this->tag . self::CT : "";
		$xmlNode = "";
		$xmlAttributes = "";

		$xmlAttributes = self::_renderAttributes($previousObject);
		$xmlNode = self::_renderNode($previousObject);
		if($element){
			$xmlString .= $this->_renderElement($element, $xmlAttributes, $xmlNode, $level);
		}

		if(is_array($objArray)) {
			foreach($objArray as $key => $object){
				$xmlString .= $this->_render($object->getChilds(),"",$object->getTag(),$currentLevel, $object, $noXML);
			}
		}

		if($element){
			$xmlString 	.= ( count($objArray) > 0 ? self::LB . self::indent($level) : "" )
			. self::ZT . $element . self::CT;
		}

		if($elementName) {
			$xmlString .= self::LB;
			if($noXML==true) {
				$xmlString = $xmlString;
			} else {
				$xmlString = "<?xml version='1.0' encoding='".$this->getCharset()."'?>\n" . $xmlString;
			}
		}
		return $xmlString;

	}

}
Return current item: QDataObject