AttributeMatchingExtension.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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\XPath\Translator;
  12. use Symfony\Component\CssSelector\XPath\XPathExpr;
  13. /**
  14. * XPath expression translator attribute 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 AttributeMatchingExtension extends AbstractExtension
  24. {
  25. public function getAttributeMatchingTranslators(): array
  26. {
  27. return [
  28. 'exists' => $this->translateExists(...),
  29. '=' => $this->translateEquals(...),
  30. '~=' => $this->translateIncludes(...),
  31. '|=' => $this->translateDashMatch(...),
  32. '^=' => $this->translatePrefixMatch(...),
  33. '$=' => $this->translateSuffixMatch(...),
  34. '*=' => $this->translateSubstringMatch(...),
  35. '!=' => $this->translateDifferent(...),
  36. ];
  37. }
  38. public function translateExists(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  39. {
  40. return $xpath->addCondition($attribute);
  41. }
  42. public function translateEquals(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  43. {
  44. return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value)));
  45. }
  46. public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  47. {
  48. return $xpath->addCondition($value ? sprintf(
  49. '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)',
  50. $attribute,
  51. Translator::getXpathLiteral(' '.$value.' ')
  52. ) : '0');
  53. }
  54. public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  55. {
  56. return $xpath->addCondition(sprintf(
  57. '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))',
  58. $attribute,
  59. Translator::getXpathLiteral($value),
  60. Translator::getXpathLiteral($value.'-')
  61. ));
  62. }
  63. public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  64. {
  65. return $xpath->addCondition($value ? sprintf(
  66. '%1$s and starts-with(%1$s, %2$s)',
  67. $attribute,
  68. Translator::getXpathLiteral($value)
  69. ) : '0');
  70. }
  71. public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  72. {
  73. return $xpath->addCondition($value ? sprintf(
  74. '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s',
  75. $attribute,
  76. \strlen($value) - 1,
  77. Translator::getXpathLiteral($value)
  78. ) : '0');
  79. }
  80. public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  81. {
  82. return $xpath->addCondition($value ? sprintf(
  83. '%1$s and contains(%1$s, %2$s)',
  84. $attribute,
  85. Translator::getXpathLiteral($value)
  86. ) : '0');
  87. }
  88. public function translateDifferent(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
  89. {
  90. return $xpath->addCondition(sprintf(
  91. $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s',
  92. $attribute,
  93. Translator::getXpathLiteral($value)
  94. ));
  95. }
  96. public function getName(): string
  97. {
  98. return 'attribute-matching';
  99. }
  100. }