<?php
/**
*
* QTag
* @package
* @subpackage
* @author Thomas Sch�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 = "&";
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:' => '',
'"' => '"',
'&' => '&',
''' => ''',
'<' => '<',
'>' => '>',
' ' => ' ',
'©' => '©',
'«' => '«',
'®' => '®',
'»' => '»',
'™' => '™',
);
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;
}
}