friendship ended with social-app. php is my new best friend
1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\CssSelector\XPath\Extension;
13
14use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
15use Symfony\Component\CssSelector\Node\FunctionNode;
16use Symfony\Component\CssSelector\XPath\Translator;
17use Symfony\Component\CssSelector\XPath\XPathExpr;
18
19/**
20 * XPath expression translator HTML extension.
21 *
22 * This component is a port of the Python cssselect library,
23 * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
24 *
25 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
26 *
27 * @internal
28 */
29class HtmlExtension extends AbstractExtension
30{
31 public function __construct(Translator $translator)
32 {
33 $translator
34 ->getExtension('node')
35 ->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true)
36 ->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true);
37 }
38
39 public function getPseudoClassTranslators(): array
40 {
41 return [
42 'checked' => $this->translateChecked(...),
43 'link' => $this->translateLink(...),
44 'disabled' => $this->translateDisabled(...),
45 'enabled' => $this->translateEnabled(...),
46 'selected' => $this->translateSelected(...),
47 'invalid' => $this->translateInvalid(...),
48 'hover' => $this->translateHover(...),
49 'visited' => $this->translateVisited(...),
50 ];
51 }
52
53 public function getFunctionTranslators(): array
54 {
55 return [
56 'lang' => $this->translateLang(...),
57 ];
58 }
59
60 public function translateChecked(XPathExpr $xpath): XPathExpr
61 {
62 return $xpath->addCondition(
63 '(@checked '
64 ."and (name(.) = 'input' or name(.) = 'command')"
65 ."and (@type = 'checkbox' or @type = 'radio'))"
66 );
67 }
68
69 public function translateLink(XPathExpr $xpath): XPathExpr
70 {
71 return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')");
72 }
73
74 public function translateDisabled(XPathExpr $xpath): XPathExpr
75 {
76 return $xpath->addCondition(
77 '('
78 .'@disabled and'
79 .'('
80 ."(name(.) = 'input' and @type != 'hidden')"
81 ." or name(.) = 'button'"
82 ." or name(.) = 'select'"
83 ." or name(.) = 'textarea'"
84 ." or name(.) = 'command'"
85 ." or name(.) = 'fieldset'"
86 ." or name(.) = 'optgroup'"
87 ." or name(.) = 'option'"
88 .')'
89 .') or ('
90 ."(name(.) = 'input' and @type != 'hidden')"
91 ." or name(.) = 'button'"
92 ." or name(.) = 'select'"
93 ." or name(.) = 'textarea'"
94 .')'
95 .' and ancestor::fieldset[@disabled]'
96 );
97 // todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any."
98 }
99
100 public function translateEnabled(XPathExpr $xpath): XPathExpr
101 {
102 return $xpath->addCondition(
103 '('
104 .'@href and ('
105 ."name(.) = 'a'"
106 ." or name(.) = 'link'"
107 ." or name(.) = 'area'"
108 .')'
109 .') or ('
110 .'('
111 ."name(.) = 'command'"
112 ." or name(.) = 'fieldset'"
113 ." or name(.) = 'optgroup'"
114 .')'
115 .' and not(@disabled)'
116 .') or ('
117 .'('
118 ."(name(.) = 'input' and @type != 'hidden')"
119 ." or name(.) = 'button'"
120 ." or name(.) = 'select'"
121 ." or name(.) = 'textarea'"
122 ." or name(.) = 'keygen'"
123 .')'
124 .' and not (@disabled or ancestor::fieldset[@disabled])'
125 .') or ('
126 ."name(.) = 'option' and not("
127 .'@disabled or ancestor::optgroup[@disabled]'
128 .')'
129 .')'
130 );
131 }
132
133 /**
134 * @throws ExpressionErrorException
135 */
136 public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
137 {
138 $arguments = $function->getArguments();
139 foreach ($arguments as $token) {
140 if (!($token->isString() || $token->isIdentifier())) {
141 throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
142 }
143 }
144
145 return $xpath->addCondition(\sprintf(
146 'ancestor-or-self::*[@lang][1][starts-with(concat('
147 ."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')"
148 .', %s)]',
149 'lang',
150 Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-')
151 ));
152 }
153
154 public function translateSelected(XPathExpr $xpath): XPathExpr
155 {
156 return $xpath->addCondition("(@selected and name(.) = 'option')");
157 }
158
159 public function translateInvalid(XPathExpr $xpath): XPathExpr
160 {
161 return $xpath->addCondition('0');
162 }
163
164 public function translateHover(XPathExpr $xpath): XPathExpr
165 {
166 return $xpath->addCondition('0');
167 }
168
169 public function translateVisited(XPathExpr $xpath): XPathExpr
170 {
171 return $xpath->addCondition('0');
172 }
173
174 public function getName(): string
175 {
176 return 'html';
177 }
178}