]> granicus.if.org Git - php/commitdiff
Also unreserve T_CLASS
authorBob Weinand <bobwei9@hotmail.com>
Mon, 25 May 2015 20:58:30 +0000 (22:58 +0200)
committerBob Weinand <bobwei9@hotmail.com>
Mon, 25 May 2015 20:58:30 +0000 (22:58 +0200)
Zend/tests/grammar/regression_003.phpt
Zend/zend_ast.c
Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y
ext/standard/string.c

index 7213ca3e07164b5577f5fa9145048029eda084f9..e475754ccd3f3f38800c20be7999180b4f85f572 100644 (file)
@@ -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
index 34a2e132f94e5d005ad0c0807ba7b57dde221099..aad9b64ffc83f4e751b8a492700fc1a3e67e9c55 100644 (file)
@@ -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];
index 33aa292fb7944306d4209ca5213fc67526484078..2b8d4d37b8bd4cb157536a5375c7a312062ce68d 100644 (file)
@@ -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,
index 458b571e5df5ecb1678836ff4d7dab9143214580..36652c646c1b324e68abbf11481a779eced12677 100644 (file)
@@ -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;
                        }
index 72506c089c49d92fd28bfacef34485a47dbb474a..e9a77c339933086249bdcbfa0565ef64ef24f58f 100644 (file)
 
 /* $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 <ast> new_expr anonymous_class class_name class_name_reference simple_variable
 %type <ast> internal_functions_in_yacc
 %type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name property_name
-%type <ast> variable_class_name dereferencable_scalar class_name_scalar constant dereferencable
+%type <ast> variable_class_name dereferencable_scalar constant dereferencable
 %type <ast> callable_expr callable_variable static_member new_variable
 %type <ast> assignment_list_element array_pair encaps_var encaps_var_offset isset_variables
 %type <ast> 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
index 3162533186a42a788be102258495204095efed8a..d3d88481284b963a40b1694c24022d85f54deb64 100644 (file)
@@ -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);
 }
 /* }}} */