PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/ReferenceCode/smarthouse/mustache/php/vendor/Mustache/Tokenizer.php

https://gitlab.com/ctheilman92/Aging-In-Place
PHP | 310 lines | 218 code | 32 blank | 60 comment | 36 complexity | cb717f5461750caf804f3d0f98f3922b MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of Mustache.php.
  4. *
  5. * (c) 2012 Justin Hileman
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * Mustache Tokenizer class.
  12. *
  13. * This class is responsible for turning raw template source into a set of Mustache tokens.
  14. */
  15. class Mustache_Tokenizer
  16. {
  17. // Finite state machine states
  18. const IN_TEXT = 0;
  19. const IN_TAG_TYPE = 1;
  20. const IN_TAG = 2;
  21. // Token types
  22. const T_SECTION = '#';
  23. const T_INVERTED = '^';
  24. const T_END_SECTION = '/';
  25. const T_COMMENT = '!';
  26. const T_PARTIAL = '>';
  27. const T_PARTIAL_2 = '<';
  28. const T_DELIM_CHANGE = '=';
  29. const T_ESCAPED = '_v';
  30. const T_UNESCAPED = '{';
  31. const T_UNESCAPED_2 = '&';
  32. const T_TEXT = '_t';
  33. const T_PRAGMA = '%';
  34. // Valid token types
  35. private static $tagTypes = array(
  36. self::T_SECTION => true,
  37. self::T_INVERTED => true,
  38. self::T_END_SECTION => true,
  39. self::T_COMMENT => true,
  40. self::T_PARTIAL => true,
  41. self::T_PARTIAL_2 => true,
  42. self::T_DELIM_CHANGE => true,
  43. self::T_ESCAPED => true,
  44. self::T_UNESCAPED => true,
  45. self::T_UNESCAPED_2 => true,
  46. self::T_PRAGMA => true,
  47. );
  48. // Interpolated tags
  49. private static $interpolatedTags = array(
  50. self::T_ESCAPED => true,
  51. self::T_UNESCAPED => true,
  52. self::T_UNESCAPED_2 => true,
  53. );
  54. // Token properties
  55. const TYPE = 'type';
  56. const NAME = 'name';
  57. const OTAG = 'otag';
  58. const CTAG = 'ctag';
  59. const INDEX = 'index';
  60. const END = 'end';
  61. const INDENT = 'indent';
  62. const NODES = 'nodes';
  63. const VALUE = 'value';
  64. private $pragmas;
  65. private $state;
  66. private $tagType;
  67. private $tag;
  68. private $buffer;
  69. private $tokens;
  70. private $seenTag;
  71. private $lineStart;
  72. private $otag;
  73. private $ctag;
  74. /**
  75. * Scan and tokenize template source.
  76. *
  77. * @param string $text Mustache template source to tokenize
  78. * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null)
  79. *
  80. * @return array Set of Mustache tokens
  81. */
  82. public function scan($text, $delimiters = null)
  83. {
  84. $this->reset();
  85. if ($delimiters = trim($delimiters)) {
  86. list($otag, $ctag) = explode(' ', $delimiters);
  87. $this->otag = $otag;
  88. $this->ctag = $ctag;
  89. }
  90. $len = strlen($text);
  91. for ($i = 0; $i < $len; $i++) {
  92. switch ($this->state) {
  93. case self::IN_TEXT:
  94. if ($this->tagChange($this->otag, $text, $i)) {
  95. $i--;
  96. $this->flushBuffer();
  97. $this->state = self::IN_TAG_TYPE;
  98. } else {
  99. $char = substr($text, $i, 1);
  100. if ($char == "\n") {
  101. $this->filterLine();
  102. } else {
  103. $this->buffer .= $char;
  104. }
  105. }
  106. break;
  107. case self::IN_TAG_TYPE:
  108. $i += strlen($this->otag) - 1;
  109. $char = substr($text, $i + 1, 1);
  110. if (isset(self::$tagTypes[$char])) {
  111. $tag = $char;
  112. $this->tagType = $tag;
  113. } else {
  114. $tag = null;
  115. $this->tagType = self::T_ESCAPED;
  116. }
  117. if ($this->tagType === self::T_DELIM_CHANGE) {
  118. $i = $this->changeDelimiters($text, $i);
  119. $this->state = self::IN_TEXT;
  120. } elseif ($this->tagType === self::T_PRAGMA) {
  121. $i = $this->addPragma($text, $i);
  122. $this->state = self::IN_TEXT;
  123. } else {
  124. if ($tag !== null) {
  125. $i++;
  126. }
  127. $this->state = self::IN_TAG;
  128. }
  129. $this->seenTag = $i;
  130. break;
  131. default:
  132. if ($this->tagChange($this->ctag, $text, $i)) {
  133. $this->tokens[] = array(
  134. self::TYPE => $this->tagType,
  135. self::NAME => trim($this->buffer),
  136. self::OTAG => $this->otag,
  137. self::CTAG => $this->ctag,
  138. self::INDEX => ($this->tagType == self::T_END_SECTION) ? $this->seenTag - strlen($this->otag) : $i + strlen($this->ctag)
  139. );
  140. $this->buffer = '';
  141. $i += strlen($this->ctag) - 1;
  142. $this->state = self::IN_TEXT;
  143. if ($this->tagType == self::T_UNESCAPED) {
  144. if ($this->ctag == '}}') {
  145. $i++;
  146. } else {
  147. // Clean up `{{{ tripleStache }}}` style tokens.
  148. $lastName = $this->tokens[count($this->tokens) - 1][self::NAME];
  149. if (substr($lastName, -1) === '}') {
  150. $this->tokens[count($this->tokens) - 1][self::NAME] = trim(substr($lastName, 0, -1));
  151. }
  152. }
  153. }
  154. } else {
  155. $this->buffer .= substr($text, $i, 1);
  156. }
  157. break;
  158. }
  159. }
  160. $this->filterLine(true);
  161. foreach ($this->pragmas as $pragma) {
  162. array_unshift($this->tokens, array(
  163. self::TYPE => self::T_PRAGMA,
  164. self::NAME => $pragma,
  165. ));
  166. }
  167. return $this->tokens;
  168. }
  169. /**
  170. * Helper function to reset tokenizer internal state.
  171. */
  172. private function reset()
  173. {
  174. $this->state = self::IN_TEXT;
  175. $this->tagType = null;
  176. $this->tag = null;
  177. $this->buffer = '';
  178. $this->tokens = array();
  179. $this->seenTag = false;
  180. $this->lineStart = 0;
  181. $this->otag = '{{';
  182. $this->ctag = '}}';
  183. $this->pragmas = array();
  184. }
  185. /**
  186. * Flush the current buffer to a token.
  187. */
  188. private function flushBuffer()
  189. {
  190. if (!empty($this->buffer)) {
  191. $this->tokens[] = array(self::TYPE => self::T_TEXT, self::VALUE => $this->buffer);
  192. $this->buffer = '';
  193. }
  194. }
  195. /**
  196. * Test whether the current line is entirely made up of whitespace.
  197. *
  198. * @return boolean True if the current line is all whitespace
  199. */
  200. private function lineIsWhitespace()
  201. {
  202. $tokensCount = count($this->tokens);
  203. for ($j = $this->lineStart; $j < $tokensCount; $j++) {
  204. $token = $this->tokens[$j];
  205. if (isset(self::$tagTypes[$token[self::TYPE]])) {
  206. if (isset(self::$interpolatedTags[$token[self::TYPE]])) {
  207. return false;
  208. }
  209. } elseif ($token[self::TYPE] == self::T_TEXT) {
  210. if (preg_match('/\S/', $token[self::VALUE])) {
  211. return false;
  212. }
  213. }
  214. }
  215. return true;
  216. }
  217. /**
  218. * Filter out whitespace-only lines and store indent levels for partials.
  219. *
  220. * @param bool $noNewLine Suppress the newline? (default: false)
  221. */
  222. private function filterLine($noNewLine = false)
  223. {
  224. $this->flushBuffer();
  225. if ($this->seenTag && $this->lineIsWhitespace()) {
  226. $tokensCount = count($this->tokens);
  227. for ($j = $this->lineStart; $j < $tokensCount; $j++) {
  228. if ($this->tokens[$j][self::TYPE] == self::T_TEXT) {
  229. if (isset($this->tokens[$j+1]) && $this->tokens[$j+1][self::TYPE] == self::T_PARTIAL) {
  230. $this->tokens[$j+1][self::INDENT] = $this->tokens[$j][self::VALUE];
  231. }
  232. $this->tokens[$j] = null;
  233. }
  234. }
  235. } elseif (!$noNewLine) {
  236. $this->tokens[] = array(self::TYPE => self::T_TEXT, self::VALUE => "\n");
  237. }
  238. $this->seenTag = false;
  239. $this->lineStart = count($this->tokens);
  240. }
  241. /**
  242. * Change the current Mustache delimiters. Set new `otag` and `ctag` values.
  243. *
  244. * @param string $text Mustache template source
  245. * @param int $index Current tokenizer index
  246. *
  247. * @return int New index value
  248. */
  249. private function changeDelimiters($text, $index)
  250. {
  251. $startIndex = strpos($text, '=', $index) + 1;
  252. $close = '='.$this->ctag;
  253. $closeIndex = strpos($text, $close, $index);
  254. list($otag, $ctag) = explode(' ', trim(substr($text, $startIndex, $closeIndex - $startIndex)));
  255. $this->otag = $otag;
  256. $this->ctag = $ctag;
  257. return $closeIndex + strlen($close) - 1;
  258. }
  259. private function addPragma($text, $index)
  260. {
  261. $end = strpos($text, $this->ctag, $index);
  262. $this->pragmas[] = trim(substr($text, $index + 2, $end - $index - 2));
  263. return $end + strlen($this->ctag) - 1;
  264. }
  265. /**
  266. * Test whether it's time to change tags.
  267. *
  268. * @param string $tag Current tag name
  269. * @param string $text Mustache template source
  270. * @param int $index Current tokenizer index
  271. *
  272. * @return boolean True if this is a closing section tag
  273. */
  274. private function tagChange($tag, $text, $index)
  275. {
  276. return substr($text, $index, strlen($tag)) === $tag;
  277. }
  278. }