【Node.php】
<?php
class Node implements ArrayAccess {
    protected $attributes = array();
    protected $children = array(); // Accessed by $this[number]
    protected $nodeName = 'div';
    
    protected static function attributeNameWarning() {
        triger_error('The name of the attribute cannot be an empty string.', E_USER_WARNING);
    }
    
    protected static function nodeNameWarning() {
        triger_error('The name of the node cannot be an empty string.', E_USER_WARNING);
    }
    /*static public function render($string) {
    }*/
    
    function __construct($nodeName = 'div') {
        $this->setNodeName($nodeName);
    }
    
    /* ------  Functions for ArrayAccess ------ */
    
    public function offsetExists($offset) {
        if (is_numeric($offset)) {
            return isset($this->children[$offset]);
        } else {
            return $this->hasAttribute($offset);
        }
    }
    
    public function offsetGet($offset) {
        if (is_numeric($offset)) {
            return $this->children[$offset];
        } else {
            return $this->getAttribute($offset);
        }
    }
    
    public function offsetSet($offset, $value) {
        if (is_numeric($offset)) {
            $this->children[$offset] = $value;
        } else {
            return $this->setAttribute($offset, $value);
        }
    }
    
    public function offsetUnset($offset) {
        if (is_numeric($offset)) {
            unset($this->children[$offset]);
        } else {
            $this->removeAttribute($offset);
        }
    }
    /* ------ END OF Functions for ArrayAccess ------ */
    
    public function getAttribute($name) {
        $name = trim($name);
        if (isset($this->attributes[$name])) {
            return $this->attributes[$name];
        } else {
            return '';
        }
    }
    
    public function getLength() {
        return count($this->children);
    }
    
    public function getNodeName() {
        return $this->nodeName;
    }
    
    public function hasAttribute($name) {
        if (empty($name)) {
            self::attributeNameWarning();
            return;
        }
        return isset($this->attributes[$name]);
    }
    
    public function removeAttribute($name) {
        if (empty($name)) {
            self::attributeNameWarning();
            return;
        }
        unset($this->attributes[$name]);
    }
    
    public function render($indentation = NULL, $noEndingSlash = false) {
        $str = $beginning = '';
        if (!is_null($indentation) && $indentation > 0) {
            $beginning = str_repeat('  ', $indentation);
            $str .= $beginning;
        }
        $str .= '<' . $this->nodeName;
        foreach ($this->attributes as $name => $value) {
            $str .= " $name";
            if (!is_null($value)) {
                $str .= "=\"$value\"";
            }
        }
        $len = $this->getLength();
        if ($len > 0) {
            $str .= '>';
            if ($len == 1 && is_string($this->children[0])) {
                $str .= $this->children[0];
            } else {
                $str .= "\n";
                foreach ($this->children as $child) {
                    if ($child instanceof Node) {
                        if (is_null($indentation)) {
                            $newIndentation = null;
                        } else {
                            $newIndentation = $indentation + 1;
                        }
                        $child = $child->render($newIndentation, $noEndingSlash);
                    } else {
                        $child = "  {$beginning}{$child}\n";
                    }
                    $str .= $child;
                }
                $str .= $beginning;
            }
            $str .= '</' . $this->nodeName . '>';
        } else {
            if ($noEndingSlash) {
                $str .= '>';
            } else {
                $str .= ' />';
            }
        }
        $str .= "\n";
        return $str;
    }
    
    public function setAttribute($name, $value = NULL) {
        if (empty($name)) {
            self::attributeNameWarning();
            return;
        }
        $this->attributes[$name] = $value;
    }
    
    public function setNodeName($newNodeName) {
        if (empty($newNodeName)) {
            self::nodeNameWarning();
            return;
        }
        $this->nodeName = trim($newNodeName);
    }
}
      