annotate vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php @ 2:92f882872392

Trusted hosts, + remove migration modules
author Chris Cannam
date Tue, 05 Dec 2017 09:26:43 +0000
parents 4c8ae668cc8c
children 1fec387a4317
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony package.
Chris@0 5 *
Chris@0 6 * (c) Fabien Potencier <fabien@symfony.com>
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Component\CssSelector\XPath\Extension;
Chris@0 13
Chris@0 14 use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
Chris@0 15 use Symfony\Component\CssSelector\Exception\SyntaxErrorException;
Chris@0 16 use Symfony\Component\CssSelector\Node\FunctionNode;
Chris@0 17 use Symfony\Component\CssSelector\Parser\Parser;
Chris@0 18 use Symfony\Component\CssSelector\XPath\Translator;
Chris@0 19 use Symfony\Component\CssSelector\XPath\XPathExpr;
Chris@0 20
Chris@0 21 /**
Chris@0 22 * XPath expression translator function extension.
Chris@0 23 *
Chris@0 24 * This component is a port of the Python cssselect library,
Chris@0 25 * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
Chris@0 26 *
Chris@0 27 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
Chris@0 28 *
Chris@0 29 * @internal
Chris@0 30 */
Chris@0 31 class FunctionExtension extends AbstractExtension
Chris@0 32 {
Chris@0 33 /**
Chris@0 34 * {@inheritdoc}
Chris@0 35 */
Chris@0 36 public function getFunctionTranslators()
Chris@0 37 {
Chris@0 38 return array(
Chris@0 39 'nth-child' => array($this, 'translateNthChild'),
Chris@0 40 'nth-last-child' => array($this, 'translateNthLastChild'),
Chris@0 41 'nth-of-type' => array($this, 'translateNthOfType'),
Chris@0 42 'nth-last-of-type' => array($this, 'translateNthLastOfType'),
Chris@0 43 'contains' => array($this, 'translateContains'),
Chris@0 44 'lang' => array($this, 'translateLang'),
Chris@0 45 );
Chris@0 46 }
Chris@0 47
Chris@0 48 /**
Chris@0 49 * @param XPathExpr $xpath
Chris@0 50 * @param FunctionNode $function
Chris@0 51 * @param bool $last
Chris@0 52 * @param bool $addNameTest
Chris@0 53 *
Chris@0 54 * @return XPathExpr
Chris@0 55 *
Chris@0 56 * @throws ExpressionErrorException
Chris@0 57 */
Chris@0 58 public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $last = false, $addNameTest = true)
Chris@0 59 {
Chris@0 60 try {
Chris@0 61 list($a, $b) = Parser::parseSeries($function->getArguments());
Chris@0 62 } catch (SyntaxErrorException $e) {
Chris@0 63 throw new ExpressionErrorException(sprintf('Invalid series: %s', implode(', ', $function->getArguments())), 0, $e);
Chris@0 64 }
Chris@0 65
Chris@0 66 $xpath->addStarPrefix();
Chris@0 67 if ($addNameTest) {
Chris@0 68 $xpath->addNameTest();
Chris@0 69 }
Chris@0 70
Chris@0 71 if (0 === $a) {
Chris@0 72 return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
Chris@0 73 }
Chris@0 74
Chris@0 75 if ($a < 0) {
Chris@0 76 if ($b < 1) {
Chris@0 77 return $xpath->addCondition('false()');
Chris@0 78 }
Chris@0 79
Chris@0 80 $sign = '<=';
Chris@0 81 } else {
Chris@0 82 $sign = '>=';
Chris@0 83 }
Chris@0 84
Chris@0 85 $expr = 'position()';
Chris@0 86
Chris@0 87 if ($last) {
Chris@0 88 $expr = 'last() - '.$expr;
Chris@0 89 --$b;
Chris@0 90 }
Chris@0 91
Chris@0 92 if (0 !== $b) {
Chris@0 93 $expr .= ' - '.$b;
Chris@0 94 }
Chris@0 95
Chris@0 96 $conditions = array(sprintf('%s %s 0', $expr, $sign));
Chris@0 97
Chris@0 98 if (1 !== $a && -1 !== $a) {
Chris@0 99 $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
Chris@0 100 }
Chris@0 101
Chris@0 102 return $xpath->addCondition(implode(' and ', $conditions));
Chris@0 103
Chris@0 104 // todo: handle an+b, odd, even
Chris@0 105 // an+b means every-a, plus b, e.g., 2n+1 means odd
Chris@0 106 // 0n+b means b
Chris@0 107 // n+0 means a=1, i.e., all elements
Chris@0 108 // an means every a elements, i.e., 2n means even
Chris@0 109 // -n means -1n
Chris@0 110 // -1n+6 means elements 6 and previous
Chris@0 111 }
Chris@0 112
Chris@0 113 /**
Chris@0 114 * @param XPathExpr $xpath
Chris@0 115 * @param FunctionNode $function
Chris@0 116 *
Chris@0 117 * @return XPathExpr
Chris@0 118 */
Chris@0 119 public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function)
Chris@0 120 {
Chris@0 121 return $this->translateNthChild($xpath, $function, true);
Chris@0 122 }
Chris@0 123
Chris@0 124 /**
Chris@0 125 * @param XPathExpr $xpath
Chris@0 126 * @param FunctionNode $function
Chris@0 127 *
Chris@0 128 * @return XPathExpr
Chris@0 129 */
Chris@0 130 public function translateNthOfType(XPathExpr $xpath, FunctionNode $function)
Chris@0 131 {
Chris@0 132 return $this->translateNthChild($xpath, $function, false, false);
Chris@0 133 }
Chris@0 134
Chris@0 135 /**
Chris@0 136 * @param XPathExpr $xpath
Chris@0 137 * @param FunctionNode $function
Chris@0 138 *
Chris@0 139 * @return XPathExpr
Chris@0 140 *
Chris@0 141 * @throws ExpressionErrorException
Chris@0 142 */
Chris@0 143 public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function)
Chris@0 144 {
Chris@0 145 if ('*' === $xpath->getElement()) {
Chris@0 146 throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
Chris@0 147 }
Chris@0 148
Chris@0 149 return $this->translateNthChild($xpath, $function, true, false);
Chris@0 150 }
Chris@0 151
Chris@0 152 /**
Chris@0 153 * @param XPathExpr $xpath
Chris@0 154 * @param FunctionNode $function
Chris@0 155 *
Chris@0 156 * @return XPathExpr
Chris@0 157 *
Chris@0 158 * @throws ExpressionErrorException
Chris@0 159 */
Chris@0 160 public function translateContains(XPathExpr $xpath, FunctionNode $function)
Chris@0 161 {
Chris@0 162 $arguments = $function->getArguments();
Chris@0 163 foreach ($arguments as $token) {
Chris@0 164 if (!($token->isString() || $token->isIdentifier())) {
Chris@0 165 throw new ExpressionErrorException(
Chris@0 166 'Expected a single string or identifier for :contains(), got '
Chris@0 167 .implode(', ', $arguments)
Chris@0 168 );
Chris@0 169 }
Chris@0 170 }
Chris@0 171
Chris@0 172 return $xpath->addCondition(sprintf(
Chris@0 173 'contains(string(.), %s)',
Chris@0 174 Translator::getXpathLiteral($arguments[0]->getValue())
Chris@0 175 ));
Chris@0 176 }
Chris@0 177
Chris@0 178 /**
Chris@0 179 * @param XPathExpr $xpath
Chris@0 180 * @param FunctionNode $function
Chris@0 181 *
Chris@0 182 * @return XPathExpr
Chris@0 183 *
Chris@0 184 * @throws ExpressionErrorException
Chris@0 185 */
Chris@0 186 public function translateLang(XPathExpr $xpath, FunctionNode $function)
Chris@0 187 {
Chris@0 188 $arguments = $function->getArguments();
Chris@0 189 foreach ($arguments as $token) {
Chris@0 190 if (!($token->isString() || $token->isIdentifier())) {
Chris@0 191 throw new ExpressionErrorException(
Chris@0 192 'Expected a single string or identifier for :lang(), got '
Chris@0 193 .implode(', ', $arguments)
Chris@0 194 );
Chris@0 195 }
Chris@0 196 }
Chris@0 197
Chris@0 198 return $xpath->addCondition(sprintf(
Chris@0 199 'lang(%s)',
Chris@0 200 Translator::getXpathLiteral($arguments[0]->getValue())
Chris@0 201 ));
Chris@0 202 }
Chris@0 203
Chris@0 204 /**
Chris@0 205 * {@inheritdoc}
Chris@0 206 */
Chris@0 207 public function getName()
Chris@0 208 {
Chris@0 209 return 'function';
Chris@0 210 }
Chris@0 211 }