Chris@0
|
1 <?php
|
Chris@0
|
2 namespace Masterminds\HTML5\Serializer;
|
Chris@0
|
3
|
Chris@0
|
4 /**
|
Chris@0
|
5 * Traverser for walking a DOM tree.
|
Chris@0
|
6 *
|
Chris@0
|
7 * This is a concrete traverser designed to convert a DOM tree into an
|
Chris@0
|
8 * HTML5 document. It is not intended to be a generic DOMTreeWalker
|
Chris@0
|
9 * implementation.
|
Chris@0
|
10 *
|
Chris@0
|
11 * @see http://www.w3.org/TR/2012/CR-html5-20121217/syntax.html#serializing-html-fragments
|
Chris@0
|
12 */
|
Chris@0
|
13 class Traverser
|
Chris@0
|
14 {
|
Chris@0
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * Namespaces that should be treated as "local" to HTML5.
|
Chris@0
|
18 */
|
Chris@0
|
19 static $local_ns = array(
|
Chris@0
|
20 'http://www.w3.org/1999/xhtml' => 'html',
|
Chris@0
|
21 'http://www.w3.org/1998/Math/MathML' => 'math',
|
Chris@0
|
22 'http://www.w3.org/2000/svg' => 'svg'
|
Chris@0
|
23 );
|
Chris@0
|
24
|
Chris@0
|
25 protected $dom;
|
Chris@0
|
26
|
Chris@0
|
27 protected $options;
|
Chris@0
|
28
|
Chris@0
|
29 protected $encode = false;
|
Chris@0
|
30
|
Chris@0
|
31 protected $rules;
|
Chris@0
|
32
|
Chris@0
|
33 protected $out;
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * Create a traverser.
|
Chris@0
|
37 *
|
Chris@0
|
38 * @param DOMNode|DOMNodeList $dom
|
Chris@0
|
39 * The document or node to traverse.
|
Chris@0
|
40 * @param resource $out
|
Chris@0
|
41 * A stream that allows writing. The traverser will output into this
|
Chris@0
|
42 * stream.
|
Chris@0
|
43 * @param array $options
|
Chris@0
|
44 * An array or options for the traverser as key/value pairs. These include:
|
Chris@0
|
45 * - encode_entities: A bool to specify if full encding should happen for all named
|
Chris@0
|
46 * charachter references. Defaults to false which escapes &'<>".
|
Chris@0
|
47 * - output_rules: The path to the class handling the output rules.
|
Chris@0
|
48 */
|
Chris@0
|
49 public function __construct($dom, $out, RulesInterface $rules, $options = array())
|
Chris@0
|
50 {
|
Chris@0
|
51 $this->dom = $dom;
|
Chris@0
|
52 $this->out = $out;
|
Chris@0
|
53 $this->rules = $rules;
|
Chris@0
|
54 $this->options = $options;
|
Chris@0
|
55
|
Chris@0
|
56 $this->rules->setTraverser($this);
|
Chris@0
|
57 }
|
Chris@0
|
58
|
Chris@0
|
59 /**
|
Chris@0
|
60 * Tell the traverser to walk the DOM.
|
Chris@0
|
61 *
|
Chris@0
|
62 * @return resource $out
|
Chris@0
|
63 * Returns the output stream.
|
Chris@0
|
64 */
|
Chris@0
|
65 public function walk()
|
Chris@0
|
66 {
|
Chris@0
|
67 if ($this->dom instanceof \DOMDocument) {
|
Chris@0
|
68 $this->rules->document($this->dom);
|
Chris@0
|
69 } elseif ($this->dom instanceof \DOMDocumentFragment) {
|
Chris@0
|
70 // Document fragments are a special case. Only the children need to
|
Chris@0
|
71 // be serialized.
|
Chris@0
|
72 if ($this->dom->hasChildNodes()) {
|
Chris@0
|
73 $this->children($this->dom->childNodes);
|
Chris@0
|
74 }
|
Chris@0
|
75 } // If NodeList, loop
|
Chris@0
|
76 elseif ($this->dom instanceof \DOMNodeList) {
|
Chris@0
|
77 // If this is a NodeList of DOMDocuments this will not work.
|
Chris@0
|
78 $this->children($this->dom);
|
Chris@0
|
79 } // Else assume this is a DOMNode-like datastructure.
|
Chris@0
|
80 else {
|
Chris@0
|
81 $this->node($this->dom);
|
Chris@0
|
82 }
|
Chris@0
|
83
|
Chris@0
|
84 return $this->out;
|
Chris@0
|
85 }
|
Chris@0
|
86
|
Chris@0
|
87 /**
|
Chris@0
|
88 * Process a node in the DOM.
|
Chris@0
|
89 *
|
Chris@0
|
90 * @param mixed $node
|
Chris@0
|
91 * A node implementing \DOMNode.
|
Chris@0
|
92 */
|
Chris@0
|
93 public function node($node)
|
Chris@0
|
94 {
|
Chris@0
|
95 // A listing of types is at http://php.net/manual/en/dom.constants.php
|
Chris@0
|
96 switch ($node->nodeType) {
|
Chris@0
|
97 case XML_ELEMENT_NODE:
|
Chris@0
|
98 $this->rules->element($node);
|
Chris@0
|
99 break;
|
Chris@0
|
100 case XML_TEXT_NODE:
|
Chris@0
|
101 $this->rules->text($node);
|
Chris@0
|
102 break;
|
Chris@0
|
103 case XML_CDATA_SECTION_NODE:
|
Chris@0
|
104 $this->rules->cdata($node);
|
Chris@0
|
105 break;
|
Chris@0
|
106 case XML_PI_NODE:
|
Chris@0
|
107 $this->rules->processorInstruction($node);
|
Chris@0
|
108 break;
|
Chris@0
|
109 case XML_COMMENT_NODE:
|
Chris@0
|
110 $this->rules->comment($node);
|
Chris@0
|
111 break;
|
Chris@0
|
112 // Currently we don't support embedding DTDs.
|
Chris@0
|
113 default:
|
Chris@0
|
114 //print '<!-- Skipped -->';
|
Chris@0
|
115 break;
|
Chris@0
|
116 }
|
Chris@0
|
117 }
|
Chris@0
|
118
|
Chris@0
|
119 /**
|
Chris@0
|
120 * Walk through all the nodes on a node list.
|
Chris@0
|
121 *
|
Chris@0
|
122 * @param \DOMNodeList $nl
|
Chris@0
|
123 * A list of child elements to walk through.
|
Chris@0
|
124 */
|
Chris@0
|
125 public function children($nl)
|
Chris@0
|
126 {
|
Chris@0
|
127 foreach ($nl as $node) {
|
Chris@0
|
128 $this->node($node);
|
Chris@0
|
129 }
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 /**
|
Chris@0
|
133 * Is an element local?
|
Chris@0
|
134 *
|
Chris@0
|
135 * @param mixed $ele
|
Chris@0
|
136 * An element that implement \DOMNode.
|
Chris@0
|
137 *
|
Chris@0
|
138 * @return bool True if local and false otherwise.
|
Chris@0
|
139 */
|
Chris@0
|
140 public function isLocalElement($ele)
|
Chris@0
|
141 {
|
Chris@0
|
142 $uri = $ele->namespaceURI;
|
Chris@0
|
143 if (empty($uri)) {
|
Chris@0
|
144 return false;
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 return isset(static::$local_ns[$uri]);
|
Chris@0
|
148 }
|
Chris@0
|
149 }
|