SelectorListSplitter.php 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <?php
  2. namespace Gt\CssXPath;
  3. class SelectorListSplitter {
  4. /** @return array<int, string> */
  5. public function split(string $selectorList):array {
  6. $selectorList = trim($selectorList);
  7. if($selectorList === "") {
  8. return [];
  9. }
  10. $parts = [];
  11. $current = "";
  12. $quote = null;
  13. $bracketDepth = 0;
  14. $parenDepth = 0;
  15. $length = strlen($selectorList);
  16. for($i = 0; $i < $length; $i++) {
  17. $char = $selectorList[$i];
  18. if($this->handleQuotedState($char, $current, $quote)) {
  19. continue;
  20. }
  21. if($this->openQuoteIfNeeded($char, $current, $quote)) {
  22. continue;
  23. }
  24. $this->trackDepth($char, $bracketDepth, $parenDepth);
  25. if($this->isTopLevelComma($char, $bracketDepth, $parenDepth)) {
  26. $this->appendCurrentPart($parts, $current);
  27. $current = "";
  28. continue;
  29. }
  30. $current .= $char;
  31. }
  32. $this->appendCurrentPart($parts, $current);
  33. return $parts;
  34. }
  35. private function handleQuotedState(
  36. string $char,
  37. string &$current,
  38. ?string &$quote
  39. ):bool {
  40. if($quote === null) {
  41. return false;
  42. }
  43. $current .= $char;
  44. if($char === $quote) {
  45. $quote = null;
  46. }
  47. return true;
  48. }
  49. private function openQuoteIfNeeded(
  50. string $char,
  51. string &$current,
  52. ?string &$quote
  53. ):bool {
  54. if($char !== "'" && $char !== '"') {
  55. return false;
  56. }
  57. $quote = $char;
  58. $current .= $char;
  59. return true;
  60. }
  61. private function trackDepth(
  62. string $char,
  63. int &$bracketDepth,
  64. int &$parenDepth
  65. ):void {
  66. match($char) {
  67. "[" => $bracketDepth++,
  68. "]" => $bracketDepth = max(0, $bracketDepth - 1),
  69. "(" => $parenDepth++,
  70. ")" => $parenDepth = max(0, $parenDepth - 1),
  71. default => null,
  72. };
  73. }
  74. private function isTopLevelComma(
  75. string $char,
  76. int $bracketDepth,
  77. int $parenDepth
  78. ):bool {
  79. return $char === ","
  80. && $bracketDepth === 0
  81. && $parenDepth === 0;
  82. }
  83. /** @param array<int, string> $parts */
  84. private function appendCurrentPart(array &$parts, string $current):void {
  85. $trimmed = trim($current);
  86. if($trimmed !== "") {
  87. $parts[] = $trimmed;
  88. }
  89. }
  90. }