From 69b54ba926b714dff0f8b54bea385e9a278c5849 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 25 May 2015 22:58:30 +0200 Subject: [PATCH] Also unreserve T_CLASS --- Zend/tests/grammar/regression_003.phpt | 3 +- Zend/zend_ast.c | 3 - Zend/zend_ast.h | 1 - Zend/zend_compile.c | 188 +++++++++++++++---------- Zend/zend_language_parser.y | 18 +-- ext/standard/string.c | 84 +++++------ 6 files changed, 152 insertions(+), 145 deletions(-) diff --git a/Zend/tests/grammar/regression_003.phpt b/Zend/tests/grammar/regression_003.phpt index 7213ca3e07..e475754ccd 100644 --- a/Zend/tests/grammar/regression_003.phpt +++ b/Zend/tests/grammar/regression_003.phpt @@ -8,5 +8,6 @@ class Obj const CLASS = 'class'; } +?> --EXPECTF-- -Parse error: syntax error, unexpected 'CLASS' (T_CLASS) in %s on line 5 +Fatal error: A class constant must not be called 'class'; it is reserved for class name fetching in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 34a2e132f9..aad9b64ffc 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1149,9 +1149,6 @@ simple_list: case ZEND_AST_CONST: zend_ast_export_ns_name(str, ast->child[0], 0, indent); break; - case ZEND_AST_RESOLVE_CLASS_NAME: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); - APPEND_STR("::class"); case ZEND_AST_UNPACK: smart_str_appends(str, "..."); ast = ast->child[0]; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 33aa292fb7..2b8d4d37b8 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -66,7 +66,6 @@ enum _zend_ast_kind { /* 1 child node */ ZEND_AST_VAR = 1 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_CONST, - ZEND_AST_RESOLVE_CLASS_NAME, ZEND_AST_UNPACK, ZEND_AST_UNARY_PLUS, ZEND_AST_UNARY_MINUS, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 458b571e5d..36652c646c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1347,6 +1347,88 @@ static inline zend_bool class_name_refers_to_active_ce(zend_string *class_name, } /* }}} */ +uint32_t zend_get_class_fetch_type(zend_string *name) /* {{{ */ +{ + if (zend_string_equals_literal_ci(name, "self")) { + return ZEND_FETCH_CLASS_SELF; + } else if (zend_string_equals_literal_ci(name, "parent")) { + return ZEND_FETCH_CLASS_PARENT; + } else if (zend_string_equals_literal_ci(name, "static")) { + return ZEND_FETCH_CLASS_STATIC; + } else { + return ZEND_FETCH_CLASS_DEFAULT; + } +} +/* }}} */ + +static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ +{ + /* Fully qualified names are always default refs */ + if (name_ast->attr == ZEND_NAME_FQ) { + return ZEND_FETCH_CLASS_DEFAULT; + } + + return zend_get_class_fetch_type(zend_ast_get_str(name_ast)); +} +/* }}} */ + +static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ +{ + if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry) && zend_is_scope_known()) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active", + fetch_type == ZEND_FETCH_CLASS_SELF ? "self" : + fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static"); + } +} +/* }}} */ + +static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_ast *name_ast, zend_bool constant) /* {{{ */ +{ + uint32_t fetch_type; + + if (name_ast->kind != ZEND_AST_ZVAL) { + return 0; + } + + if (!zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) { + return 0; + } + + if (class_ast->kind != ZEND_AST_ZVAL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Dynamic class names are not allowed in compile-time ::class fetch"); + } + + fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); + zend_ensure_valid_class_fetch_type(fetch_type); + + switch (fetch_type) { + case ZEND_FETCH_CLASS_SELF: + if (constant || (CG(active_class_entry) && zend_is_scope_known())) { + ZVAL_STR_COPY(zv, CG(active_class_entry)->name); + } else { + ZVAL_NULL(zv); + } + return 1; + case ZEND_FETCH_CLASS_STATIC: + case ZEND_FETCH_CLASS_PARENT: + if (constant) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s::class cannot be used for compile-time class name resolution", + fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" + ); + } else { + ZVAL_NULL(zv); + } + return 1; + case ZEND_FETCH_CLASS_DEFAULT: + ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast)); + return 1; + EMPTY_SWITCH_DEFAULT_CASE() + } +} +/* }}} */ + static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */ { uint32_t fetch_type = zend_get_class_fetch_type(class_name); @@ -1627,41 +1709,6 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify } /* }}} */ -uint32_t zend_get_class_fetch_type(zend_string *name) /* {{{ */ -{ - if (zend_string_equals_literal_ci(name, "self")) { - return ZEND_FETCH_CLASS_SELF; - } else if (zend_string_equals_literal_ci(name, "parent")) { - return ZEND_FETCH_CLASS_PARENT; - } else if (zend_string_equals_literal_ci(name, "static")) { - return ZEND_FETCH_CLASS_STATIC; - } else { - return ZEND_FETCH_CLASS_DEFAULT; - } -} -/* }}} */ - -static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ -{ - /* Fully qualified names are always default refs */ - if (name_ast->attr == ZEND_NAME_FQ) { - return ZEND_FETCH_CLASS_DEFAULT; - } - - return zend_get_class_fetch_type(zend_ast_get_str(name_ast)); -} -/* }}} */ - -static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ -{ - if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry) && zend_is_scope_known()) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active", - fetch_type == ZEND_FETCH_CLASS_SELF ? "self" : - fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static"); - } -} -/* }}} */ - ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_array, uint32_t var) /* {{{ */ { return op_array->vars[EX_VAR_TO_NUM(var)]; @@ -4749,6 +4796,11 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ zend_string *name = zend_ast_get_str(name_ast); zval value_zv; + if (zend_string_equals_literal_ci(name, "class")) { + zend_error(E_COMPILE_ERROR, + "A class constant must not be called 'class'; it is reserved for class name fetching"); + } + zend_const_expr_to_zval(&value_zv, value_ast); name = zend_new_interned_string_safe(name); @@ -6315,6 +6367,16 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ zend_op *opline; zend_string *resolved_name; + if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, const_ast, 0)) { + if (Z_TYPE(result->u.constant) == IS_NULL) { + zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); + opline->extended_value = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); + } else { + result->op_type = IS_CONST; + } + return; + } + zend_eval_const_expr(&class_ast); zend_eval_const_expr(&const_ast); @@ -6326,6 +6388,10 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ return; } } + if (const_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(const_ast), "class")) { + zend_error_noreturn(E_COMPILE_ERROR, + "Dynamic class names are not allowed in compile-time ::class fetch"); + } if (zend_is_const_default_class_ref(class_ast)) { class_node.op_type = IS_CONST; @@ -6538,7 +6604,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM || kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM || kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST - || kind == ZEND_AST_RESOLVE_CLASS_NAME || kind == ZEND_AST_MAGIC_CONST; + || kind == ZEND_AST_MAGIC_CONST; } /* }}} */ @@ -6557,6 +6623,11 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ "Dynamic class names are not allowed in compile-time class constant references"); } + if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, const_ast, 1)) { + *ast_ptr = zend_ast_create_zval(&result); + return; + } + class_name = zend_ast_get_str(class_ast); fetch_type = zend_get_class_fetch_type(class_name); @@ -6612,36 +6683,6 @@ void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */ } /* }}} */ -void zend_compile_const_expr_resolve_class_name(zend_ast **ast_ptr) /* {{{ */ -{ - zend_ast *ast = *ast_ptr; - zend_ast *name_ast = ast->child[0]; - zval result; - uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); - zend_ensure_valid_class_fetch_type(fetch_type); - - switch (fetch_type) { - case ZEND_FETCH_CLASS_SELF: - ZVAL_STR_COPY(&result, CG(active_class_entry)->name); - break; - case ZEND_FETCH_CLASS_STATIC: - case ZEND_FETCH_CLASS_PARENT: - zend_error_noreturn(E_COMPILE_ERROR, - "%s::class cannot be used for compile-time class name resolution", - fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" - ); - break; - case ZEND_FETCH_CLASS_DEFAULT: - ZVAL_STR(&result, zend_resolve_class_name_ast(name_ast)); - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - - zend_ast_destroy(ast); - *ast_ptr = zend_ast_create_zval(&result); -} -/* }}} */ - void zend_compile_const_expr_magic_const(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; @@ -6680,9 +6721,6 @@ void zend_compile_const_expr(zend_ast **ast_ptr) /* {{{ */ case ZEND_AST_CONST: zend_compile_const_expr_const(ast_ptr); break; - case ZEND_AST_RESOLVE_CLASS_NAME: - zend_compile_const_expr_resolve_class_name(ast_ptr); - break; case ZEND_AST_MAGIC_CONST: zend_compile_const_expr_magic_const(ast_ptr); break; @@ -6957,9 +6995,6 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_CLASS_CONST: zend_compile_class_const(result, ast); return; - case ZEND_AST_RESOLVE_CLASS_NAME: - zend_compile_resolve_class_name(result, ast); - return; case ZEND_AST_ENCAPS_LIST: zend_compile_encaps_list(result, ast); return; @@ -7129,9 +7164,18 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ zend_ast *name_ast = ast->child[1]; zend_string *resolved_name; + if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, name_ast, 1)) { + break; + } + zend_eval_const_expr(&class_ast); zend_eval_const_expr(&name_ast); + if (name_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) { + zend_error_noreturn(E_COMPILE_ERROR, + "Dynamic class names are not allowed in compile-time ::class fetch"); + } + if (class_ast->kind != ZEND_AST_ZVAL || name_ast->kind != ZEND_AST_ZVAL) { return; } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 72506c089c..e9a77c3399 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -21,14 +21,6 @@ /* $Id$ */ -/* - * LALR shift/reduce conflicts and how they are resolved: - * - * - 2 shift/reduce conflicts due to the dangling elseif/else ambiguity. Solved by shift. - * - */ - - #include "zend_compile.h" #include "zend.h" #include "zend_list.h" @@ -246,7 +238,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type new_expr anonymous_class class_name class_name_reference simple_variable %type internal_functions_in_yacc %type exit_expr scalar backticks_expr lexical_var function_call member_name property_name -%type variable_class_name dereferencable_scalar class_name_scalar constant dereferencable +%type variable_class_name dereferencable_scalar constant dereferencable %type callable_expr callable_variable static_member new_variable %type assignment_list_element array_pair encaps_var encaps_var_offset isset_variables %type top_statement_list use_declarations const_list inner_statement_list if_stmt @@ -279,7 +271,7 @@ reserved_non_modifiers: | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE -// | T_CLASS + | T_CLASS ; semi_reserved: @@ -1058,7 +1050,6 @@ scalar: | '"' encaps_list '"' { $$ = $2; } | T_START_HEREDOC encaps_list T_END_HEREDOC { $$ = $2; } | dereferencable_scalar { $$ = $1; } - | class_name_scalar { $$ = $1; } | constant { $$ = $1; } ; @@ -1262,11 +1253,6 @@ isset_variable: expr { $$ = zend_ast_create(ZEND_AST_ISSET, $1); } ; -class_name_scalar: - class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS - { $$ = zend_ast_create(ZEND_AST_RESOLVE_CLASS_NAME, $1); } -; - %% /* Copy to YYRES the contents of YYSTR after stripping away unnecessary diff --git a/ext/standard/string.c b/ext/standard/string.c index 3162533186..d3d8848128 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1200,69 +1200,49 @@ PHP_FUNCTION(explode) */ PHPAPI void php_implode(const zend_string *delim, zval *arr, zval *return_value) { - zval *tmp; - smart_str implstr = {0}; - int numelems, i = 0; - zend_string *str; + zval *tmp; + int numelems; + zend_string *str; + char *cptr; + size_t len = 0; + zend_string **strings, **strptr; numelems = zend_hash_num_elements(Z_ARRVAL_P(arr)); if (numelems == 0) { RETURN_EMPTY_STRING(); + } else if (numelems == 1) { + /* loop to search the first not undefined element... */ + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) { + RETURN_STR(zval_get_string(tmp)); + } ZEND_HASH_FOREACH_END(); } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) { -again: - switch (Z_TYPE_P(tmp)) { - case IS_STRING: - smart_str_append(&implstr, Z_STR_P(tmp)); - break; - - case IS_LONG: - smart_str_append_long(&implstr, Z_LVAL_P(tmp)); - break; - - case IS_TRUE: - smart_str_appendl(&implstr, "1", sizeof("1")-1); - break; - - case IS_NULL: - case IS_FALSE: - break; - - case IS_DOUBLE: { - char *stmp; - size_t str_len = spprintf(&stmp, 0, "%.*G", (int) EG(precision), Z_DVAL_P(tmp)); - smart_str_appendl(&implstr, stmp, str_len); - efree(stmp); - break; - } - - case IS_REFERENCE: - tmp = Z_REFVAL_P(tmp); - goto again; - - default: - str = zval_get_string(tmp); - smart_str_append(&implstr, str); - zend_string_release(str); - break; - - } + strings = emalloc(sizeof(zend_string *) * numelems); + strptr = strings - 1; - if (++i != numelems) { - smart_str_append(&implstr, delim); - } + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) { + *++strptr = zval_get_string(tmp); + len += (*strptr)->len; } ZEND_HASH_FOREACH_END(); - smart_str_0(&implstr); + str = zend_string_alloc(len + (numelems - 1) * delim->len, 0); + cptr = str->val + str->len; + *cptr = 0; - if (implstr.s) { - RETURN_NEW_STR(implstr.s); - } else { - smart_str_free(&implstr); - RETURN_EMPTY_STRING(); - } + do { + cptr -= (*strptr)->len; + memcpy(cptr, (*strptr)->val, (*strptr)->len); + zend_string_release(*strptr); + + cptr -= delim->len; + memcpy(cptr, delim->val, delim->len); + } while (--strptr > strings); + memcpy(str->val, (*strptr)->val, (*strptr)->len); + zend_string_release(*strptr); + + efree(strings); + RETURN_NEW_STR(str); } /* }}} */ -- 2.40.0