Share Pastebin
Guest
Public paste!

troelskn

By: a guest | Sep 24th, 2008 | Syntax: PHP | Size: 6.90 KB | Hits: 686 | Expires: Never
Copy text to clipboard
  1. <?php
  2. class DomQuery {
  3.   protected $xpath;
  4.   function __construct($document) {
  5.     $this->xpath = new DomXPath($document instanceOf DomDocument ? $document : $document->ownerDocument);
  6.   }
  7.   function getFirstElementByClassname($class_name, $node = null) {
  8.     if (!$class_name) {
  9.       return $node;
  10.     }
  11.     if ($node) {
  12.       return $this->xpath->query("descendant::*[contains(@class,'$class_name')]", $node)->item(0);
  13.     }
  14.     return $this->xpath->query("descendant::*[contains(@class,'$class_name')]")->item(0);
  15.   }
  16. }
  17.  
  18. class Domling {
  19.   protected $node;
  20.   protected $query;
  21.   protected $captures = array();
  22.   function __construct($source) {
  23.     if (is_string($source)) {
  24.       $this->node = $this->parse($source);
  25.     } else {
  26.       $this->node = $source;
  27.     }
  28.     $this->query = new DomQuery($this->node);
  29.   }
  30.   protected function parse($html) {
  31.     $document = new DomDocument();
  32.     if (!preg_match('~^(<!doctype|<html)~', $html)) {
  33.       $document->loadHtml("<html><body><div>" . $html . "</div></body></html>");
  34.       return $document->getElementsByTagName("body")->item(0)->firstChild;
  35.     }
  36.     $document->loadHtml($html);
  37.     return $document;
  38.   }
  39.   function purge() {
  40.     foreach ($this->captures as $capture) {
  41.       $capture->purge();
  42.     }
  43.   }
  44.   function render() {
  45.     $this->purge();
  46.     if ($this->node instanceOf DOMElement) {
  47.       preg_match('~<html>\s*<body>\s*<div>([\s\S]*)</div>\s*</body>\s*</html>\s*$~', $this->node->ownerDocument->saveHtml(), $matches);
  48.       return $matches[1];
  49.     }
  50.     return $this->node->saveHtml();
  51.   }
  52.   function capture($class_name) {
  53.     if (!isset($this->captures[$class_name])) {
  54.       $this->captures[$class_name] = new Capture($this->query->getFirstElementByClassname($class_name));
  55.     }
  56.     return $this->captures[$class_name];
  57.   }
  58.   function sequence($item_class_name, $container_class_name) {
  59.     if (!isset($this->captures[$item_class_name.":".$container_class_name])) {
  60.       $container = $this->query->getFirstElementByClassname($container_class_name);
  61.       $this->captures[$item_class_name.":".$container_class_name] = new Sequence(
  62.         $this->query->getFirstElementByClassname($item_class_name, $container),
  63.         $container);
  64.     }
  65.     return $this->captures[$item_class_name.":".$container_class_name];
  66.   }
  67. }
  68.  
  69. class Capture extends Domling {
  70.   protected $placeholder_node;
  71.   function __construct(DomElement $node) {
  72.     parent::__construct($node);
  73.     $this->placeholder_node = $this->node->ownerDocument->createComment("placeholder");
  74.     $this->node->parentNode->replaceChild($this->placeholder_node, $this->node);
  75.   }
  76.   function purge() {
  77.     parent::purge();
  78.     if ($this->placeholder_node->parentNode) {
  79.       $this->placeholder_node->parentNode->removeChild($this->placeholder_node);
  80.     }
  81.   }
  82.   function bind($data = array()) {
  83.     $clone = $this->node->cloneNode(true);
  84.     $this->placeholder_node->parentNode->insertBefore($clone, $this->placeholder_node);
  85.     if (is_string($data)) {
  86.       $data = array('' => $data);
  87.     }
  88.     foreach ($data as $id => $value) {
  89.       if (preg_match('~(.+):(.+)~', $id, $matches)) {
  90.         $class_name = $matches[1];
  91.         $attribute = $matches[2];
  92.         $element = $this->query->getFirstElementByClassname($class_name, $clone);
  93.         $element->setAttribute($attribute, $value);
  94.       } else {
  95.         $element = $this->query->getFirstElementByClassname($id, $clone);
  96.         while ($element->hasChildNodes()) {
  97.           $element->removeChild($element->firstChild);
  98.         }
  99.         $element->appendChild($element->ownerDocument->createTextNode($value));
  100.       }
  101.     }
  102.     return $clone;
  103.   }
  104. }
  105.  
  106. class Sequence extends Capture {
  107.   protected $container;
  108.   protected $placeholder_container;
  109.   function __construct(DomElement $node, DomElement $container) {
  110.     parent::__construct($node);
  111.     $this->container = $container;
  112.     $this->placeholder_container = $container->ownerDocument->createComment("container");
  113.     $this->container->parentNode->replaceChild($this->placeholder_container, $this->container);
  114.   }
  115.   function purge() {
  116.     parent::purge();
  117.     if ($this->placeholder_container->parentNode) {
  118.       $this->placeholder_container->parentNode->removeChild($this->placeholder_container);
  119.     }
  120.   }
  121.   function bind($data = array()) {
  122.     if (!$this->container->parentNode) {
  123.       $this->placeholder_container->parentNode->replaceChild($this->container, $this->placeholder_container);
  124.     }
  125.     return parent::bind($data);
  126.   }
  127. }
  128.  
  129. if (realpath($_SERVER['SCRIPT_NAME']) == __FILE__) {
  130.   error_reporting(E_ALL);
  131.  
  132.   // simple variable binding
  133.   $t = new Domling('<p class="hello"></p>');
  134.   $t->capture('hello')->bind("Hello World");
  135.   echo $t->render();
  136.   // <p class="hello">Hello World</p>
  137.  
  138.   echo "\n---\n";
  139.  
  140.   // Switching a block out
  141.   $t = new Domling('<p>Lorem Ipsum</p><p class="message">Hidden message</p>');
  142.   $t->capture('message');
  143.   echo $t->render();
  144.   // <p>Lorem Ipsum</p>
  145.  
  146.   echo "\n---\n";
  147.  
  148.   // Putting it back in
  149.   $t = new Domling('<p>Lorem Ipsum</p><p class="message">Hidden message</p>');
  150.   $block = $t->capture('message');
  151.   $block->bind();
  152.   echo $t->render();
  153.   // <p>Lorem Ipsum</p>
  154.   // <p class="message">Hidden message</p>
  155.  
  156.   echo "\n---\n";
  157.  
  158.   // And looping over a block
  159.   $t = new Domling('<ul class="links"><li class="link"><a class="anchor" href="#">title</a></li></ul>');
  160.   $links = array(
  161.     'Sitepoint' => 'http://www.sitepoint.com',
  162.     'Example' => 'http://www.example.org?foo=bar&ding=dong');
  163.   foreach ($links as $title => $link) {
  164.     $t->sequence('link', 'links')->bind(array('anchor:href' => $link, 'anchor' => $title));
  165.   }
  166.   echo $t->render();
  167.   // <ul class="links">
  168.   // <li class="link"><a class="anchor" href="http://www.sitepoint.com">Sitepoint</a></li>
  169.   // <li class="link"><a class="anchor" href="http://www.example.org?foo=bar&amp;ding=dong">Example</a></li>
  170.   // </ul>
  171.  
  172.   echo "\n---\n";
  173.  
  174.   // And a full sample
  175.   $template = "
  176. <h1 class='title'>title</h1>
  177. <ul class='links'>
  178.  <li class='link'><a class='anchor' href='#'>title</a></li>
  179. </ul>
  180. <p class='switch'>Hide this</p>
  181. ";
  182.   $t = new Domling($template);
  183.  
  184.   // let's bind some data to the template
  185.   $t->capture('title')->bind("Hello World");
  186.  
  187.   // Or we can repeat a block as a sequence
  188.   $links = array(
  189.     'Sitepoint' => 'http://www.sitepoint.com',
  190.     'Example' => 'http://www.example.org?foo=bar&ding=dong');
  191.   foreach ($links as $title => $link) {
  192.     $t->sequence('link', 'links')->bind(array('anchor:href' => $link, 'anchor' => $title));
  193.   }
  194.  
  195.   // we can even remove a block entirely
  196.   $t->capture('switch');
  197.  
  198.   echo $t->render();
  199.  
  200.   // <h1 class="title">Hello World</h1>
  201.   // <ul class="links">
  202.   // <li class="link"><a class="anchor" href="http://www.sitepoint.com">Sitepoint</a></li>
  203.   // <li class="link"><a class="anchor" href="http://www.example.org?foo=bar&amp;ding=dong">Example</a></li>
  204.   // </ul>
  205. }