PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.9.15
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.9.15
1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 All 163 releases
woocommerce-pos / vendor_prefixed / masterminds / html5 / src / HTML5 / Serializer / OutputRules.php

OutputRules.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.9.15, at vendor_prefixed/masterminds/html5/src/HTML5/Serializer/OutputRules.php

395 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @file
5 * The rules for generating output in the serializer.
6 *
7 * These output rules are likely to generate output similar to the document that
8 * was parsed. It is not intended to output exactly the document that was parsed.
9 */
10 namespace WCPOS\Vendor\Masterminds\HTML5\Serializer;
11
12 use WCPOS\Vendor\Masterminds\HTML5\Elements;
13 /**
14 * Generate the output html5 based on element rules.
15 */
16 class OutputRules implements RulesInterface
17 {
18 /**
19 * Defined in http://www.w3.org/TR/html51/infrastructure.html#html-namespace-0.
20 */
21 const NAMESPACE_HTML = 'http://www.w3.org/1999/xhtml';
22 const NAMESPACE_MATHML = 'http://www.w3.org/1998/Math/MathML';
23 const NAMESPACE_SVG = 'http://www.w3.org/2000/svg';
24 const NAMESPACE_XLINK = 'http://www.w3.org/1999/xlink';
25 const NAMESPACE_XML = 'http://www.w3.org/XML/1998/namespace';
26 const NAMESPACE_XMLNS = 'http://www.w3.org/2000/xmlns/';
27 /**
28 * Holds the HTML5 element names that causes a namespace switch.
29 *
30 * @var array
31 */
32 protected $implicitNamespaces = array(self::NAMESPACE_HTML, self::NAMESPACE_SVG, self::NAMESPACE_MATHML, self::NAMESPACE_XML, self::NAMESPACE_XMLNS);
33 const IM_IN_HTML = 1;
34 const IM_IN_SVG = 2;
35 const IM_IN_MATHML = 3;
36 /**
37 * Used as cache to detect if is available ENT_HTML5.
38 *
39 * @var bool
40 */
41 private $hasHTML5 = \false;
42 protected $traverser;
43 protected $encode = \false;
44 protected $out;
45 protected $outputMode;
46 private $xpath;
47 protected $nonBooleanAttributes = array(
48 /*
49 array(
50 'nodeNamespace'=>'http://www.w3.org/1999/xhtml',
51 'attrNamespace'=>'http://www.w3.org/1999/xhtml',
52
53 'nodeName'=>'img', 'nodeName'=>array('img', 'a'),
54 'attrName'=>'alt', 'attrName'=>array('title', 'alt'),
55 ),
56 */
57 array('nodeNamespace' => 'http://www.w3.org/1999/xhtml', 'attrName' => array('href', 'hreflang', 'http-equiv', 'icon', 'id', 'keytype', 'kind', 'label', 'lang', 'language', 'list', 'maxlength', 'media', 'method', 'name', 'placeholder', 'rel', 'rows', 'rowspan', 'sandbox', 'spellcheck', 'scope', 'seamless', 'shape', 'size', 'sizes', 'span', 'src', 'srcdoc', 'srclang', 'srcset', 'start', 'step', 'style', 'summary', 'tabindex', 'target', 'title', 'type', 'value', 'width', 'border', 'charset', 'cite', 'class', 'code', 'codebase', 'color', 'cols', 'colspan', 'content', 'coords', 'data', 'datetime', 'default', 'dir', 'dirname', 'enctype', 'for', 'form', 'formaction', 'headers', 'height', 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'alt', 'bgcolor')),
58 array('nodeNamespace' => 'http://www.w3.org/1999/xhtml', 'xpath' => 'starts-with(local-name(), \'data-\')'),
59 );
60 const DOCTYPE = '<!DOCTYPE html>';
61 public function __construct($output, $options = array())
62 {
63 if (isset($options['encode_entities'])) {
64 $this->encode = $options['encode_entities'];
65 }
66 $this->outputMode = static::IM_IN_HTML;
67 $this->out = $output;
68 $this->hasHTML5 = \defined('ENT_HTML5');
69 }
70 public function addRule(array $rule)
71 {
72 $this->nonBooleanAttributes[] = $rule;
73 }
74 public function setTraverser(Traverser $traverser)
75 {
76 $this->traverser = $traverser;
77 return $this;
78 }
79 public function unsetTraverser()
80 {
81 $this->traverser = null;
82 return $this;
83 }
84 public function document($dom)
85 {
86 $this->doctype();
87 if ($dom->documentElement) {
88 foreach ($dom->childNodes as $node) {
89 $this->traverser->node($node);
90 }
91 $this->nl();
92 }
93 }
94 protected function doctype()
95 {
96 $this->wr(static::DOCTYPE);
97 $this->nl();
98 }
99 /**
100 * @param \DOMElement $ele
101 */
102 public function element($ele)
103 {
104 $name = $ele->tagName;
105 // Per spec:
106 // If the element has a declared namespace in the HTML, MathML or
107 // SVG namespaces, we use the lname instead of the tagName.
108 if ($this->traverser->isLocalElement($ele)) {
109 $name = $ele->localName;
110 }
111 // If we are in SVG or MathML there is special handling.
112 // Using if/elseif instead of switch because it's faster in PHP.
113 if ('svg' == $name) {
114 $this->outputMode = static::IM_IN_SVG;
115 $name = Elements::normalizeSvgElement($name);
116 } elseif ('math' == $name) {
117 $this->outputMode = static::IM_IN_MATHML;
118 }
119 $this->openTag($ele);
120 // The tag is already self-closed (`<svg />` or `<math />`) in `openTag` if there are no child nodes.
121 $handledAsVoidTag = $this->outputMode !== static::IM_IN_HTML && !$ele->hasChildNodes();
122 if (Elements::isA($name, Elements::TEXT_RAW)) {
123 foreach ($ele->childNodes as $child) {
124 if ($child instanceof \DOMCharacterData) {
125 $this->wr($child->data);
126 } elseif ($child instanceof \DOMElement) {
127 $this->element($child);
128 }
129 }
130 } else {
131 // Handle children.
132 if ($ele->hasChildNodes()) {
133 $this->traverser->children($ele->childNodes);
134 }
135 // Close out the SVG or MathML special handling.
136 if ('svg' == $name || 'math' == $name) {
137 $this->outputMode = static::IM_IN_HTML;
138 }
139 }
140 // If not unary, add a closing tag.
141 if (!$handledAsVoidTag && !Elements::isA($name, Elements::VOID_TAG)) {
142 $this->closeTag($ele);
143 }
144 }
145 /**
146 * Write a text node.
147 *
148 * @param \DOMText $ele The text node to write.
149 */
150 public function text($ele)
151 {
152 if (isset($ele->parentNode) && isset($ele->parentNode->tagName) && Elements::isA($ele->parentNode->localName, Elements::TEXT_RAW)) {
153 $this->wr($ele->data);
154 return;
155 }
156 // FIXME: This probably needs some flags set.
157 $this->wr($this->enc($ele->data));
158 }
159 public function cdata($ele)
160 {
161 // This encodes CDATA.
162 $this->wr($ele->ownerDocument->saveXML($ele));
163 }
164 public function comment($ele)
165 {
166 // These produce identical output.
167 // $this->wr('<!--')->wr($ele->data)->wr('-->');
168 $this->wr($ele->ownerDocument->saveXML($ele));
169 }
170 public function processorInstruction($ele)
171 {
172 $this->wr('<?')->wr($ele->target)->wr(' ')->wr($ele->data)->wr('?>');
173 }
174 /**
175 * Write the namespace attributes.
176 *
177 * @param \DOMNode $ele The element being written.
178 */
179 protected function namespaceAttrs($ele)
180 {
181 if (!$this->xpath || $this->xpath->document !== $ele->ownerDocument) {
182 $this->xpath = new \DOMXPath($ele->ownerDocument);
183 }
184 foreach ($this->xpath->query('namespace::*[not(.=../../namespace::*)]', $ele) as $nsNode) {
185 if (!\in_array($nsNode->nodeValue, $this->implicitNamespaces)) {
186 $this->wr(' ')->wr($nsNode->nodeName)->wr('="')->wr($nsNode->nodeValue)->wr('"');
187 }
188 }
189 }
190 /**
191 * Write the opening tag.
192 *
193 * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the
194 * qualified name (8.3).
195 *
196 * @param \DOMNode $ele The element being written.
197 */
198 protected function openTag($ele)
199 {
200 $this->wr('<')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName);
201 $this->attrs($ele);
202 $this->namespaceAttrs($ele);
203 if ($this->outputMode == static::IM_IN_HTML) {
204 $this->wr('>');
205 } else {
206 if ($ele->hasChildNodes()) {
207 $this->wr('>');
208 } else {
209 $this->wr(' />');
210 }
211 }
212 }
213 protected function attrs($ele)
214 {
215 // FIXME: Needs support for xml, xmlns, xlink, and namespaced elements.
216 if (!$ele->hasAttributes()) {
217 return $this;
218 }
219 // TODO: Currently, this always writes name="value", and does not do
220 // value-less attributes.
221 $map = $ele->attributes;
222 $len = $map->length;
223 for ($i = 0; $i < $len; ++$i) {
224 $node = $map->item($i);
225 $val = $this->enc($node->value, \true);
226 // XXX: The spec says that we need to ensure that anything in
227 // the XML, XMLNS, or XLink NS's should use the canonical
228 // prefix. It seems that DOM does this for us already, but there
229 // may be exceptions.
230 $name = $node->nodeName;
231 // Special handling for attributes in SVG and MathML.
232 // Using if/elseif instead of switch because it's faster in PHP.
233 if ($this->outputMode == static::IM_IN_SVG) {
234 $name = Elements::normalizeSvgAttribute($name);
235 } elseif ($this->outputMode == static::IM_IN_MATHML) {
236 $name = Elements::normalizeMathMlAttribute($name);
237 }
238 $this->wr(' ')->wr($name);
239 if (isset($val) && '' !== $val || $this->nonBooleanAttribute($node)) {
240 $this->wr('="')->wr($val)->wr('"');
241 }
242 }
243 }
244 protected function nonBooleanAttribute(\DOMAttr $attr)
245 {
246 $ele = $attr->ownerElement;
247 foreach ($this->nonBooleanAttributes as $rule) {
248 if (isset($rule['nodeNamespace']) && $rule['nodeNamespace'] !== $ele->namespaceURI) {
249 continue;
250 }
251 if (isset($rule['attNamespace']) && $rule['attNamespace'] !== $attr->namespaceURI) {
252 continue;
253 }
254 if (isset($rule['nodeName']) && !\is_array($rule['nodeName']) && $rule['nodeName'] !== $ele->localName) {
255 continue;
256 }
257 if (isset($rule['nodeName']) && \is_array($rule['nodeName']) && !\in_array($ele->localName, $rule['nodeName'], \true)) {
258 continue;
259 }
260 if (isset($rule['attrName']) && !\is_array($rule['attrName']) && $rule['attrName'] !== $attr->localName) {
261 continue;
262 }
263 if (isset($rule['attrName']) && \is_array($rule['attrName']) && !\in_array($attr->localName, $rule['attrName'], \true)) {
264 continue;
265 }
266 if (isset($rule['xpath'])) {
267 $xp = $this->getXPath($attr);
268 if (isset($rule['prefixes'])) {
269 foreach ($rule['prefixes'] as $nsPrefix => $ns) {
270 $xp->registerNamespace($nsPrefix, $ns);
271 }
272 }
273 if (!$xp->evaluate($rule['xpath'], $attr)) {
274 continue;
275 }
276 }
277 return \true;
278 }
279 return \false;
280 }
281 private function getXPath(\DOMNode $node)
282 {
283 if (!$this->xpath) {
284 $this->xpath = new \DOMXPath($node->ownerDocument);
285 }
286 return $this->xpath;
287 }
288 /**
289 * Write the closing tag.
290 *
291 * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the
292 * qualified name (8.3).
293 *
294 * @param \DOMNode $ele The element being written.
295 */
296 protected function closeTag($ele)
297 {
298 if ($this->outputMode == static::IM_IN_HTML || $ele->hasChildNodes()) {
299 $this->wr('</')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName)->wr('>');
300 }
301 }
302 /**
303 * Write to the output.
304 *
305 * @param string $text The string to put into the output
306 *
307 * @return $this
308 */
309 protected function wr($text)
310 {
311 \fwrite($this->out, (string) $text);
312 return $this;
313 }
314 /**
315 * Write a new line character.
316 *
317 * @return $this
318 */
319 protected function nl()
320 {
321 \fwrite($this->out, \PHP_EOL);
322 return $this;
323 }
324 /**
325 * Encode text.
326 *
327 * When encode is set to false, the default value, the text passed in is
328 * escaped per section 8.3 of the html5 spec. For details on how text is
329 * escaped see the escape() method.
330 *
331 * When encoding is set to true the text is converted to named character
332 * references where appropriate. Section 8.1.4 Character references of the
333 * html5 spec refers to using named character references. This is useful for
334 * characters that can't otherwise legally be used in the text.
335 *
336 * The named character references are listed in section 8.5.
337 *
338 * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#named-character-references True encoding will turn all named character references into their entities.
339 * This includes such characters as +.# and many other common ones. By default
340 * encoding here will just escape &'<>".
341 *
342 * Note, PHP 5.4+ has better html5 encoding.
343 *
344 * @todo Use the Entities class in php 5.3 to have html5 entities.
345 *
346 * @param string $text Text to encode.
347 * @param bool $attribute True if we are encoding an attrubute, false otherwise.
348 *
349 * @return string The encoded text.
350 */
351 protected function enc($text, $attribute = \false)
352 {
353 // Escape the text rather than convert to named character references.
354 if (!$this->encode) {
355 return $this->escape($text, $attribute);
356 }
357 // If we are in PHP 5.4+ we can use the native html5 entity functionality to
358 // convert the named character references.
359 if ($this->hasHTML5) {
360 return \htmlentities($text, \ENT_HTML5 | \ENT_SUBSTITUTE | \ENT_QUOTES, 'UTF-8', \false);
361 } else {
362 return \strtr($text, HTML5Entities::$map);
363 }
364 }
365 /**
366 * Escape test.
367 *
368 * According to the html5 spec section 8.3 Serializing HTML fragments, text
369 * within tags that are not style, script, xmp, iframe, noembed, and noframes
370 * need to be properly escaped.
371 *
372 * The & should be converted to &amp;, no breaking space unicode characters
373 * converted to &nbsp;, when in attribute mode the " should be converted to
374 * &quot;, and when not in attribute mode the < and > should be converted to
375 * &lt; and &gt;.
376 *
377 * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#escapingString
378 *
379 * @param string $text Text to escape.
380 * @param bool $attribute True if we are escaping an attrubute, false otherwise.
381 */
382 protected function escape($text, $attribute = \false)
383 {
384 // Not using htmlspecialchars because, while it does escaping, it doesn't
385 // match the requirements of section 8.5. For example, it doesn't handle
386 // non-breaking spaces.
387 if ($attribute) {
388 $replace = array('"' => '&quot;', '&' => '&amp;', " " => '&nbsp;');
389 } else {
390 $replace = array('<' => '&lt;', '>' => '&gt;', '&' => '&amp;', " " => '&nbsp;');
391 }
392 return \strtr($text, $replace);
393 }
394 }
395