view class.template.php @ 15:51645aad97a8

Simplification du code, impliquant un changement important de comportement. Avant, les balises dans l'espace de nom template n'apparaissant pas dans les paramètres d'applications étaient supprimés. Maintenant, leur contenu passe dans le template de base.
author Franck Deroche <webmaster@defr.org>
date Mon, 22 Oct 2007 16:04:12 +0200
parents a7ce1a423cbe
children 7a3ce31086b3
line wrap: on
line source
<?php
class Template {
    private $xmlDocument;
    private $xmlXPath;
    
    const NS = 'http://defr.net/2007/template';
    
    function __construct($fileName, $shouldValidate = false) {
        $this->xmlDocument = new DOMDocument();
        $this->xmlDocument->validateOnParse = $shouldValidate;
        $this->xmlDocument->preserveWhiteSpace = false;
        $this->xmlDocument->loadXML(file_get_contents($fileName));
        $this->xmlDocument->preserveWhiteSpace = false;
        $this->xmlXPath = new DOMXPath($this->xmlDocument);
        $this->xmlXPath->registerNamespace('_t', Template::NS);
    }
    
    function apply($selector, $obj, DOMElement $root = null) {
        if(!($root instanceof DOMElement))
            $root = $this->xmlDocument->documentElement;
        $rootSelector = $this->parseSelector($selector, $root);
        if(is_array($obj))
            $this->applyClone($rootSelector, $obj, $root);
        else {
            foreach($rootSelector->nodes as $node) {
                $content = $this->parseReplacement($obj); 
                if(isset($rootSelector->attribute))
                    $node->setAttribute($rootSelector->attribute, $obj);
                else
                    $this->setNodeContent($node, $content);
            }
        }
    }

    function applyClone($rootSelector, $obj, $root) {
        foreach($obj as $array) {
            $nodeName = key($array);
	        foreach($rootSelector->nodes as $node) {
                $tmp = $this->getClonedNode($node, $nodeName);
                $futureNode = $tmp->clone;
                $futureNode = $node->insertBefore($futureNode, $tmp->orig);
                foreach($array as $sel => $test) {
                    $locSelector = $this->parseSelector($sel, $node);
                    if(isset($locSelector->attribute))
                        $futureNode->setAttribute($locSelector->attribute, $test);
                    else {
                        $content = $this->parseReplacement($test);
                        $node->insertBefore($content, $tmp->orig);
                        $this->setNodeContent($futureNode, $content);
                    }
                }
	        }
        }
    }
    
    function getClonedNode($node, $childNodeName) {
        $candidates = $this->parseSelector($childNodeName, $node)->nodes;
        foreach($candidates as $candidate) {
            if($candidate->getAttributeNS(Template::NS, 'toClone') == 'true') {
                $tmp = array();
                $tmp['orig'] = $candidate;
                $cnode = $candidate->cloneNode(true);
                $cnode->removeAttributeNS(Template::NS, 'toClone');
                $tmp['clone'] = $cnode;
                return (object)$tmp;
            }
        }
    }
    
    function parseSelector($selector, DOMElement $root) {
        $obj = array();
        $pos = strpos($selector, '@');
        if($pos !== false) {
            $obj['attribute'] = substr($selector,$pos +1);
            $selector = substr($selector, 0, $pos);
        }
        if($selector[0] == '#') {
            $obj['xpath'] = "//*[@_t:id='" . substr($selector, 1) . "']";
            $obj['nodes'] = $this->xmlXPath->query($obj['xpath'], $root);
        }
        else {
            $obj['nodeName'] = $selector;
            $obj['nodes'] = $root->getElementsByTagName($selector);
        }
        return (object)$obj;
    }
    
    function parseReplacement($obj) {
        $retVal = NULL;
        if(is_string($obj))
            $retVal = $this->xmlDocument->createTextNode($obj);
        else if($obj instanceof DOMDocument)
            $retVal = $obj->documentElement->clone(true);
        else if($obj instanceof DOMNode)
            $retVal = $obj->clone(true);
        else if($obj instanceof Template) {
            $node = $obj->xmlDocument->documentElement;
            $retVal = $this->xmlDocument->importNode($node, true);
        }
        return $retVal;
    }
    
    function setParams($array) {
        foreach($array as $selector => $obj) {
            $this->apply($selector, $obj);
        }
    }
    
    function replaceNode(DOMNode $node, DOMNode $content) {
        $parent = $node->parentNode;
        $parent->replaceChild($content, $node);
    }
    
    function setNodeContent(DOMElement $node, DOMNode $content) {
        // Suppress existing childs
        foreach($node->childNodes as $child) {
            $node->removeChild($child);
        }
        // Add the new child
        $node->appendChild($content);
    }
    
    function clean() {
        // Suppression des noeuds à cloner
        $nodes = $this->xmlXPath->query("//*[@_t:toClone]|//_t:*");
        foreach($nodes as $node) {
            if($node->parentNode) {
                $parent = $node->parentNode;
                if(!$node->hasAttributeNS(Template::NS, 'toClone')) {
                    while($node->hasChildNodes())
                        $parent->insertBefore($node->firstChild, $node);
                }
                $parent->removeChild($node);
            }
        }
    }
    
    function __toString() {
        $this->clean();
        $this->xmlDocument->formatOutput = true;
        $this->xmlDocument->normalizeDocument();
        return $this->xmlDocument->saveXML();
    }
}