PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.18
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.18
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.10.18, at vendor_prefixed/masterminds/html5/src/HTML5/Serializer/OutputRules.php

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