diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c12f5756..a416d5e7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -95,7 +95,7 @@ jobs: run: | phpize - if [ "${{ matrix.ccov}}" = "ON" ]; then + if [ "${{ matrix.ccov }}" = "ON" ]; then ./configure \ --enable-zephir-parser \ --enable-zephir-parser-debug \ diff --git a/parser/base.c b/parser/base.c index 70907ad9..19b87db9 100644 --- a/parser/base.c +++ b/parser/base.c @@ -178,6 +178,9 @@ void xx_parse_program(zval *return_value, char *program, size_t program_length, case XX_T_RETURN: xx_(xx_parser, XX_RETURN, NULL, parser_status); break; + case XX_T_REQUIRE_ONCE: + xx_(xx_parser, XX_REQUIRE_ONCE, NULL, parser_status); + break; case XX_T_REQUIRE: xx_(xx_parser, XX_REQUIRE, NULL, parser_status); break; diff --git a/parser/parser.h b/parser/parser.h index 96f4ee68..1240979d 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -33,6 +33,7 @@ static int is_empty(const char *program) static void parser_add_str(zval *arr, const char *key, const char *val) { zval tmp; zend_string *tmp_str; + if (!strcmp(val, "return_value")) { tmp_str = zend_string_init("_zephir_return_value", strlen("_zephir_return_value"), 0); } else if (!strcmp(val, "this_ptr")) { @@ -40,6 +41,7 @@ static void parser_add_str(zval *arr, const char *key, const char *val) { } else { tmp_str = zend_string_init(val, strlen(val), 0); } + ZVAL_STR(&tmp, tmp_str); zend_hash_str_add(Z_ARRVAL_P(arr), key, strlen(key), &tmp); } @@ -894,6 +896,18 @@ static void xx_ret_return_statement(zval *ret, zval *expr, xx_scanner_state *sta parser_add_int(ret, "char", state->active_char); } +static void xx_ret_require_once_statement(zval *ret, zval *expr, xx_scanner_state *state) +{ + array_init(ret); + + parser_add_str(ret, "type", "require_once"); + parser_add_zval(ret, "expr", expr); + + parser_add_str(ret, "file", state->active_file); + parser_add_int(ret, "line", state->active_line); + parser_add_int(ret, "char", state->active_char); +} + static void xx_ret_require_statement(zval *ret, zval *expr, xx_scanner_state *state) { array_init(ret); @@ -1046,7 +1060,7 @@ static void xx_ret_declare_variable(zval *ret, xx_parser_token *T, zval *expr, x array_init(ret); parser_add_str_free(ret, "variable", T->token); - + efree(T); if (expr) { diff --git a/parser/scanner.h b/parser/scanner.h index 9f1b1222..104e8400 100644 --- a/parser/scanner.h +++ b/parser/scanner.h @@ -97,6 +97,7 @@ #define XX_T_TRY 397 #define XX_T_CATCH 398 #define XX_T_DEPRECATED 399 +#define XX_T_REQUIRE_ONCE 459 /* Operators */ #define XX_T_AT '@' diff --git a/parser/scanner.re b/parser/scanner.re index bc8125f1..88b11a05 100644 --- a/parser/scanner.re +++ b/parser/scanner.re @@ -24,13 +24,11 @@ #define YYMARKER (s->marker) int xx_get_token(xx_scanner_state *s, xx_scanner_token *token) { - char *start = YYCURSOR; int status = XX_SCANNER_RETCODE_IMPOSSIBLE; int is_constant = 0, j; while (XX_SCANNER_RETCODE_IMPOSSIBLE == status) { - /*!re2c re2c:indent:top = 2; re2c:yyfill:enable = 0; @@ -353,6 +351,12 @@ int xx_get_token(xx_scanner_state *s, xx_scanner_token *token) { return 0; } + 'require_once' { + s->active_char += sizeof("require_once")-1; + token->opcode = XX_T_REQUIRE_ONCE; + return 0; + } + 'require' { s->active_char += sizeof("require")-1; token->opcode = XX_T_REQUIRE; diff --git a/parser/zephir.lemon b/parser/zephir.lemon index e6f6d082..5abbddc1 100644 --- a/parser/zephir.lemon +++ b/parser/zephir.lemon @@ -37,8 +37,10 @@ // // The token values assigned to these symbols is determined by the order // in which lemon first sees them. + %left INTERNAL PUBLIC PROTECTED STATIC PRIVATE SCOPED . %left COMMA . +%right REQUIRE_ONCE . %right REQUIRE . %right DOUBLEARROW . %right QUESTION . @@ -980,6 +982,10 @@ xx_statement(R) ::= xx_return_statement(S) . { R = S; } +xx_statement(R) ::= xx_require_once_statement(S) . { + R = S; +} + xx_statement(R) ::= xx_require_statement(S) . { R = S; } @@ -1526,6 +1532,11 @@ xx_return_statement(R) ::= RETURN DOTCOMMA . { xx_ret_return_statement(&R, NULL, status->scanner_state); } +/* require_once statement */ +xx_require_once_statement(R) ::= REQUIRE_ONCE xx_common_expr(E) DOTCOMMA . { + xx_ret_require_once_statement(&R, &E, status->scanner_state); +} + /* require statement */ xx_require_statement(R) ::= REQUIRE xx_common_expr(E) DOTCOMMA . { xx_ret_require_statement(&R, &E, status->scanner_state); @@ -1630,6 +1641,10 @@ xx_common_expr(R) ::= ISSET xx_common_expr(O1) . { xx_ret_expr(&R, "isset", &O1, NULL, NULL, status->scanner_state); } +xx_common_expr(R) ::= REQUIRE_ONCE xx_common_expr(O1) . { + xx_ret_expr(&R, "require_once", &O1, NULL, NULL, status->scanner_state); +} + xx_common_expr(R) ::= REQUIRE xx_common_expr(O1) . { xx_ret_expr(&R, "require", &O1, NULL, NULL, status->scanner_state); } diff --git a/tests/statements/require_once.phpt b/tests/statements/require_once.phpt new file mode 100644 index 00000000..d0a1c959 --- /dev/null +++ b/tests/statements/require_once.phpt @@ -0,0 +1,39 @@ +--TEST-- +require_once statement +--SKIPIF-- + +--FILE-- + +--EXPECT-- +array(5) { + ["type"]=> + string(12) "require_once" + ["expr"]=> + array(5) { + ["type"]=> + string(8) "variable" + ["value"]=> + string(3) "foo" + ["file"]=> + string(11) "(eval code)" + ["line"]=> + int(2) + ["char"]=> + int(18) + } + ["file"]=> + string(11) "(eval code)" + ["line"]=> + int(3) + ["char"]=> + int(1) +}