PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 2.0.4
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v2.0.4
4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 All 340 releases
profile-builder / assets / lib / Mustache / Tokenizer.php

Tokenizer.php in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 2.0.4, at assets/lib/Mustache/Tokenizer.php

279 lines 8.6 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) 2012 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
20 // Finite state machine states
21 const IN_TEXT = 0;
22 const IN_TAG_TYPE = 1;
23 const IN_TAG = 2;
24
25 // Token types
26 const T_SECTION = '#';
27 const T_INVERTED = '^';
28 const T_END_SECTION = '/';
29 const T_COMMENT = '!';
30 const T_PARTIAL = '>';
31 const T_PARTIAL_2 = '<';
32 const T_DELIM_CHANGE = '=';
33 const T_ESCAPED = '_v';
34 const T_UNESCAPED = '{';
35 const T_UNESCAPED_2 = '&';
36 const T_TEXT = '_t';
37 const T_PRAGMA = '%';
38
39 // Valid token types
40 private static $tagTypes = array(
41 self::T_SECTION => true,
42 self::T_INVERTED => true,
43 self::T_END_SECTION => true,
44 self::T_COMMENT => true,
45 self::T_PARTIAL => true,
46 self::T_PARTIAL_2 => true,
47 self::T_DELIM_CHANGE => true,
48 self::T_ESCAPED => true,
49 self::T_UNESCAPED => true,
50 self::T_UNESCAPED_2 => true,
51 self::T_PRAGMA => true,
52 );
53
54 // Interpolated tags
55 private static $interpolatedTags = array(
56 self::T_ESCAPED => true,
57 self::T_UNESCAPED => true,
58 self::T_UNESCAPED_2 => true,
59 );
60
61 // Token properties
62 const TYPE = 'type';
63 const NAME = 'name';
64 const OTAG = 'otag';
65 const CTAG = 'ctag';
66 const LINE = 'line';
67 const INDEX = 'index';
68 const END = 'end';
69 const INDENT = 'indent';
70 const NODES = 'nodes';
71 const VALUE = 'value';
72
73 private $state;
74 private $tagType;
75 private $tag;
76 private $buffer;
77 private $tokens;
78 private $seenTag;
79 private $line;
80 private $otag;
81 private $ctag;
82
83 /**
84 * Scan and tokenize template source.
85 *
86 * @param string $text Mustache template source to tokenize
87 * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null)
88 *
89 * @return array Set of Mustache tokens
90 */
91 public function scan($text, $delimiters = null)
92 {
93 $this->reset();
94
95 if ($delimiters = trim($delimiters)) {
96 list($otag, $ctag) = explode(' ', $delimiters);
97 $this->otag = $otag;
98 $this->ctag = $ctag;
99 }
100
101 $len = strlen($text);
102 for ($i = 0; $i < $len; $i++) {
103 switch ($this->state) {
104 case self::IN_TEXT:
105 if ($this->tagChange($this->otag, $text, $i)) {
106 $i--;
107 $this->flushBuffer();
108 $this->state = self::IN_TAG_TYPE;
109 } else {
110 $char = substr($text, $i, 1);
111 $this->buffer .= $char;
112 if ($char == "\n") {
113 $this->flushBuffer();
114 $this->line++;
115 }
116 }
117 break;
118
119 case self::IN_TAG_TYPE:
120 $i += strlen($this->otag) - 1;
121 $char = substr($text, $i + 1, 1);
122 if (isset(self::$tagTypes[$char])) {
123 $tag = $char;
124 $this->tagType = $tag;
125 } else {
126 $tag = null;
127 $this->tagType = self::T_ESCAPED;
128 }
129
130 if ($this->tagType === self::T_DELIM_CHANGE) {
131 $i = $this->changeDelimiters($text, $i);
132 $this->state = self::IN_TEXT;
133 } elseif ($this->tagType === self::T_PRAGMA) {
134 $i = $this->addPragma($text, $i);
135 $this->state = self::IN_TEXT;
136 } else {
137 if ($tag !== null) {
138 $i++;
139 }
140 $this->state = self::IN_TAG;
141 }
142 $this->seenTag = $i;
143 break;
144
145 default:
146 if ($this->tagChange($this->ctag, $text, $i)) {
147 $this->tokens[] = array(
148 self::TYPE => $this->tagType,
149 self::NAME => trim($this->buffer),
150 self::OTAG => $this->otag,
151 self::CTAG => $this->ctag,
152 self::LINE => $this->line,
153 self::INDEX => ($this->tagType == self::T_END_SECTION) ? $this->seenTag - strlen($this->otag) : $i + strlen($this->ctag)
154 );
155
156 $this->buffer = '';
157 $i += strlen($this->ctag) - 1;
158 $this->state = self::IN_TEXT;
159 if ($this->tagType == self::T_UNESCAPED) {
160 if ($this->ctag == '}}') {
161 $i++;
162 } else {
163 // Clean up `{{{ tripleStache }}}` style tokens.
164 $lastName = $this->tokens[count($this->tokens) - 1][self::NAME];
165 if (substr($lastName, -1) === '}') {
166 $this->tokens[count($this->tokens) - 1][self::NAME] = trim(substr($lastName, 0, -1));
167 }
168 }
169 }
170 } else {
171 $this->buffer .= substr($text, $i, 1);
172 }
173 break;
174 }
175 }
176
177 $this->flushBuffer();
178
179 return $this->tokens;
180 }
181
182 /**
183 * Helper function to reset tokenizer internal state.
184 */
185 private function reset()
186 {
187 $this->state = self::IN_TEXT;
188 $this->tagType = null;
189 $this->tag = null;
190 $this->buffer = '';
191 $this->tokens = array();
192 $this->seenTag = false;
193 $this->line = 0;
194 $this->otag = '{{';
195 $this->ctag = '}}';
196 }
197
198 /**
199 * Flush the current buffer to a token.
200 */
201 private function flushBuffer()
202 {
203 if (!empty($this->buffer)) {
204 $this->tokens[] = array(
205 self::TYPE => self::T_TEXT,
206 self::LINE => $this->line,
207 self::VALUE => $this->buffer
208 );
209 $this->buffer = '';
210 }
211 }
212
213 /**
214 * Change the current Mustache delimiters. Set new `otag` and `ctag` values.
215 *
216 * @param string $text Mustache template source
217 * @param int $index Current tokenizer index
218 *
219 * @return int New index value
220 */
221 private function changeDelimiters($text, $index)
222 {
223 $startIndex = strpos($text, '=', $index) + 1;
224 $close = '='.$this->ctag;
225 $closeIndex = strpos($text, $close, $index);
226
227 list($otag, $ctag) = explode(' ', trim(substr($text, $startIndex, $closeIndex - $startIndex)));
228 $this->otag = $otag;
229 $this->ctag = $ctag;
230
231 $this->tokens[] = array(
232 self::TYPE => self::T_DELIM_CHANGE,
233 self::LINE => $this->line,
234 );
235
236 return $closeIndex + strlen($close) - 1;
237 }
238
239 /**
240 * Add pragma token.
241 *
242 * Pragmas are hoisted to the front of the template, so all pragma tokens
243 * will appear at the front of the token list.
244 *
245 * @param string $text
246 * @param int $index
247 *
248 * @return int New index value
249 */
250 private function addPragma($text, $index)
251 {
252 $end = strpos($text, $this->ctag, $index);
253 $pragma = trim(substr($text, $index + 2, $end - $index - 2));
254
255 // Pragmas are hoisted to the front of the template.
256 array_unshift($this->tokens, array(
257 self::TYPE => self::T_PRAGMA,
258 self::NAME => $pragma,
259 self::LINE => 0,
260 ));
261
262 return $end + strlen($this->ctag) - 1;
263 }
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 }
279