PluginProbe
GetResponse Forms by Optin Cat / 1.5.4
GetResponse Forms by Optin Cat v1.5.4
1.3.4 1.3.5 1.3.6 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 2.0.0 All 36 releases
getresponse / includes / classes / Mustache / Tokenizer.php

Tokenizer.php in GetResponse Forms by Optin Cat 1.5.4, at includes/classes/Mustache/Tokenizer.php

328 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of Mustache.php.
5 *
6 * (c) 2010-2014 Justin Hileman
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 /**
13 * Mustache Tokenizer class.
14 *
15 * This class is responsible for turning raw template source into a set of Mustache tokens.
16 */
17 class Mustache_Tokenizer
18 {
19 // Finite state machine states
20 const IN_TEXT = 0;
21 const IN_TAG_TYPE = 1;
22 const IN_TAG = 2;
23
24 // Token types
25 const T_SECTION = '#';
26 const T_INVERTED = '^';
27 const T_END_SECTION = '/';
28 const T_COMMENT = '!';
29 const T_PARTIAL = '>';
30 const T_PARTIAL_2 = '<';
31 const T_DELIM_CHANGE = '=';
32 const T_ESCAPED = '_v';
33 const T_UNESCAPED = '{';
34 const T_UNESCAPED_2 = '&';
35 const T_TEXT = '_t';
36 const T_PRAGMA = '%';
37
38 // Valid token types
39 private static $tagTypes = array(
40 self::T_SECTION => true,
41 self::T_INVERTED => true,
42 self::T_END_SECTION => true,
43 self::T_COMMENT => true,
44 self::T_PARTIAL => true,
45 self::T_PARTIAL_2 => true,
46 self::T_DELIM_CHANGE => true,
47 self::T_ESCAPED => true,
48 self::T_UNESCAPED => true,
49 self::T_UNESCAPED_2 => true,
50 self::T_PRAGMA => true,
51 );
52
53 // Interpolated tags
54 private static $interpolatedTags = array(
55 self::T_ESCAPED => true,
56 self::T_UNESCAPED => true,
57 self::T_UNESCAPED_2 => true,
58 );
59
60 // Token properties
61 const TYPE = 'type';
62 const NAME = 'name';
63 const OTAG = 'otag';
64 const CTAG = 'ctag';
65 const LINE = 'line';
66 const INDEX = 'index';
67 const END = 'end';
68 const INDENT = 'indent';
69 const NODES = 'nodes';
70 const VALUE = 'value';
71
72 private $state;
73 private $tagType;
74 private $tag;
75 private $buffer;
76 private $tokens;
77 private $seenTag;
78 private $line;
79 private $otag;
80 private $ctag;
81 private $otagLen;
82 private $ctagLen;
83
84 /**
85 * Scan and tokenize template source.
86 *
87 * @throws Mustache_Exception_SyntaxException when mismatched section tags are encountered.
88 *
89 * @param string $text Mustache template source to tokenize
90 * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null)
91 *
92 * @return array Set of Mustache tokens
93 */
94 public function scan($text, $delimiters = null)
95 {
96 // Setting mbstring.func_overload makes things *really* slow.
97 // Let's do everyone a favor and scan this string as ASCII instead.
98 $encoding = null;
99 if (function_exists('mb_internal_encoding') && ini_get('mbstring.func_overload') & 2) {
100 $encoding = mb_internal_encoding();
101 mb_internal_encoding('ASCII');
102 }
103
104 $this->reset();
105
106 if ($delimiters = trim($delimiters)) {
107 $this->setDelimiters($delimiters);
108 }
109
110 $len = strlen($text);
111 for ($i = 0; $i < $len; $i++) {
112 switch ($this->state) {
113 case self::IN_TEXT:
114 if ($this->tagChange($this->otag, $this->otagLen, $text, $i)) {
115 $i--;
116 $this->flushBuffer();
117 $this->state = self::IN_TAG_TYPE;
118 } else {
119 $char = $text[$i];
120 $this->buffer .= $char;
121 if ($char === "\n") {
122 $this->flushBuffer();
123 $this->line++;
124 }
125 }
126 break;
127
128 case self::IN_TAG_TYPE:
129 $i += $this->otagLen - 1;
130 $char = $text[$i + 1];
131 if (isset(self::$tagTypes[$char])) {
132 $tag = $char;
133 $this->tagType = $tag;
134 } else {
135 $tag = null;
136 $this->tagType = self::T_ESCAPED;
137 }
138
139 if ($this->tagType === self::T_DELIM_CHANGE) {
140 $i = $this->changeDelimiters($text, $i);
141 $this->state = self::IN_TEXT;
142 } elseif ($this->tagType === self::T_PRAGMA) {
143 $i = $this->addPragma($text, $i);
144 $this->state = self::IN_TEXT;
145 } else {
146 if ($tag !== null) {
147 $i++;
148 }
149 $this->state = self::IN_TAG;
150 }
151 $this->seenTag = $i;
152 break;
153
154 default:
155 if ($this->tagChange($this->ctag, $this->ctagLen, $text, $i)) {
156 $token = array(
157 self::TYPE => $this->tagType,
158 self::NAME => trim($this->buffer),
159 self::OTAG => $this->otag,
160 self::CTAG => $this->ctag,
161 self::LINE => $this->line,
162 self::INDEX => ($this->tagType === self::T_END_SECTION) ? $this->seenTag - $this->otagLen : $i + $this->ctagLen
163 );
164
165 if ($this->tagType === self::T_UNESCAPED) {
166 // Clean up `{{{ tripleStache }}}` style tokens.
167 if ($this->ctag === '}}') {
168 if (($i + 2 < $len) && $text[$i + 2] === '}') {
169 $i++;
170 } else {
171 $msg = sprintf(
172 'Mismatched tag delimiters: %s on line %d',
173 $token[self::NAME],
174 $token[self::LINE]
175 );
176
177 throw new Mustache_Exception_SyntaxException($msg, $token);
178 }
179 } else {
180 $lastName = $token[self::NAME];
181 if (substr($lastName, -1) === '}') {
182 $token[self::NAME] = trim(substr($lastName, 0, -1));
183 } else {
184 $msg = sprintf(
185 'Mismatched tag delimiters: %s on line %d',
186 $token[self::NAME],
187 $token[self::LINE]
188 );
189
190 throw new Mustache_Exception_SyntaxException($msg, $token);
191 }
192 }
193 }
194
195 $this->buffer = '';
196 $i += $this->ctagLen - 1;
197 $this->state = self::IN_TEXT;
198 $this->tokens[] = $token;
199 } else {
200 $this->buffer .= $text[$i];
201 }
202 break;
203 }
204 }
205
206 $this->flushBuffer();
207
208 // Restore the user's encoding...
209 if ($encoding) {
210 mb_internal_encoding($encoding);
211 }
212
213 return $this->tokens;
214 }
215
216 /**
217 * Helper function to reset tokenizer internal state.
218 */
219 private function reset()
220 {
221 $this->state = self::IN_TEXT;
222 $this->tagType = null;
223 $this->tag = null;
224 $this->buffer = '';
225 $this->tokens = array();
226 $this->seenTag = false;
227 $this->line = 0;
228 $this->otag = '{{';
229 $this->ctag = '}}';
230 $this->otagLen = 2;
231 $this->ctagLen = 2;
232 }
233
234 /**
235 * Flush the current buffer to a token.
236 */
237 private function flushBuffer()
238 {
239 if (strlen($this->buffer) > 0) {
240 $this->tokens[] = array(
241 self::TYPE => self::T_TEXT,
242 self::LINE => $this->line,
243 self::VALUE => $this->buffer
244 );
245 $this->buffer = '';
246 }
247 }
248
249 /**
250 * Change the current Mustache delimiters. Set new `otag` and `ctag` values.
251 *
252 * @param string $text Mustache template source
253 * @param int $index Current tokenizer index
254 *
255 * @return int New index value
256 */
257 private function changeDelimiters($text, $index)
258 {
259 $startIndex = strpos($text, '=', $index) + 1;
260 $close = '='.$this->ctag;
261 $closeIndex = strpos($text, $close, $index);
262
263 $this->setDelimiters(trim(substr($text, $startIndex, $closeIndex - $startIndex)));
264
265 $this->tokens[] = array(
266 self::TYPE => self::T_DELIM_CHANGE,
267 self::LINE => $this->line,
268 );
269
270 return $closeIndex + strlen($close) - 1;
271 }
272
273 /**
274 * Set the current Mustache `otag` and `ctag` delimiters.
275 *
276 * @param string $delimiters
277 */
278 private function setDelimiters($delimiters)
279 {
280 list($otag, $ctag) = explode(' ', $delimiters);
281 $this->otag = $otag;
282 $this->ctag = $ctag;
283 $this->otagLen = strlen($otag);
284 $this->ctagLen = strlen($ctag);
285 }
286
287 /**
288 * Add pragma token.
289 *
290 * Pragmas are hoisted to the front of the template, so all pragma tokens
291 * will appear at the front of the token list.
292 *
293 * @param string $text
294 * @param int $index
295 *
296 * @return int New index value
297 */
298 private function addPragma($text, $index)
299 {
300 $end = strpos($text, $this->ctag, $index);
301 $pragma = trim(substr($text, $index + 2, $end - $index - 2));
302
303 // Pragmas are hoisted to the front of the template.
304 array_unshift($this->tokens, array(
305 self::TYPE => self::T_PRAGMA,
306 self::NAME => $pragma,
307 self::LINE => 0,
308 ));
309
310 return $end + $this->ctagLen - 1;
311 }
312
313 /**
314 * Test whether it's time to change tags.
315 *
316 * @param string $tag Current tag name
317 * @param int $tagLen Current tag name length
318 * @param string $text Mustache template source
319 * @param int $index Current tokenizer index
320 *
321 * @return boolean True if this is a closing section tag
322 */
323 private function tagChange($tag, $tagLen, $text, $index)
324 {
325 return substr($text, $index, $tagLen) === $tag;
326 }
327 }
328