CssSelectorConverter.php 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  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;
  11. use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser;
  12. use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser;
  13. use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser;
  14. use Symfony\Component\CssSelector\Parser\Shortcut\HashParser;
  15. use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension;
  16. use Symfony\Component\CssSelector\XPath\Translator;
  17. /**
  18. * CssSelectorConverter is the main entry point of the component and can convert CSS
  19. * selectors to XPath expressions.
  20. *
  21. * @author Christophe Coevoet <stof@notk.org>
  22. */
  23. class CssSelectorConverter
  24. {
  25. public static int $maxCachedItems = 1024;
  26. private Translator $translator;
  27. private array $cache;
  28. private static array $xmlCache = [];
  29. private static array $htmlCache = [];
  30. /**
  31. * @param bool $html Whether HTML support should be enabled. Disable it for XML documents
  32. */
  33. public function __construct(bool $html = true)
  34. {
  35. $this->translator = new Translator();
  36. if ($html) {
  37. $this->translator->registerExtension(new HtmlExtension($this->translator));
  38. $this->cache = &self::$htmlCache;
  39. } else {
  40. $this->cache = &self::$xmlCache;
  41. }
  42. $this->translator
  43. ->registerParserShortcut(new EmptyStringParser())
  44. ->registerParserShortcut(new ElementParser())
  45. ->registerParserShortcut(new ClassParser())
  46. ->registerParserShortcut(new HashParser())
  47. ;
  48. }
  49. /**
  50. * Translates a CSS expression to its XPath equivalent.
  51. *
  52. * Optionally, a prefix can be added to the resulting XPath
  53. * expression with the $prefix parameter.
  54. */
  55. public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string
  56. {
  57. $cacheKey = $prefix."\0".$cssExpr;
  58. if (isset($this->cache[$cacheKey])) {
  59. // Move the item last in cache (LRU)
  60. $value = $this->cache[$cacheKey];
  61. unset($this->cache[$cacheKey]);
  62. return $this->cache[$cacheKey] = $value;
  63. }
  64. if (\count($this->cache) >= self::$maxCachedItems) {
  65. // Evict the oldest entry
  66. unset($this->cache[array_key_first($this->cache)]);
  67. }
  68. return $this->cache[$cacheKey] = $this->translator->cssToXPath($cssExpr, $prefix);
  69. }
  70. }