TemplateTokenizer.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. <?php
  2. namespace App\Services\Template;
  3. // ================== 词法分析器 ==================
  4. class TemplateTokenizer
  5. {
  6. private string $content;
  7. private int $position = 0;
  8. private int $length;
  9. public function __construct(string $content)
  10. {
  11. $this->content = $content;
  12. $this->length = strlen($content);
  13. }
  14. public function tokenize(): array
  15. {
  16. $tokens = [];
  17. $this->position = 0;
  18. while ($this->position < $this->length) {
  19. $token = $this->nextToken();
  20. if ($token) {
  21. $tokens[] = $token;
  22. }
  23. }
  24. return $tokens;
  25. }
  26. private function nextToken(): ?array
  27. {
  28. // 寻找模板开始标记
  29. $templateStart = strpos($this->content, '{{', $this->position);
  30. if ($templateStart === false) {
  31. // 没有更多模板,返回剩余文本
  32. if ($this->position < $this->length) {
  33. $text = substr($this->content, $this->position);
  34. $this->position = $this->length;
  35. return [
  36. 'type' => 'text',
  37. 'content' => $text,
  38. 'position' => ['start' => $this->position - strlen($text), 'end' => $this->position]
  39. ];
  40. }
  41. return null;
  42. }
  43. // 如果模板前有文本
  44. if ($templateStart > $this->position) {
  45. $text = substr($this->content, $this->position, $templateStart - $this->position);
  46. $this->position = $templateStart;
  47. return [
  48. 'type' => 'text',
  49. 'content' => $text,
  50. 'position' => ['start' => $this->position - strlen($text), 'end' => $this->position]
  51. ];
  52. }
  53. // 解析模板
  54. return $this->parseTemplate();
  55. }
  56. private function parseTemplate(): ?array
  57. {
  58. $start = $this->position;
  59. $this->position += 2; // 跳过 '{{'
  60. $braceCount = 1;
  61. $templateContent = '';
  62. while ($this->position < $this->length && $braceCount > 0) {
  63. $char = $this->content[$this->position];
  64. $nextChar = $this->position + 1 < $this->length ? $this->content[$this->position + 1] : '';
  65. if ($char === '{' && $nextChar === '{') {
  66. $braceCount++;
  67. $templateContent .= '{{';
  68. $this->position += 2;
  69. } elseif ($char === '}' && $nextChar === '}') {
  70. $braceCount--;
  71. if ($braceCount > 0) {
  72. $templateContent .= '}}';
  73. }
  74. $this->position += 2;
  75. } else {
  76. $templateContent .= $char;
  77. $this->position++;
  78. }
  79. }
  80. if ($braceCount > 0) {
  81. // 未闭合的模板,当作普通文本处理
  82. $this->position = $start + 2;
  83. return [
  84. 'type' => 'text',
  85. 'content' => '{{',
  86. 'position' => ['start' => $start, 'end' => $start + 2]
  87. ];
  88. }
  89. return [
  90. 'type' => 'template',
  91. 'content' => trim($templateContent),
  92. 'raw' => '{{' . $templateContent . '}}',
  93. 'position' => ['start' => $start, 'end' => $this->position]
  94. ];
  95. }
  96. }