Mercurial > defr > templates
view class.template.php @ 19:fb0e206f1acf tip
Possibilité de supprimer les attributs dans l'espace de nom de transformation
Si des attributs sont présents dans un espace de nom autre que celui par
défaut dans un document XHTML, ce dernier n'est plus valide au sens
strict d'après la documentation du W3C[1], et en conséquence le validator
se plaint. Par conséquent, il est désormais possible de définir un paramètre
du template amenant la suppression de tous ses attributs.
Par défaut, on ne les supprime pas, car il pourrait y avoir des utilisations
valides d'une part (CSS étant au courant de l'existence des espaces de noms
entre autres), et que cela entraine une pénalité au niveau du temps
d'execution d'autre part, puisqu'il faut retrouver ses attributs avant de les
supprimer (la pénalité ne devrait pas être trop grande grâce à XPath, mais
non nul quand même)
Ajout d'un test unitaire qui va bien pour tester cette fonctionnalité.
[1] : http://www.w3.org/TR/xhtml1/#well-formed
author | Franck Deroche <webmaster@defr.org> |
---|---|
date | Wed, 31 Oct 2007 17:56:14 +0100 |
parents | d4ac7cef0cdd |
children |
line wrap: on
line source
<?php class Template { private $xmlDocument; private $xmlXPath; private $stripAttributesInTemplateNS = false; const NS = 'http://defr.net/2007/template'; function __construct($fileName, $shouldValidate = false) { $this->xmlDocument = new DOMDocument(); $this->xmlDocument->validateOnParse = $shouldValidate; $this->xmlDocument->preserveWhiteSpace = false; $contents = file_get_contents($fileName, FILE_USE_INCLUDE_PATH); $this->xmlDocument->loadXML($contents); $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 = $this->xmlDocument->importNode($obj->documentElement); else if($obj instanceof DOMDocumentFragment) $retVal = $obj; 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); } } // Suppression éventuelle des attributs de l'espace de nom Template::NS if($this->stripAttributesInTemplateNS) { $nodes = $this->xmlXPath->query("//*[@_t:*]"); foreach($nodes as $node) { foreach($node->attributes as $attr) { if($attr->namespaceURI == Template::NS) $node->removeAttributeNode($attr); } } } } function shouldStripAttributesInTemplateNS($boolean) { $this->stripAttributesInTemplateNS = $boolean; } function __toString() { $this->clean(); $this->xmlDocument->formatOutput = true; $this->xmlDocument->normalizeDocument(); return $this->xmlDocument->saveXML(); } function getDocumentFragment() { return $this->xmlDocument->createDocumentFragment(); } }