Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 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
    public function getFunctionTranslators(): array
34
    {
35
        return [
36
            'nth-child' => $this->translateNthChild(...),
37
            'nth-last-child' => $this->translateNthLastChild(...),
38
            'nth-of-type' => $this->translateNthOfType(...),
39
            'nth-last-of-type' => $this->translateNthLastOfType(...),
40
            'contains' => $this->translateContains(...),
41
            'lang' => $this->translateLang(...),
42
        ];
43
    }
44
 
45
    /**
46
     * @throws ExpressionErrorException
47
     */
48
    public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr
49
    {
50
        try {
51
            [$a, $b] = Parser::parseSeries($function->getArguments());
52
        } catch (SyntaxErrorException $e) {
53
            throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e);
54
        }
55
 
56
        $xpath->addStarPrefix();
57
        if ($addNameTest) {
58
            $xpath->addNameTest();
59
        }
60
 
61
        if (0 === $a) {
62
            return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
63
        }
64
 
65
        if ($a < 0) {
66
            if ($b < 1) {
67
                return $xpath->addCondition('false()');
68
            }
69
 
70
            $sign = '<=';
71
        } else {
72
            $sign = '>=';
73
        }
74
 
75
        $expr = 'position()';
76
 
77
        if ($last) {
78
            $expr = 'last() - '.$expr;
79
            --$b;
80
        }
81
 
82
        if (0 !== $b) {
83
            $expr .= ' - '.$b;
84
        }
85
 
86
        $conditions = [sprintf('%s %s 0', $expr, $sign)];
87
 
88
        if (1 !== $a && -1 !== $a) {
89
            $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
90
        }
91
 
92
        return $xpath->addCondition(implode(' and ', $conditions));
93
 
94
        // todo: handle an+b, odd, even
95
        // an+b means every-a, plus b, e.g., 2n+1 means odd
96
        // 0n+b means b
97
        // n+0 means a=1, i.e., all elements
98
        // an means every a elements, i.e., 2n means even
99
        // -n means -1n
100
        // -1n+6 means elements 6 and previous
101
    }
102
 
103
    public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr
104
    {
105
        return $this->translateNthChild($xpath, $function, true);
106
    }
107
 
108
    public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
109
    {
110
        return $this->translateNthChild($xpath, $function, false, false);
111
    }
112
 
113
    /**
114
     * @throws ExpressionErrorException
115
     */
116
    public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
117
    {
118
        if ('*' === $xpath->getElement()) {
119
            throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
120
        }
121
 
122
        return $this->translateNthChild($xpath, $function, true, false);
123
    }
124
 
125
    /**
126
     * @throws ExpressionErrorException
127
     */
128
    public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr
129
    {
130
        $arguments = $function->getArguments();
131
        foreach ($arguments as $token) {
132
            if (!($token->isString() || $token->isIdentifier())) {
133
                throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));
134
            }
135
        }
136
 
137
        return $xpath->addCondition(sprintf(
138
            'contains(string(.), %s)',
139
            Translator::getXpathLiteral($arguments[0]->getValue())
140
        ));
141
    }
142
 
143
    /**
144
     * @throws ExpressionErrorException
145
     */
146
    public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
147
    {
148
        $arguments = $function->getArguments();
149
        foreach ($arguments as $token) {
150
            if (!($token->isString() || $token->isIdentifier())) {
151
                throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
152
            }
153
        }
154
 
155
        return $xpath->addCondition(sprintf(
156
            'lang(%s)',
157
            Translator::getXpathLiteral($arguments[0]->getValue())
158
        ));
159
    }
160
 
161
    public function getName(): string
162
    {
163
        return 'function';
164
    }
165
}