1 
2 /*
3   +------------------------------------------------------------------------+
4   | Phalcon Framework													   |
5   +------------------------------------------------------------------------+
6   | Copyright (c) 2011-present Phalcon Team (http://www.phalconphp.com)    |
7   +------------------------------------------------------------------------+
8   | This source file is subject to the New BSD License that is bundled	   |
9   | with this package in the file docs/LICENSE.txt.					       |
10   |																	       |
11   | If you did not receive a copy of the license and are unable to		   |
12   | obtain it through the world-wide-web, please send an email			   |
13   | to license@phalconphp.com so we can send you a copy immediately.	   |
14   +------------------------------------------------------------------------+
15   | Authors: Andres Gutierrez <andres@phalconphp.com>					   |
16   |		  Eduar Carvajal <eduar@phalconphp.com>						       |
17   +------------------------------------------------------------------------+
18 */
19 
20 const phvolt_token_names phvolt_tokens[] =
21 {
22   { SL("INTEGER"),		PHVOLT_T_INTEGER },
23   { SL("DOUBLE"),		 PHVOLT_T_DOUBLE },
24   { SL("STRING"),		 PHVOLT_T_STRING },
25   { SL("IDENTIFIER"),	 PHVOLT_T_IDENTIFIER },
26   { SL("MINUS"),		  PHVOLT_T_MINUS },
27   { SL("+"),			  PHVOLT_T_ADD },
28   { SL("-"),			  PHVOLT_T_SUB },
29   { SL("*"),			  PHVOLT_T_MUL },
30   { SL("/"),			  PHVOLT_T_DIV },
31   { SL("%%"),			 PHVOLT_T_MOD },
32   { SL("!"),			  PHVOLT_T_NOT },
33   { SL("~"),			  PHVOLT_T_CONCAT },
34   { SL("AND"),			PHVOLT_T_AND },
35   { SL("OR"),			 PHVOLT_T_OR },
36   { SL("DOT"),			PHVOLT_T_DOT },
37   { SL("COMMA"),		  PHVOLT_T_COMMA },
38   { SL("EQUALS"),		 PHVOLT_T_EQUALS },
39   { SL("NOT EQUALS"),	 PHVOLT_T_NOTEQUALS },
40   { SL("IDENTICAL"),	  PHVOLT_T_IDENTICAL },
41   { SL("NOT IDENTICAL"),  PHVOLT_T_NOTIDENTICAL },
42   { SL("NOT"),			PHVOLT_T_NOT },
43   { SL("RANGE"),		  PHVOLT_T_RANGE },
44   { SL("COLON"),		  PHVOLT_T_COLON },
45   { SL("QUESTION MARK"),  PHVOLT_T_QUESTION },
46   { SL("<"),			  PHVOLT_T_LESS },
47   { SL("<="),			 PHVOLT_T_LESSEQUAL },
48   { SL(">"),			  PHVOLT_T_GREATER },
49   { SL(">="),			 PHVOLT_T_GREATEREQUAL },
50   { SL("("),			  PHVOLT_T_PARENTHESES_OPEN },
51   { SL(")"),			  PHVOLT_T_PARENTHESES_CLOSE },
52   { SL("["),			  PHVOLT_T_SBRACKET_OPEN },
53   { SL("]"),			  PHVOLT_T_SBRACKET_CLOSE },
54   { SL("{"),			  PHVOLT_T_CBRACKET_OPEN },
55   { SL("}"),			  PHVOLT_T_CBRACKET_CLOSE },
56   { SL("{%"),			 PHVOLT_T_OPEN_DELIMITER },
57   { SL("%}"),			 PHVOLT_T_CLOSE_DELIMITER },
58   { SL("{{"),			 PHVOLT_T_OPEN_EDELIMITER },
59   { SL("}}"),			 PHVOLT_T_CLOSE_EDELIMITER },
60   { SL("IF"),			 PHVOLT_T_IF },
61   { SL("ELSE"),		   PHVOLT_T_ELSE },
62   { SL("ELSEIF"),		 PHVOLT_T_ELSEIF },
63   { SL("ELSEFOR"),		PHVOLT_T_ELSEFOR },
64   { SL("ENDIF"),		  PHVOLT_T_ENDIF },
65   { SL("FOR"),			PHVOLT_T_FOR },
66   { SL("SWITCH"),		PHVOLT_T_SWITCH },
67   { SL("CASE"),			PHVOLT_T_CASE },
68   { SL("DEFAULT"),		PHVOLT_T_DEFAULT },
69   { SL("ENDSWITCH"),	PHVOLT_T_ENDSWITCH },
70   { SL("IN"),			 PHVOLT_T_IN },
71   { SL("ENDFOR"),		 PHVOLT_T_ENDFOR },
72   { SL("SET"),			PHVOLT_T_SET },
73   { SL("ASSIGN"),		 PHVOLT_T_ASSIGN },
74   { SL("+="),			 PHVOLT_T_ADD_ASSIGN },
75   { SL("-="),			 PHVOLT_T_SUB_ASSIGN },
76   { SL("*="),			 PHVOLT_T_MUL_ASSIGN },
77   { SL("/="),			 PHVOLT_T_DIV_ASSIGN },
78   { SL("++"),			 PHVOLT_T_INCR },
79   { SL("--"),			 PHVOLT_T_DECR },
80   { SL("BLOCK"),		  PHVOLT_T_BLOCK },
81   { SL("ENDBLOCK"),	   PHVOLT_T_ENDBLOCK },
82   { SL("CACHE"),		  PHVOLT_T_CACHE },
83   { SL("ENDCACHE"),	   PHVOLT_T_ENDCACHE },
84   { SL("EXTENDS"),		PHVOLT_T_EXTENDS },
85   { SL("IS"),			 PHVOLT_T_IS },
86   { SL("DEFINED"),		PHVOLT_T_DEFINED },
87   { SL("EMPTY"),		  PHVOLT_T_EMPTY },
88   { SL("EVEN"),		   PHVOLT_T_EVEN },
89   { SL("ODD"),			PHVOLT_T_ODD },
90   { SL("NUMERIC"),		PHVOLT_T_NUMERIC },
91   { SL("SCALAR"),		 PHVOLT_T_SCALAR },
92   { SL("ITERABLE"),	   PHVOLT_T_ITERABLE },
93   { SL("INCLUDE"),		PHVOLT_T_INCLUDE },
94   { SL("DO"),			 PHVOLT_T_DO },
95   { SL("WHITESPACE"),	 PHVOLT_T_IGNORE },
96   { SL("AUTOESCAPE"),	 PHVOLT_T_AUTOESCAPE },
97   { SL("ENDAUTOESCAPE"),  PHVOLT_T_ENDAUTOESCAPE },
98   { SL("CONTINUE"),	   PHVOLT_T_CONTINUE },
99   { SL("BREAK"),		  PHVOLT_T_BREAK },
100   { SL("WITH"),		   PHVOLT_T_WITH },
101   { SL("RETURN"),		 PHVOLT_T_RETURN },
102   { SL("MACRO"),		  PHVOLT_T_MACRO },
103   { SL("ENDMACRO"),	   PHVOLT_T_ENDMACRO },
104   { SL("CALL"),		   PHVOLT_T_CALL },
105   { SL("WITH"),		   PHVOLT_T_WITH },
106   { NULL, 0, 0 }
107 };
108 
109 /**
110  * Wrapper to alloc memory within the parser
111  */
phvolt_wrapper_alloc(size_t bytes)112 static void *phvolt_wrapper_alloc(size_t bytes){
113 	return emalloc(bytes);
114 }
115 
116 /**
117  * Wrapper to free memory within the parser
118  */
phvolt_wrapper_free(void * pointer)119 static void phvolt_wrapper_free(void *pointer){
120 	efree(pointer);
121 }
122 
123 /**
124  * Creates a parser_token to be passed to the parser
125  */
phvolt_parse_with_token(void * phvolt_parser,int opcode,int parsercode,phvolt_scanner_token * token,phvolt_parser_status * parser_status)126 static void phvolt_parse_with_token(void* phvolt_parser, int opcode, int parsercode, phvolt_scanner_token *token, phvolt_parser_status *parser_status){
127 
128 	phvolt_parser_token *pToken;
129 
130 	pToken = emalloc(sizeof(phvolt_parser_token));
131 	pToken->opcode = opcode;
132 	pToken->token = token->value;
133 	pToken->token_len = token->len;
134 	pToken->free_flag = 1;
135 
136 	phvolt_(phvolt_parser, parsercode, pToken, parser_status);
137 
138 	token->value = NULL;
139 	token->len = 0;
140 }
141 
142 /**
143  * Creates an error message
144  */
phvolt_create_error_msg(phvolt_parser_status * parser_status,char * message)145 static void phvolt_create_error_msg(phvolt_parser_status *parser_status, char *message){
146 
147 	unsigned int length = (128 + Z_STRLEN_P(parser_status->scanner_state->active_file));
148 	char *str = emalloc(sizeof(char) * length);
149 
150 	snprintf(str, length, "%s in %s on line %d", message, Z_STRVAL_P(parser_status->scanner_state->active_file), parser_status->scanner_state->active_line);
151 	str[length - 1] = '\0';
152 
153 	parser_status->syntax_error = estrndup(str, strlen(str));
154 	efree(str);
155 }
156 
157 /**
158  * Creates an error message when it's triggered by the scanner
159  */
phvolt_scanner_error_msg(phvolt_parser_status * parser_status,zval ** error_msg TSRMLS_DC)160 static void phvolt_scanner_error_msg(phvolt_parser_status *parser_status, zval **error_msg TSRMLS_DC){
161 
162 	char *error, *error_part;
163 	int length;
164 	phvolt_scanner_state *state = parser_status->scanner_state;
165 
166 #if PHP_VERSION_ID < 70000
167 	MAKE_STD_ZVAL(*error_msg);
168 #else
169 	ZVAL_NULL(*error_msg);
170 #endif
171 
172 	if (state->start) {
173 		error = emalloc(sizeof(char) * 72 + state->start_length +  Z_STRLEN_P(state->active_file));
174 		if (state->start_length > 16) {
175 			length = 72 + Z_STRLEN_P(state->active_file);
176 			error_part = estrndup(state->start, 16);
177 			snprintf(error, length, "Scanning error before '%s...' in %s on line %d", error_part, Z_STRVAL_P(state->active_file), state->active_line);
178 			error[length - 1] = '\0';
179 			efree(error_part);
180 		} else {
181 			length = 48 + state->start_length + Z_STRLEN_P(state->active_file);
182 			snprintf(error, length, "Scanning error before '%s' in %s on line %d", state->start, Z_STRVAL_P(state->active_file), state->active_line);
183 		}
184 	} else {
185 		error = emalloc(sizeof(char) * (32 + Z_STRLEN_P(state->active_file)));
186 		length = 32 + Z_STRLEN_P(state->active_file);
187 		snprintf(error, length, "Scanning error near to EOF in %s", Z_STRVAL_P(state->active_file));
188 	}
189 
190 	error[length - 1] = '\0';
191 #if PHP_VERSION_ID < 70000
192 	ZVAL_STRING(*error_msg, error, 1);
193 #else
194 	ZVAL_STRING(*error_msg, error);
195 #endif
196 
197 	efree(error);
198 }
199 
200 /**
201  * Receives the volt code tokenizes and parses it
202  */
phvolt_parse_view(zval * result,zval * view_code,zval * template_path TSRMLS_DC)203 int phvolt_parse_view(zval *result, zval *view_code, zval *template_path TSRMLS_DC){
204 
205 #if PHP_VERSION_ID < 70000
206 	zval *error_msg = NULL;
207 #else
208 	zval em, *error_msg = &em;
209 #endif
210 
211 	ZVAL_NULL(result);
212 
213 #if PHP_VERSION_ID >= 70000
214 	ZVAL_NULL(error_msg);
215 #endif
216 
217 	if (Z_TYPE_P(view_code) != IS_STRING) {
218 		ZEPHIR_THROW_EXCEPTION_STRW(phalcon_mvc_view_exception_ce, "View code must be a string");
219 		return FAILURE;
220 	}
221 
222 	if (phvolt_internal_parse_view(&result, view_code, template_path, &error_msg TSRMLS_CC) == FAILURE) {
223 		ZEPHIR_THROW_EXCEPTION_STRW(phalcon_mvc_view_exception_ce, Z_STRVAL_P(error_msg));
224 #if PHP_VERSION_ID < 70000
225 		zval_ptr_dtor(&error_msg);
226 #else
227 		zval_dtor(error_msg);
228 #endif
229 		return FAILURE;
230 	}
231 
232 	return SUCCESS;
233 }
234 
235 /**
236  * Checks whether the token has only blank characters
237  */
phvolt_is_blank_string(phvolt_scanner_token * token)238 int phvolt_is_blank_string(phvolt_scanner_token *token){
239 
240 	char *marker = token->value;
241 	unsigned int ch, i;
242 
243 	for (i = 0; i < token->len; i++) {
244 		ch = *marker;
245 		if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != 11) {
246 			return 0;
247 		}
248 		marker++;
249 	}
250 
251 	return 1;
252 }
253 
254 /**
255  * Parses a volt template returning an intermediate array representation
256  */
phvolt_internal_parse_view(zval ** result,zval * view_code,zval * template_path,zval ** error_msg TSRMLS_DC)257 int phvolt_internal_parse_view(zval **result, zval *view_code, zval *template_path, zval **error_msg TSRMLS_DC) {
258 
259 	char *error;
260 	phvolt_scanner_state *state;
261 	phvolt_scanner_token token;
262 	int scanner_status, status = SUCCESS;
263 	phvolt_parser_status *parser_status = NULL;
264 	void* phvolt_parser;
265 
266 	/** Check if the view has code */
267 	if (!Z_STRVAL_P(view_code)) {
268 #if PHP_VERSION_ID < 70000
269 		MAKE_STD_ZVAL(*error_msg);
270 		ZVAL_STRING(*error_msg, "View code cannot be null", 1);
271 #else
272 		ZVAL_STRING(*error_msg, "View code cannot be null");
273 #endif
274 		return FAILURE;
275 	}
276 
277 	if (!Z_STRLEN_P(view_code)) {
278 		array_init(*result);
279 		return SUCCESS;
280 	}
281 
282 	/** Start the reentrant parser */
283 	phvolt_parser = phvolt_Alloc(phvolt_wrapper_alloc);
284 	if (unlikely(!phvolt_parser)) {
285 #if PHP_VERSION_ID < 70000
286 		MAKE_STD_ZVAL(*error_msg);
287 		ZVAL_STRING(*error_msg, "Memory allocation error", 1);
288 #else
289 		ZVAL_STRING(*error_msg, "Memory allocation error");
290 #endif
291 		return FAILURE;
292 	}
293 
294 	parser_status = emalloc(sizeof(phvolt_parser_status));
295 	state = emalloc(sizeof(phvolt_scanner_state));
296 
297 	parser_status->status = PHVOLT_PARSING_OK;
298 	parser_status->scanner_state = state;
299 #if PHP_VERSION_ID < 70000
300 	parser_status->ret = NULL;
301 #else
302 	ZVAL_UNDEF(&parser_status->ret);
303 #endif
304 	parser_status->token = &token;
305 	parser_status->syntax_error = NULL;
306 
307 	/** Initialize the scanner state */
308 	state->active_token = 0;
309 	state->start = Z_STRVAL_P(view_code);
310 	state->mode = PHVOLT_MODE_RAW;
311 	state->raw_buffer = emalloc(sizeof(char) * PHVOLT_RAW_BUFFER_SIZE);
312 	state->raw_buffer_size = PHVOLT_RAW_BUFFER_SIZE;
313 	state->raw_buffer_cursor = 0;
314 	state->active_file = template_path;
315 	state->active_line = 1;
316 	state->statement_position = 0;
317 	state->extends_mode = 0;
318 	state->block_level = 0;
319 	state->macro_level = 0;
320 	state->start_length = 0;
321 	state->old_if_level = 0;
322 	state->if_level = 0;
323 	state->for_level = 0;
324 	state->switch_level = 0;
325 	state->whitespace_control = 0;
326 	state->forced_raw_state = 0;
327 
328 	state->end = state->start;
329 
330 	token.value = NULL;
331 	token.len = 0;
332 
333 	while (0 <= (scanner_status = phvolt_get_token(state, &token))) {
334 
335 		state->active_token = token.opcode;
336 
337 		state->start_length = (Z_STRVAL_P(view_code) + Z_STRLEN_P(view_code) - state->start);
338 
339 		switch (token.opcode) {
340 
341 			case PHVOLT_T_IGNORE:
342 				break;
343 
344 			case PHVOLT_T_ADD:
345 				phvolt_(phvolt_parser, PHVOLT_PLUS, NULL, parser_status);
346 				break;
347 			case PHVOLT_T_SUB:
348 				phvolt_(phvolt_parser, PHVOLT_MINUS, NULL, parser_status);
349 				break;
350 			case PHVOLT_T_MUL:
351 				phvolt_(phvolt_parser, PHVOLT_TIMES, NULL, parser_status);
352 				break;
353 			case PHVOLT_T_DIV:
354 				phvolt_(phvolt_parser, PHVOLT_DIVIDE, NULL, parser_status);
355 				break;
356 			case PHVOLT_T_MOD:
357 				phvolt_(phvolt_parser, PHVOLT_MOD, NULL, parser_status);
358 				break;
359 			case PHVOLT_T_AND:
360 				phvolt_(phvolt_parser, PHVOLT_AND, NULL, parser_status);
361 				break;
362 			case PHVOLT_T_OR:
363 				phvolt_(phvolt_parser, PHVOLT_OR, NULL, parser_status);
364 				break;
365 			case PHVOLT_T_IS:
366 				phvolt_(phvolt_parser, PHVOLT_IS, NULL, parser_status);
367 				break;
368 			case PHVOLT_T_EQUALS:
369 				phvolt_(phvolt_parser, PHVOLT_EQUALS, NULL, parser_status);
370 				break;
371 			case PHVOLT_T_NOTEQUALS:
372 				phvolt_(phvolt_parser, PHVOLT_NOTEQUALS, NULL, parser_status);
373 				break;
374 			case PHVOLT_T_LESS:
375 				phvolt_(phvolt_parser, PHVOLT_LESS, NULL, parser_status);
376 				break;
377 			case PHVOLT_T_GREATER:
378 				phvolt_(phvolt_parser, PHVOLT_GREATER, NULL, parser_status);
379 				break;
380 			case PHVOLT_T_GREATEREQUAL:
381 				phvolt_(phvolt_parser, PHVOLT_GREATEREQUAL, NULL, parser_status);
382 				break;
383 			case PHVOLT_T_LESSEQUAL:
384 				phvolt_(phvolt_parser, PHVOLT_LESSEQUAL, NULL, parser_status);
385 				break;
386 			case PHVOLT_T_IDENTICAL:
387 				phvolt_(phvolt_parser, PHVOLT_IDENTICAL, NULL, parser_status);
388 				break;
389 			case PHVOLT_T_NOTIDENTICAL:
390 				phvolt_(phvolt_parser, PHVOLT_NOTIDENTICAL, NULL, parser_status);
391 				break;
392 			case PHVOLT_T_NOT:
393 				phvolt_(phvolt_parser, PHVOLT_NOT, NULL, parser_status);
394 				break;
395 			case PHVOLT_T_DOT:
396 				phvolt_(phvolt_parser, PHVOLT_DOT, NULL, parser_status);
397 				break;
398 			case PHVOLT_T_CONCAT:
399 				phvolt_(phvolt_parser, PHVOLT_CONCAT, NULL, parser_status);
400 				break;
401 			case PHVOLT_T_RANGE:
402 				phvolt_(phvolt_parser, PHVOLT_RANGE, NULL, parser_status);
403 				break;
404 			case PHVOLT_T_PIPE:
405 				phvolt_(phvolt_parser, PHVOLT_PIPE, NULL, parser_status);
406 				break;
407 			case PHVOLT_T_COMMA:
408 				phvolt_(phvolt_parser, PHVOLT_COMMA, NULL, parser_status);
409 				break;
410 			case PHVOLT_T_COLON:
411 				phvolt_(phvolt_parser, PHVOLT_COLON, NULL, parser_status);
412 				break;
413 			case PHVOLT_T_QUESTION:
414 				phvolt_(phvolt_parser, PHVOLT_QUESTION, NULL, parser_status);
415 				break;
416 
417 			case PHVOLT_T_PARENTHESES_OPEN:
418 				phvolt_(phvolt_parser, PHVOLT_PARENTHESES_OPEN, NULL, parser_status);
419 				break;
420 			case PHVOLT_T_PARENTHESES_CLOSE:
421 				phvolt_(phvolt_parser, PHVOLT_PARENTHESES_CLOSE, NULL, parser_status);
422 				break;
423 			case PHVOLT_T_SBRACKET_OPEN:
424 				phvolt_(phvolt_parser, PHVOLT_SBRACKET_OPEN, NULL, parser_status);
425 				break;
426 			case PHVOLT_T_SBRACKET_CLOSE:
427 				phvolt_(phvolt_parser, PHVOLT_SBRACKET_CLOSE, NULL, parser_status);
428 				break;
429 			case PHVOLT_T_CBRACKET_OPEN:
430 				phvolt_(phvolt_parser, PHVOLT_CBRACKET_OPEN, NULL, parser_status);
431 				break;
432 			case PHVOLT_T_CBRACKET_CLOSE:
433 				phvolt_(phvolt_parser, PHVOLT_CBRACKET_CLOSE, NULL, parser_status);
434 				break;
435 
436 			case PHVOLT_T_OPEN_DELIMITER:
437 				phvolt_(phvolt_parser, PHVOLT_OPEN_DELIMITER, NULL, parser_status);
438 				break;
439 			case PHVOLT_T_CLOSE_DELIMITER:
440 				phvolt_(phvolt_parser, PHVOLT_CLOSE_DELIMITER, NULL, parser_status);
441 				break;
442 
443 			case PHVOLT_T_OPEN_EDELIMITER:
444 				if (state->extends_mode == 1 && state->block_level == 0) {
445 					phvolt_create_error_msg(parser_status, "Child templates only may contain blocks");
446 					parser_status->status = PHVOLT_PARSING_FAILED;
447 					break;
448 				}
449 				phvolt_(phvolt_parser, PHVOLT_OPEN_EDELIMITER, NULL, parser_status);
450 				break;
451 			case PHVOLT_T_CLOSE_EDELIMITER:
452 				phvolt_(phvolt_parser, PHVOLT_CLOSE_EDELIMITER, NULL, parser_status);
453 				break;
454 
455 			case PHVOLT_T_NULL:
456 				phvolt_(phvolt_parser, PHVOLT_NULL, NULL, parser_status);
457 				break;
458 			case PHVOLT_T_TRUE:
459 				phvolt_(phvolt_parser, PHVOLT_TRUE, NULL, parser_status);
460 				break;
461 			case PHVOLT_T_FALSE:
462 				phvolt_(phvolt_parser, PHVOLT_FALSE, NULL, parser_status);
463 				break;
464 
465 			case PHVOLT_T_INTEGER:
466 				phvolt_parse_with_token(phvolt_parser, PHVOLT_T_INTEGER, PHVOLT_INTEGER, &token, parser_status);
467 				break;
468 			case PHVOLT_T_DOUBLE:
469 				phvolt_parse_with_token(phvolt_parser, PHVOLT_T_DOUBLE, PHVOLT_DOUBLE, &token, parser_status);
470 				break;
471 			case PHVOLT_T_STRING:
472 				phvolt_parse_with_token(phvolt_parser, PHVOLT_T_STRING, PHVOLT_STRING, &token, parser_status);
473 				break;
474 			case PHVOLT_T_IDENTIFIER:
475 				phvolt_parse_with_token(phvolt_parser, PHVOLT_T_IDENTIFIER, PHVOLT_IDENTIFIER, &token, parser_status);
476 				break;
477 
478 			case PHVOLT_T_IF:
479 				if (state->extends_mode == 1 && state->block_level == 0){
480 					phvolt_create_error_msg(parser_status, "Child templates only may contain blocks");
481 					parser_status->status = PHVOLT_PARSING_FAILED;
482 					break;
483 				} else {
484 					state->if_level++;
485 					state->block_level++;
486 				}
487 				phvolt_(phvolt_parser, PHVOLT_IF, NULL, parser_status);
488 				break;
489 
490 			case PHVOLT_T_ELSE:
491 				if (state->if_level == 0 && state->for_level > 0) {
492 					phvolt_(phvolt_parser, PHVOLT_ELSEFOR, NULL, parser_status);
493 				} else {
494 					phvolt_(phvolt_parser, PHVOLT_ELSE, NULL, parser_status);
495 				}
496 				break;
497 
498 			case PHVOLT_T_ELSEFOR:
499 				phvolt_(phvolt_parser, PHVOLT_ELSEFOR, NULL, parser_status);
500 				break;
501 
502 			case PHVOLT_T_ELSEIF:
503 				if (state->if_level == 0) {
504 					phvolt_create_error_msg(parser_status, "Unexpected ENDIF");
505 					parser_status->status = PHVOLT_PARSING_FAILED;
506 					break;
507 				}
508 				phvolt_(phvolt_parser, PHVOLT_ELSEIF, NULL, parser_status);
509 				break;
510 
511 			case PHVOLT_T_ENDIF:
512 				state->block_level--;
513 				state->if_level--;
514 				phvolt_(phvolt_parser, PHVOLT_ENDIF, NULL, parser_status);
515 				break;
516 
517 			case PHVOLT_T_FOR:
518 				if (state->extends_mode == 1 && state->block_level == 0){
519 					phvolt_create_error_msg(parser_status, "Child templates only may contain blocks");
520 					parser_status->status = PHVOLT_PARSING_FAILED;
521 					break;
522 				} else {
523 					state->old_if_level = state->if_level;
524 					state->if_level = 0;
525 					state->for_level++;
526 					state->block_level++;
527 				}
528 				phvolt_(phvolt_parser, PHVOLT_FOR, NULL, parser_status);
529 				break;
530 
531 			case PHVOLT_T_IN:
532 				phvolt_(phvolt_parser, PHVOLT_IN, NULL, parser_status);
533 				break;
534 
535 			case PHVOLT_T_ENDFOR:
536 				state->block_level--;
537 				state->for_level--;
538 				state->if_level = state->old_if_level;
539 				phvolt_(phvolt_parser, PHVOLT_ENDFOR, NULL, parser_status);
540 				break;
541 
542 			case PHVOLT_T_SWITCH:
543 				if (state->extends_mode == 1 && state->block_level == 0){
544 					phvolt_create_error_msg(parser_status, "Child templates only may contain blocks");
545 					parser_status->status = PHVOLT_PARSING_FAILED;
546 					break;
547 				} else if (state->switch_level > 0) {
548 					phvolt_create_error_msg(parser_status, "A nested switch detected. There is no nested switch-case statements support");
549 					parser_status->status = PHVOLT_PARSING_FAILED;
550 					break;
551 				}  else {
552 					state->switch_level = 1;
553 					state->block_level++;
554 				}
555 				phvolt_(phvolt_parser, PHVOLT_SWITCH, NULL, parser_status);
556 				break;
557 
558 			case PHVOLT_T_CASE:
559 				if (state->switch_level == 0) {
560 					phvolt_create_error_msg(parser_status, "Unexpected CASE");
561 					parser_status->status = PHVOLT_PARSING_FAILED;
562 					break;
563 				}
564 				phvolt_(phvolt_parser, PHVOLT_CASE, NULL, parser_status);
565 				break;
566 
567 			/* only for switch-case statements */
568 			case PHVOLT_T_DEFAULT:
569 				if (state->switch_level != 0) {
570 					phvolt_(phvolt_parser, PHVOLT_DEFAULT, NULL, parser_status);
571 				} else {
572 					// TODO: Make this better.
573 					// Issue: https://github.com/phalcon/cphalcon/issues/13242
574 					// Introduced: https://github.com/phalcon/cphalcon/pull/13130
575 					phvolt_parse_with_token(phvolt_parser, PHVOLT_T_IDENTIFIER, PHVOLT_IDENTIFIER, &token, parser_status);
576 				}
577 
578 				break;
579 
580 			case PHVOLT_T_ENDSWITCH:
581 				if (state->switch_level == 0) {
582 					phvolt_create_error_msg(parser_status, "Unexpected ENDSWITCH");
583 					parser_status->status = PHVOLT_PARSING_FAILED;
584 					break;
585 				} else {
586 					state->switch_level = 0;
587 					state->block_level--;
588 				}
589 
590 				phvolt_(phvolt_parser, PHVOLT_ENDSWITCH, NULL, parser_status);
591 				break;
592 
593 			case PHVOLT_T_RAW_FRAGMENT:
594 				if (token.len > 0) {
595 					if (state->extends_mode == 1 && state->block_level == 0){
596 						if (!phvolt_is_blank_string(&token)) {
597 							phvolt_create_error_msg(parser_status, "Child templates only may contain blocks");
598 							parser_status->status = PHVOLT_PARSING_FAILED;
599 						}
600 						efree(token.value);
601 						break;
602 					} else {
603 						if (!phvolt_is_blank_string(&token)) {
604 							state->statement_position++;
605 						}
606 					}
607 					phvolt_parse_with_token(phvolt_parser, PHVOLT_T_RAW_FRAGMENT, PHVOLT_RAW_FRAGMENT, &token, parser_status);
608 				} else {
609 					efree(token.value);
610 				}
611 				break;
612 
613 			case PHVOLT_T_SET:
614 				if (state->extends_mode == 1 && state->block_level == 0){
615 					phvolt_create_error_msg(parser_status, "Child templates only may contain blocks");
616 					parser_status->status = PHVOLT_PARSING_FAILED;
617 					break;
618 				}
619 				phvolt_(phvolt_parser, PHVOLT_SET, NULL, parser_status);
620 				break;
621 			case PHVOLT_T_ASSIGN:
622 				phvolt_(phvolt_parser, PHVOLT_ASSIGN, NULL, parser_status);
623 				break;
624 			case PHVOLT_T_ADD_ASSIGN:
625 				phvolt_(phvolt_parser, PHVOLT_ADD_ASSIGN, NULL, parser_status);
626 				break;
627 			case PHVOLT_T_SUB_ASSIGN:
628 				phvolt_(phvolt_parser, PHVOLT_SUB_ASSIGN, NULL, parser_status);
629 				break;
630 			case PHVOLT_T_MUL_ASSIGN:
631 				phvolt_(phvolt_parser, PHVOLT_MUL_ASSIGN, NULL, parser_status);
632 				break;
633 			case PHVOLT_T_DIV_ASSIGN:
634 				phvolt_(phvolt_parser, PHVOLT_DIV_ASSIGN, NULL, parser_status);
635 				break;
636 
637 			case PHVOLT_T_INCR:
638 				phvolt_(phvolt_parser, PHVOLT_INCR, NULL, parser_status);
639 				break;
640 			case PHVOLT_T_DECR:
641 				phvolt_(phvolt_parser, PHVOLT_DECR, NULL, parser_status);
642 				break;
643 
644 			case PHVOLT_T_BLOCK:
645 				if (state->block_level > 0) {
646 					phvolt_create_error_msg(parser_status, "Embedding blocks into other blocks is not supported");
647 					parser_status->status = PHVOLT_PARSING_FAILED;
648 					break;
649 				} else {
650 					state->block_level++;
651 				}
652 				phvolt_(phvolt_parser, PHVOLT_BLOCK, NULL, parser_status);
653 				break;
654 			case PHVOLT_T_ENDBLOCK:
655 				state->block_level--;
656 				phvolt_(phvolt_parser, PHVOLT_ENDBLOCK, NULL, parser_status);
657 				break;
658 
659 			case PHVOLT_T_MACRO:
660 				if (state->macro_level > 0) {
661 					phvolt_create_error_msg(parser_status, "Embedding macros into other macros is not allowed");
662 					parser_status->status = PHVOLT_PARSING_FAILED;
663 					break;
664 				} else {
665 					state->macro_level++;
666 				}
667 				phvolt_(phvolt_parser, PHVOLT_MACRO, NULL, parser_status);
668 				break;
669 			case PHVOLT_T_ENDMACRO:
670 				state->macro_level--;
671 				phvolt_(phvolt_parser, PHVOLT_ENDMACRO, NULL, parser_status);
672 				break;
673 
674 			case PHVOLT_T_CALL:
675 				phvolt_(phvolt_parser, PHVOLT_CALL, NULL, parser_status);
676 				break;
677 			case PHVOLT_T_ENDCALL:
678 				phvolt_(phvolt_parser, PHVOLT_ENDCALL, NULL, parser_status);
679 				break;
680 
681 			case PHVOLT_T_CACHE:
682 				phvolt_(phvolt_parser, PHVOLT_CACHE, NULL, parser_status);
683 				break;
684 			case PHVOLT_T_ENDCACHE:
685 				phvolt_(phvolt_parser, PHVOLT_ENDCACHE, NULL, parser_status);
686 				break;
687 
688 			case PHVOLT_T_RAW:
689 				phvolt_(phvolt_parser, PHVOLT_RAW, NULL, parser_status);
690 				state->forced_raw_state++;
691 				break;
692 			case PHVOLT_T_ENDRAW:
693 				phvolt_(phvolt_parser, PHVOLT_ENDRAW, NULL, parser_status);
694 				state->forced_raw_state--;
695 				break;
696 
697 			case PHVOLT_T_INCLUDE:
698 				phvolt_(phvolt_parser, PHVOLT_INCLUDE, NULL, parser_status);
699 				break;
700 
701 			case PHVOLT_T_WITH:
702 				phvolt_(phvolt_parser, PHVOLT_WITH, NULL, parser_status);
703 				break;
704 
705 			case PHVOLT_T_DEFINED:
706 				phvolt_(phvolt_parser, PHVOLT_DEFINED, NULL, parser_status);
707 				break;
708 
709 			case PHVOLT_T_EMPTY:
710 				phvolt_(phvolt_parser, PHVOLT_EMPTY, NULL, parser_status);
711 				break;
712 
713 			case PHVOLT_T_EVEN:
714 				phvolt_(phvolt_parser, PHVOLT_EVEN, NULL, parser_status);
715 				break;
716 
717 			case PHVOLT_T_ODD:
718 				phvolt_(phvolt_parser, PHVOLT_ODD, NULL, parser_status);
719 				break;
720 
721 			case PHVOLT_T_NUMERIC:
722 				phvolt_(phvolt_parser, PHVOLT_NUMERIC, NULL, parser_status);
723 				break;
724 
725 			case PHVOLT_T_SCALAR:
726 				phvolt_(phvolt_parser, PHVOLT_SCALAR, NULL, parser_status);
727 				break;
728 
729 			case PHVOLT_T_ITERABLE:
730 				phvolt_(phvolt_parser, PHVOLT_ITERABLE, NULL, parser_status);
731 				break;
732 
733 			case PHVOLT_T_DO:
734 				phvolt_(phvolt_parser, PHVOLT_DO, NULL, parser_status);
735 				break;
736 			case PHVOLT_T_RETURN:
737 				phvolt_(phvolt_parser, PHVOLT_RETURN, NULL, parser_status);
738 				break;
739 
740 			case PHVOLT_T_AUTOESCAPE:
741 				phvolt_(phvolt_parser, PHVOLT_AUTOESCAPE, NULL, parser_status);
742 				break;
743 
744 			case PHVOLT_T_ENDAUTOESCAPE:
745 				phvolt_(phvolt_parser, PHVOLT_ENDAUTOESCAPE, NULL, parser_status);
746 				break;
747 
748 			case PHVOLT_T_BREAK:
749 				phvolt_(phvolt_parser, PHVOLT_BREAK, NULL, parser_status);
750 				break;
751 
752 			case PHVOLT_T_CONTINUE:
753 				phvolt_(phvolt_parser, PHVOLT_CONTINUE, NULL, parser_status);
754 				break;
755 
756 			case PHVOLT_T_EXTENDS:
757 				if (state->statement_position != 1) {
758 					phvolt_create_error_msg(parser_status, "Extends statement must be placed at the first line in the template");
759 					parser_status->status = PHVOLT_PARSING_FAILED;
760 					break;
761 				} else {
762 					state->extends_mode = 1;
763 				}
764 				phvolt_(phvolt_parser, PHVOLT_EXTENDS, NULL, parser_status);
765 				break;
766 
767 			default:
768 				parser_status->status = PHVOLT_PARSING_FAILED;
769 				error = emalloc(sizeof(char) * (48 + Z_STRLEN_P(state->active_file)));
770 				snprintf(error, 48 + Z_STRLEN_P(state->active_file) + state->active_line, "Scanner: unknown opcode %d on in %s line %d", token.opcode, Z_STRVAL_P(state->active_file), state->active_line);
771 #if PHP_VERSION_ID < 70000
772 				if (!*error_msg) {
773 					MAKE_STD_ZVAL(*error_msg);
774 					ZVAL_STRING(*error_msg, error, 1);
775 				}
776 #else
777 				if (Z_TYPE_P(*error_msg) == IS_NULL) {
778 					ZVAL_STRING((*error_msg), error);
779 				}
780 #endif
781 				efree(error);
782 				break;
783 		}
784 
785 		if (parser_status->status != PHVOLT_PARSING_OK) {
786 			status = FAILURE;
787 			break;
788 		}
789 
790 		state->end = state->start;
791 	}
792 
793 	if (status != FAILURE) {
794 		switch (scanner_status) {
795 			case PHVOLT_SCANNER_RETCODE_ERR:
796 			case PHVOLT_SCANNER_RETCODE_IMPOSSIBLE:
797 				if (!*error_msg) {
798 					phvolt_scanner_error_msg(parser_status, error_msg TSRMLS_CC);
799 				} else {
800 					if (Z_TYPE_P(*error_msg) == IS_NULL) {
801 						phvolt_scanner_error_msg(parser_status, error_msg TSRMLS_CC);
802 					}
803 				}
804 				status = FAILURE;
805 				break;
806 			default:
807 				phvolt_(phvolt_parser, 0, NULL, parser_status);
808 		}
809 	}
810 
811 	state->active_token = 0;
812 	state->start = NULL;
813 	efree(state->raw_buffer);
814 
815 	if (parser_status->status != PHVOLT_PARSING_OK) {
816 		status = FAILURE;
817 		if (parser_status->syntax_error) {
818 #if PHP_VERSION_ID < 70000
819 			if (!*error_msg) {
820 				MAKE_STD_ZVAL(*error_msg);
821 				ZVAL_STRING(*error_msg, parser_status->syntax_error, 1);
822 			}
823 #else
824 			ZVAL_STRING(*error_msg, parser_status->syntax_error);
825 #endif
826 			efree(parser_status->syntax_error);
827 		}
828 	}
829 
830 	phvolt_Free(phvolt_parser, phvolt_wrapper_free);
831 
832 	if (status != FAILURE) {
833 		if (parser_status->status == PHVOLT_PARSING_OK) {
834 #if PHP_VERSION_ID < 70000
835 			if (parser_status->ret) {
836 				ZVAL_ZVAL(*result, parser_status->ret, 0, 0);
837 				ZVAL_NULL(parser_status->ret);
838 				zval_ptr_dtor(&parser_status->ret);
839 			} else {
840 				array_init(*result);
841 			}
842 #else
843 			if (Z_TYPE(parser_status->ret) != IS_UNDEF) {
844 				ZVAL_ZVAL(*result, &parser_status->ret, 1, 1);
845 			} else {
846 				array_init(*result);
847 			}
848 #endif
849 		}
850 	}
851 
852 	efree(parser_status);
853 	efree(state);
854 
855 	return status;
856 }
857