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\XPath\XPathExpr;
16
17/**
18 * XPath expression translator pseudo-class extension.
19 *
20 * This component is a port of the Python cssselect library,
21 * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
22 *
23 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
24 *
25 * @internal
26 */
27class PseudoClassExtension extends AbstractExtension
28{
29 public function getPseudoClassTranslators(): array
30 {
31 return [
32 'root' => $this->translateRoot(...),
33 'scope' => $this->translateScopePseudo(...),
34 'first-child' => $this->translateFirstChild(...),
35 'last-child' => $this->translateLastChild(...),
36 'first-of-type' => $this->translateFirstOfType(...),
37 'last-of-type' => $this->translateLastOfType(...),
38 'only-child' => $this->translateOnlyChild(...),
39 'only-of-type' => $this->translateOnlyOfType(...),
40 'empty' => $this->translateEmpty(...),
41 ];
42 }
43
44 public function translateRoot(XPathExpr $xpath): XPathExpr
45 {
46 return $xpath->addCondition('not(parent::*)');
47 }
48
49 public function translateScopePseudo(XPathExpr $xpath): XPathExpr
50 {
51 return $xpath->addCondition('1');
52 }
53
54 public function translateFirstChild(XPathExpr $xpath): XPathExpr
55 {
56 return $xpath
57 ->addStarPrefix()
58 ->addNameTest()
59 ->addCondition('position() = 1');
60 }
61
62 public function translateLastChild(XPathExpr $xpath): XPathExpr
63 {
64 return $xpath
65 ->addStarPrefix()
66 ->addNameTest()
67 ->addCondition('position() = last()');
68 }
69
70 /**
71 * @throws ExpressionErrorException
72 */
73 public function translateFirstOfType(XPathExpr $xpath): XPathExpr
74 {
75 if ('*' === $xpath->getElement()) {
76 throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
77 }
78
79 return $xpath
80 ->addStarPrefix()
81 ->addCondition('position() = 1');
82 }
83
84 /**
85 * @throws ExpressionErrorException
86 */
87 public function translateLastOfType(XPathExpr $xpath): XPathExpr
88 {
89 if ('*' === $xpath->getElement()) {
90 throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
91 }
92
93 return $xpath
94 ->addStarPrefix()
95 ->addCondition('position() = last()');
96 }
97
98 public function translateOnlyChild(XPathExpr $xpath): XPathExpr
99 {
100 return $xpath
101 ->addStarPrefix()
102 ->addNameTest()
103 ->addCondition('last() = 1');
104 }
105
106 public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
107 {
108 $element = $xpath->getElement();
109
110 return $xpath->addCondition(\sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
111 }
112
113 public function translateEmpty(XPathExpr $xpath): XPathExpr
114 {
115 return $xpath->addCondition('not(*) and not(string-length())');
116 }
117
118 public function getName(): string
119 {
120 return 'pseudo-class';
121 }
122}