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