PseudoClassExtension.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\CssSelector\XPath\Extension;
  11. use Symfony\Component\CssSelector\Exception\ExpressionErrorException;
  12. use Symfony\Component\CssSelector\XPath\XPathExpr;
  13. /**
  14. * XPath expression translator pseudo-class extension.
  15. *
  16. * This component is a port of the Python cssselect library,
  17. * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
  18. *
  19. * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
  20. *
  21. * @internal
  22. */
  23. class PseudoClassExtension extends AbstractExtension
  24. {
  25. /**
  26. * {@inheritdoc}
  27. */
  28. public function getPseudoClassTranslators()
  29. {
  30. return [
  31. 'root' => [$this, 'translateRoot'],
  32. 'first-child' => [$this, 'translateFirstChild'],
  33. 'last-child' => [$this, 'translateLastChild'],
  34. 'first-of-type' => [$this, 'translateFirstOfType'],
  35. 'last-of-type' => [$this, 'translateLastOfType'],
  36. 'only-child' => [$this, 'translateOnlyChild'],
  37. 'only-of-type' => [$this, 'translateOnlyOfType'],
  38. 'empty' => [$this, 'translateEmpty'],
  39. ];
  40. }
  41. /**
  42. * @return XPathExpr
  43. */
  44. public function translateRoot(XPathExpr $xpath)
  45. {
  46. return $xpath->addCondition('not(parent::*)');
  47. }
  48. /**
  49. * @return XPathExpr
  50. */
  51. public function translateFirstChild(XPathExpr $xpath)
  52. {
  53. return $xpath
  54. ->addStarPrefix()
  55. ->addNameTest()
  56. ->addCondition('position() = 1');
  57. }
  58. /**
  59. * @return XPathExpr
  60. */
  61. public function translateLastChild(XPathExpr $xpath)
  62. {
  63. return $xpath
  64. ->addStarPrefix()
  65. ->addNameTest()
  66. ->addCondition('position() = last()');
  67. }
  68. /**
  69. * @return XPathExpr
  70. *
  71. * @throws ExpressionErrorException
  72. */
  73. public function translateFirstOfType(XPathExpr $xpath)
  74. {
  75. if ('*' === $xpath->getElement()) {
  76. throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
  77. }
  78. return $xpath
  79. ->addStarPrefix()
  80. ->addCondition('position() = 1');
  81. }
  82. /**
  83. * @return XPathExpr
  84. *
  85. * @throws ExpressionErrorException
  86. */
  87. public function translateLastOfType(XPathExpr $xpath)
  88. {
  89. if ('*' === $xpath->getElement()) {
  90. throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
  91. }
  92. return $xpath
  93. ->addStarPrefix()
  94. ->addCondition('position() = last()');
  95. }
  96. /**
  97. * @return XPathExpr
  98. */
  99. public function translateOnlyChild(XPathExpr $xpath)
  100. {
  101. return $xpath
  102. ->addStarPrefix()
  103. ->addNameTest()
  104. ->addCondition('last() = 1');
  105. }
  106. /**
  107. * @return XPathExpr
  108. *
  109. * @throws ExpressionErrorException
  110. */
  111. public function translateOnlyOfType(XPathExpr $xpath)
  112. {
  113. if ('*' === $xpath->getElement()) {
  114. throw new ExpressionErrorException('"*:only-of-type" is not implemented.');
  115. }
  116. return $xpath->addCondition('last() = 1');
  117. }
  118. /**
  119. * @return XPathExpr
  120. */
  121. public function translateEmpty(XPathExpr $xpath)
  122. {
  123. return $xpath->addCondition('not(*) and not(string-length())');
  124. }
  125. /**
  126. * {@inheritdoc}
  127. */
  128. public function getName()
  129. {
  130. return 'pseudo-class';
  131. }
  132. }