PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 3.16.6
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v3.16.6
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 / Parser.php

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

318 lines 10.7 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-2017 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 Parser class.
14 *
15 * This class is responsible for turning a set of Mustache tokens into a parse tree.
16 */
17 class Mustache_Parser
18 {
19 private $lineNum;
20 private $lineTokens;
21 private $pragmas;
22 private $defaultPragmas = array();
23
24 private $pragmaFilters;
25 private $pragmaBlocks;
26
27 /**
28 * Process an array of Mustache tokens and convert them into a parse tree.
29 *
30 * @param array $tokens Set of Mustache tokens
31 *
32 * @return array Mustache token parse tree
33 */
34 public function parse(array $tokens = array())
35 {
36 $this->lineNum = -1;
37 $this->lineTokens = 0;
38 $this->pragmas = $this->defaultPragmas;
39
40 $this->pragmaFilters = isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS]);
41 $this->pragmaBlocks = isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS]);
42
43 return $this->buildTree($tokens);
44 }
45
46 /**
47 * Enable pragmas across all templates, regardless of the presence of pragma
48 * tags in the individual templates.
49 *
50 * @internal Users should set global pragmas in Mustache_Engine, not here :)
51 *
52 * @param string[] $pragmas
53 */
54 public function setPragmas(array $pragmas)
55 {
56 $this->pragmas = array();
57 foreach ($pragmas as $pragma) {
58 $this->enablePragma($pragma);
59 }
60 $this->defaultPragmas = $this->pragmas;
61 }
62
63 /**
64 * Helper method for recursively building a parse tree.
65 *
66 * @throws Mustache_Exception_SyntaxException when nesting errors or mismatched section tags are encountered
67 *
68 * @param array &$tokens Set of Mustache tokens
69 * @param array $parent Parent token (default: null)
70 *
71 * @return array Mustache Token parse tree
72 */
73 private function buildTree(array &$tokens, ?array $parent = null)
74 {
75 $nodes = array();
76
77 while (!empty($tokens)) {
78 $token = array_shift($tokens);
79
80 if ($token[Mustache_Tokenizer::LINE] === $this->lineNum) {
81 $this->lineTokens++;
82 } else {
83 $this->lineNum = $token[Mustache_Tokenizer::LINE];
84 $this->lineTokens = 0;
85 }
86
87 if ($this->pragmaFilters && isset($token[Mustache_Tokenizer::NAME])) {
88 list($name, $filters) = $this->getNameAndFilters($token[Mustache_Tokenizer::NAME]);
89 if (!empty($filters)) {
90 $token[Mustache_Tokenizer::NAME] = $name;
91 $token[Mustache_Tokenizer::FILTERS] = $filters;
92 }
93 }
94
95 switch ($token[Mustache_Tokenizer::TYPE]) {
96 case Mustache_Tokenizer::T_DELIM_CHANGE:
97 $this->checkIfTokenIsAllowedInParent($parent, $token);
98 $this->clearStandaloneLines($nodes, $tokens);
99 break;
100
101 case Mustache_Tokenizer::T_SECTION:
102 case Mustache_Tokenizer::T_INVERTED:
103 $this->checkIfTokenIsAllowedInParent($parent, $token);
104 $this->clearStandaloneLines($nodes, $tokens);
105 $nodes[] = $this->buildTree($tokens, $token);
106 break;
107
108 case Mustache_Tokenizer::T_END_SECTION:
109 if (!isset($parent)) {
110 $msg = sprintf(
111 'Unexpected closing tag: /%s on line %d',
112 $token[Mustache_Tokenizer::NAME],
113 $token[Mustache_Tokenizer::LINE]
114 );
115 throw new Mustache_Exception_SyntaxException($msg, $token);
116 }
117
118 if ($token[Mustache_Tokenizer::NAME] !== $parent[Mustache_Tokenizer::NAME]) {
119 $msg = sprintf(
120 'Nesting error: %s (on line %d) vs. %s (on line %d)',
121 $parent[Mustache_Tokenizer::NAME],
122 $parent[Mustache_Tokenizer::LINE],
123 $token[Mustache_Tokenizer::NAME],
124 $token[Mustache_Tokenizer::LINE]
125 );
126 throw new Mustache_Exception_SyntaxException($msg, $token);
127 }
128
129 $this->clearStandaloneLines($nodes, $tokens);
130 $parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX];
131 $parent[Mustache_Tokenizer::NODES] = $nodes;
132
133 return $parent;
134
135 case Mustache_Tokenizer::T_PARTIAL:
136 $this->checkIfTokenIsAllowedInParent($parent, $token);
137 //store the whitespace prefix for laters!
138 if ($indent = $this->clearStandaloneLines($nodes, $tokens)) {
139 $token[Mustache_Tokenizer::INDENT] = $indent[Mustache_Tokenizer::VALUE];
140 }
141 $nodes[] = $token;
142 break;
143
144 case Mustache_Tokenizer::T_PARENT:
145 $this->checkIfTokenIsAllowedInParent($parent, $token);
146 $nodes[] = $this->buildTree($tokens, $token);
147 break;
148
149 case Mustache_Tokenizer::T_BLOCK_VAR:
150 if ($this->pragmaBlocks) {
151 // BLOCKS pragma is enabled, let's do this!
152 if (isset($parent) && $parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) {
153 $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG;
154 }
155 $this->clearStandaloneLines($nodes, $tokens);
156 $nodes[] = $this->buildTree($tokens, $token);
157 } else {
158 // pretend this was just a normal "escaped" token...
159 $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_ESCAPED;
160 // TODO: figure out how to figure out if there was a space after this dollar:
161 $token[Mustache_Tokenizer::NAME] = '$' . $token[Mustache_Tokenizer::NAME];
162 $nodes[] = $token;
163 }
164 break;
165
166 case Mustache_Tokenizer::T_PRAGMA:
167 $this->enablePragma($token[Mustache_Tokenizer::NAME]);
168 // no break
169
170 case Mustache_Tokenizer::T_COMMENT:
171 $this->clearStandaloneLines($nodes, $tokens);
172 $nodes[] = $token;
173 break;
174
175 default:
176 $nodes[] = $token;
177 break;
178 }
179 }
180
181 if (isset($parent)) {
182 $msg = sprintf(
183 'Missing closing tag: %s opened on line %d',
184 $parent[Mustache_Tokenizer::NAME],
185 $parent[Mustache_Tokenizer::LINE]
186 );
187 throw new Mustache_Exception_SyntaxException($msg, $parent);
188 }
189
190 return $nodes;
191 }
192
193 /**
194 * Clear standalone line tokens.
195 *
196 * Returns a whitespace token for indenting partials, if applicable.
197 *
198 * @param array $nodes Parsed nodes
199 * @param array $tokens Tokens to be parsed
200 *
201 * @return array|null Resulting indent token, if any
202 */
203 private function clearStandaloneLines(array &$nodes, array &$tokens)
204 {
205 if ($this->lineTokens > 1) {
206 // this is the third or later node on this line, so it can't be standalone
207 return;
208 }
209
210 $prev = null;
211 if ($this->lineTokens === 1) {
212 // this is the second node on this line, so it can't be standalone
213 // unless the previous node is whitespace.
214 if ($prev = end($nodes)) {
215 if (!$this->tokenIsWhitespace($prev)) {
216 return;
217 }
218 }
219 }
220
221 if ($next = reset($tokens)) {
222 // If we're on a new line, bail.
223 if ($next[Mustache_Tokenizer::LINE] !== $this->lineNum) {
224 return;
225 }
226
227 // If the next token isn't whitespace, bail.
228 if (!$this->tokenIsWhitespace($next)) {
229 return;
230 }
231
232 if (count($tokens) !== 1) {
233 // Unless it's the last token in the template, the next token
234 // must end in newline for this to be standalone.
235 if (substr($next[Mustache_Tokenizer::VALUE], -1) !== "\n") {
236 return;
237 }
238 }
239
240 // Discard the whitespace suffix
241 array_shift($tokens);
242 }
243
244 if ($prev) {
245 // Return the whitespace prefix, if any
246 return array_pop($nodes);
247 }
248 }
249
250 /**
251 * Check whether token is a whitespace token.
252 *
253 * True if token type is T_TEXT and value is all whitespace characters.
254 *
255 * @param array $token
256 *
257 * @return bool True if token is a whitespace token
258 */
259 private function tokenIsWhitespace(array $token)
260 {
261 if ($token[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_TEXT) {
262 return preg_match('/^\s*$/', $token[Mustache_Tokenizer::VALUE]);
263 }
264
265 return false;
266 }
267
268 /**
269 * Check whether a token is allowed inside a parent tag.
270 *
271 * @throws Mustache_Exception_SyntaxException if an invalid token is found inside a parent tag
272 *
273 * @param array|null $parent
274 * @param array $token
275 */
276 private function checkIfTokenIsAllowedInParent($parent, array $token)
277 {
278 if (isset($parent) && $parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) {
279 throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token);
280 }
281 }
282
283 /**
284 * Split a tag name into name and filters.
285 *
286 * @param string $name
287 *
288 * @return array [Tag name, Array of filters]
289 */
290 private function getNameAndFilters($name)
291 {
292 $filters = array_map('trim', explode('|', $name));
293 $name = array_shift($filters);
294
295 return array($name, $filters);
296 }
297
298 /**
299 * Enable a pragma.
300 *
301 * @param string $name
302 */
303 private function enablePragma($name)
304 {
305 $this->pragmas[$name] = true;
306
307 switch ($name) {
308 case Mustache_Engine::PRAGMA_BLOCKS:
309 $this->pragmaBlocks = true;
310 break;
311
312 case Mustache_Engine::PRAGMA_FILTERS:
313 $this->pragmaFilters = true;
314 break;
315 }
316 }
317 }
318