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