annotate vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
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@17 38 return [
Chris@17 39 'nth-child' => [$this, 'translateNthChild'],
Chris@17 40 'nth-last-child' => [$this, 'translateNthLastChild'],
Chris@17 41 'nth-of-type' => [$this, 'translateNthOfType'],
Chris@17 42 'nth-last-of-type' => [$this, 'translateNthLastOfType'],
Chris@17 43 'contains' => [$this, 'translateContains'],
Chris@17 44 'lang' => [$this, 'translateLang'],
Chris@17 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@17 96 $conditions = [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 * @return XPathExpr
Chris@0 115 */
Chris@0 116 public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function)
Chris@0 117 {
Chris@0 118 return $this->translateNthChild($xpath, $function, true);
Chris@0 119 }
Chris@0 120
Chris@0 121 /**
Chris@0 122 * @return XPathExpr
Chris@0 123 */
Chris@0 124 public function translateNthOfType(XPathExpr $xpath, FunctionNode $function)
Chris@0 125 {
Chris@0 126 return $this->translateNthChild($xpath, $function, false, false);
Chris@0 127 }
Chris@0 128
Chris@0 129 /**
Chris@0 130 * @return XPathExpr
Chris@0 131 *
Chris@0 132 * @throws ExpressionErrorException
Chris@0 133 */
Chris@0 134 public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function)
Chris@0 135 {
Chris@0 136 if ('*' === $xpath->getElement()) {
Chris@0 137 throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
Chris@0 138 }
Chris@0 139
Chris@0 140 return $this->translateNthChild($xpath, $function, true, false);
Chris@0 141 }
Chris@0 142
Chris@0 143 /**
Chris@0 144 * @return XPathExpr
Chris@0 145 *
Chris@0 146 * @throws ExpressionErrorException
Chris@0 147 */
Chris@0 148 public function translateContains(XPathExpr $xpath, FunctionNode $function)
Chris@0 149 {
Chris@0 150 $arguments = $function->getArguments();
Chris@0 151 foreach ($arguments as $token) {
Chris@0 152 if (!($token->isString() || $token->isIdentifier())) {
Chris@17 153 throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));
Chris@0 154 }
Chris@0 155 }
Chris@0 156
Chris@0 157 return $xpath->addCondition(sprintf(
Chris@0 158 'contains(string(.), %s)',
Chris@0 159 Translator::getXpathLiteral($arguments[0]->getValue())
Chris@0 160 ));
Chris@0 161 }
Chris@0 162
Chris@0 163 /**
Chris@0 164 * @return XPathExpr
Chris@0 165 *
Chris@0 166 * @throws ExpressionErrorException
Chris@0 167 */
Chris@0 168 public function translateLang(XPathExpr $xpath, FunctionNode $function)
Chris@0 169 {
Chris@0 170 $arguments = $function->getArguments();
Chris@0 171 foreach ($arguments as $token) {
Chris@0 172 if (!($token->isString() || $token->isIdentifier())) {
Chris@17 173 throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
Chris@0 174 }
Chris@0 175 }
Chris@0 176
Chris@0 177 return $xpath->addCondition(sprintf(
Chris@0 178 'lang(%s)',
Chris@0 179 Translator::getXpathLiteral($arguments[0]->getValue())
Chris@0 180 ));
Chris@0 181 }
Chris@0 182
Chris@0 183 /**
Chris@0 184 * {@inheritdoc}
Chris@0 185 */
Chris@0 186 public function getName()
Chris@0 187 {
Chris@0 188 return 'function';
Chris@0 189 }
Chris@0 190 }