[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
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 12 namespace Symfony\Component\CssSelector\XPath\Extension; 13 14 use Symfony\Component\CssSelector\Exception\ExpressionErrorException; 15 use Symfony\Component\CssSelector\Exception\SyntaxErrorException; 16 use Symfony\Component\CssSelector\Node\FunctionNode; 17 use Symfony\Component\CssSelector\Parser\Parser; 18 use Symfony\Component\CssSelector\XPath\Translator; 19 use Symfony\Component\CssSelector\XPath\XPathExpr; 20 21 /** 22 * XPath expression translator function extension. 23 * 24 * This component is a port of the Python cssselect library, 25 * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. 26 * 27 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> 28 * 29 * @internal 30 */ 31 class FunctionExtension extends AbstractExtension 32 { 33 /** 34 * {@inheritdoc} 35 */ 36 public function getFunctionTranslators(): array 37 { 38 return [ 39 'nth-child' => [$this, 'translateNthChild'], 40 'nth-last-child' => [$this, 'translateNthLastChild'], 41 'nth-of-type' => [$this, 'translateNthOfType'], 42 'nth-last-of-type' => [$this, 'translateNthLastOfType'], 43 'contains' => [$this, 'translateContains'], 44 'lang' => [$this, 'translateLang'], 45 ]; 46 } 47 48 /** 49 * @throws ExpressionErrorException 50 */ 51 public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr 52 { 53 try { 54 [$a, $b] = Parser::parseSeries($function->getArguments()); 55 } catch (SyntaxErrorException $e) { 56 throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e); 57 } 58 59 $xpath->addStarPrefix(); 60 if ($addNameTest) { 61 $xpath->addNameTest(); 62 } 63 64 if (0 === $a) { 65 return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b)); 66 } 67 68 if ($a < 0) { 69 if ($b < 1) { 70 return $xpath->addCondition('false()'); 71 } 72 73 $sign = '<='; 74 } else { 75 $sign = '>='; 76 } 77 78 $expr = 'position()'; 79 80 if ($last) { 81 $expr = 'last() - '.$expr; 82 --$b; 83 } 84 85 if (0 !== $b) { 86 $expr .= ' - '.$b; 87 } 88 89 $conditions = [sprintf('%s %s 0', $expr, $sign)]; 90 91 if (1 !== $a && -1 !== $a) { 92 $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a); 93 } 94 95 return $xpath->addCondition(implode(' and ', $conditions)); 96 97 // todo: handle an+b, odd, even 98 // an+b means every-a, plus b, e.g., 2n+1 means odd 99 // 0n+b means b 100 // n+0 means a=1, i.e., all elements 101 // an means every a elements, i.e., 2n means even 102 // -n means -1n 103 // -1n+6 means elements 6 and previous 104 } 105 106 public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr 107 { 108 return $this->translateNthChild($xpath, $function, true); 109 } 110 111 public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr 112 { 113 return $this->translateNthChild($xpath, $function, false, false); 114 } 115 116 /** 117 * @throws ExpressionErrorException 118 */ 119 public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr 120 { 121 if ('*' === $xpath->getElement()) { 122 throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.'); 123 } 124 125 return $this->translateNthChild($xpath, $function, true, false); 126 } 127 128 /** 129 * @throws ExpressionErrorException 130 */ 131 public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr 132 { 133 $arguments = $function->getArguments(); 134 foreach ($arguments as $token) { 135 if (!($token->isString() || $token->isIdentifier())) { 136 throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments)); 137 } 138 } 139 140 return $xpath->addCondition(sprintf( 141 'contains(string(.), %s)', 142 Translator::getXpathLiteral($arguments[0]->getValue()) 143 )); 144 } 145 146 /** 147 * @throws ExpressionErrorException 148 */ 149 public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr 150 { 151 $arguments = $function->getArguments(); 152 foreach ($arguments as $token) { 153 if (!($token->isString() || $token->isIdentifier())) { 154 throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments)); 155 } 156 } 157 158 return $xpath->addCondition(sprintf( 159 'lang(%s)', 160 Translator::getXpathLiteral($arguments[0]->getValue()) 161 )); 162 } 163 164 /** 165 * {@inheritdoc} 166 */ 167 public function getName(): string 168 { 169 return 'function'; 170 } 171 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body