From 76703a042ef22b29a59b65de1a6b83ac6880c188 Mon Sep 17 00:00:00 2001 From: Ralph Schindler Date: Fri, 7 Sep 2012 23:56:47 -0500 Subject: [PATCH 1/3] "::class resolution as scalar" Feature * Allows for Name::class, self::class, static::class, parent::class resolution to a scalar based on current use rules and current namespace. * Reuses existing keyword "class" * Alters zend_compile.c\zend_resolve_class_name() to facilitate compile time class name resolution (and runtime by way of FCALL) --- Zend/tests/class_name_scalar.phpt | 57 +++++++++++++++++++++++++++++++ Zend/zend_compile.c | 48 +++++++++++++++++++++----- Zend/zend_language_parser.y | 6 ++++ 3 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/class_name_scalar.phpt diff --git a/Zend/tests/class_name_scalar.phpt b/Zend/tests/class_name_scalar.phpt new file mode 100644 index 0000000000000..329984cc1c042 --- /dev/null +++ b/Zend/tests/class_name_scalar.phpt @@ -0,0 +1,57 @@ +--TEST-- +class name as scaler from ::class keyword +--FILE-- + +--EXPECTF-- +In NS +string(11) "Foo\Bar\Moo" +Top +string(11) "Foo\Bar\One" +string(3) "Boo" +string(7) "Bee\Bop" +string(3) "Moo" +object(Foo\Bar\One)#1 (0) { +} +string(11) "Foo\Bar\Two" +string(11) "Foo\Bar\Two" +string(11) "Foo\Bar\One" +string(11) "Foo\Bar\Baz" +Parent +string(11) "Foo\Bar\Two" +string(13) "Foo\Bar\Three" +string(11) "Foo\Bar\One" +string(11) "Foo\Bar\Baz" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8046089ed456d..a510e5fecdc5a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2109,6 +2109,8 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n zval **ns; znode tmp; int len; + int lctype; + zend_op *opline; compound = memchr(Z_STRVAL(class_name->u.constant), '\\', Z_STRLEN(class_name->u.constant)); if (compound) { @@ -2153,7 +2155,7 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n *class_name = tmp; } } - } else if (CG(current_import) || CG(current_namespace)) { + } else { /* this is a plain name (without \) */ lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant)); @@ -2163,13 +2165,43 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n zval_dtor(&class_name->u.constant); class_name->u.constant = **ns; zval_copy_ctor(&class_name->u.constant); - } else if (CG(current_namespace)) { - /* plain name, no import - prepend current namespace to it */ - tmp.op_type = IS_CONST; - tmp.u.constant = *CG(current_namespace); - zval_copy_ctor(&tmp.u.constant); - zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC); - *class_name = tmp; + } else { + lctype = zend_get_class_fetch_type(lcname, strlen(lcname)); + switch (lctype) { + case ZEND_FETCH_CLASS_SELF: + tmp.op_type = IS_CONST; + ZVAL_STRINGL(&tmp.u.constant, CG(active_class_entry)->name, strlen(CG(active_class_entry)->name), 1); + *class_name = tmp; + break; + case ZEND_FETCH_CLASS_STATIC: + case ZEND_FETCH_CLASS_PARENT: + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_DO_FCALL; + opline->result.var = get_temporary_variable(CG(active_op_array)); + opline->result_type = IS_VAR; + if (lctype == ZEND_FETCH_CLASS_STATIC) { + LITERAL_STRINGL(opline->op1, estrndup("get_called_class", sizeof("get_called_class")-1), sizeof("get_called_class")-1, 0); + } else { + LITERAL_STRINGL(opline->op1, estrndup("get_parent_class", sizeof("get_parent_class")-1), sizeof("get_parent_class")-1, 0); + } + CALCULATE_LITERAL_HASH(opline->op1.constant); + opline->op1_type = IS_CONST; + GET_CACHE_SLOT(opline->op1.constant); + opline->extended_value = 0; + SET_UNUSED(opline->op2); + GET_NODE(class_name, opline->result); + break; + case ZEND_FETCH_CLASS_DEFAULT: + if (CG(current_namespace)) { + /* plain name, no import - prepend current namespace to it */ + tmp.op_type = IS_CONST; + tmp.u.constant = *CG(current_namespace); + zval_copy_ctor(&tmp.u.constant); + zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC); + *class_name = tmp; + } + break; + } } efree(lcname); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index c1514c5d783c1..9b3d6d5e0a12a 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -942,6 +942,7 @@ common_scalar: static_scalar: /* compile-time evaluated scalars */ common_scalar { $$ = $1; } + | class_name_scalar { $$ = $1; } | namespace_name { zend_do_fetch_constant(&$$, NULL, &$1, ZEND_CT, 1 TSRMLS_CC); } | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_CT, 0 TSRMLS_CC); } | T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); zend_do_fetch_constant(&$$, NULL, &$2, ZEND_CT, 0 TSRMLS_CC); } @@ -959,6 +960,7 @@ static_class_constant: scalar: T_STRING_VARNAME { $$ = $1; } + | class_name_scalar { $$ = $1; } | class_constant { $$ = $1; } | namespace_name { zend_do_fetch_constant(&$$, NULL, &$1, ZEND_RT, 1 TSRMLS_CC); } | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_RT, 0 TSRMLS_CC); } @@ -1200,6 +1202,10 @@ class_constant: | variable_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_fetch_constant(&$$, &$1, &$3, ZEND_RT, 0 TSRMLS_CC); } ; +class_name_scalar: + class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS { zend_resolve_class_name(&$1, ZEND_FETCH_CLASS_GLOBAL, 1 TSRMLS_CC); $$ = $1; } +; + %% /* Copy to YYRES the contents of YYSTR after stripping away unnecessary From 26d592a13a7c92ad535e9ede8264ccfb23c22612 Mon Sep 17 00:00:00 2001 From: Ralph Schindler Date: Sat, 8 Sep 2012 00:41:43 -0500 Subject: [PATCH 2/3] "::class" Feature: * Added class scope checking for self,parent+static * Fixed tab/spaces in phpt file --- Zend/tests/class_name_scalar.phpt | 25 ++++++++++++------------- Zend/zend_compile.c | 9 +++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Zend/tests/class_name_scalar.phpt b/Zend/tests/class_name_scalar.phpt index 329984cc1c042..21257592216c7 100644 --- a/Zend/tests/class_name_scalar.phpt +++ b/Zend/tests/class_name_scalar.phpt @@ -4,7 +4,7 @@ class name as scaler from ::class keyword ---EXPECTF-- +--EXPECTF-- In NS string(11) "Foo\Bar\Moo" Top diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a510e5fecdc5a..f341b75650dc2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2169,12 +2169,21 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n lctype = zend_get_class_fetch_type(lcname, strlen(lcname)); switch (lctype) { case ZEND_FETCH_CLASS_SELF: + if (!CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, "Cannot access self::class when no class scope is active"); + } tmp.op_type = IS_CONST; ZVAL_STRINGL(&tmp.u.constant, CG(active_class_entry)->name, strlen(CG(active_class_entry)->name), 1); *class_name = tmp; break; case ZEND_FETCH_CLASS_STATIC: + if (!CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, "Cannot access static::class when no class scope is active"); + } case ZEND_FETCH_CLASS_PARENT: + if (!CG(active_class_entry)) { + zend_error(E_COMPILE_ERROR, "Cannot access parent::class when no class scope is active"); + } opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_DO_FCALL; opline->result.var = get_temporary_variable(CG(active_op_array)); From 41a10e73fc67f6feaf24b9922ff9523835bf8c85 Mon Sep 17 00:00:00 2001 From: Ralph Schindler Date: Mon, 10 Sep 2012 10:40:52 -0500 Subject: [PATCH 3/3] "::class" Feature * Fixed memory leak by adding zval_dtor() on class_name in zend_compile.c's zend_resolve_class_name() --- Zend/zend_compile.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f341b75650dc2..c27b77c1cb008 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2172,9 +2172,9 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n if (!CG(active_class_entry)) { zend_error(E_COMPILE_ERROR, "Cannot access self::class when no class scope is active"); } - tmp.op_type = IS_CONST; - ZVAL_STRINGL(&tmp.u.constant, CG(active_class_entry)->name, strlen(CG(active_class_entry)->name), 1); - *class_name = tmp; + zval_dtor(&class_name->u.constant); + class_name->op_type = IS_CONST; + ZVAL_STRINGL(&class_name->u.constant, CG(active_class_entry)->name, CG(active_class_entry)->name_length, 1); break; case ZEND_FETCH_CLASS_STATIC: if (!CG(active_class_entry)) { @@ -2184,6 +2184,7 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n if (!CG(active_class_entry)) { zend_error(E_COMPILE_ERROR, "Cannot access parent::class when no class scope is active"); } + zval_dtor(&class_name->u.constant); opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_DO_FCALL; opline->result.var = get_temporary_variable(CG(active_op_array));