From e62554aa29732a2d323c235b3b4663f3c5c9fa1b Mon Sep 17 00:00:00 2001 From: krakjoe Date: Thu, 16 Oct 2014 06:25:27 +0100 Subject: [PATCH 01/21] anon class --- Zend/zend.c | 1 + Zend/zend_ast.c | 4 +- Zend/zend_compile.c | 370 ++++++++++++++++++++---------------- Zend/zend_globals.h | 4 +- Zend/zend_language_parser.y | 12 +- 5 files changed, 222 insertions(+), 169 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index d5a933b47cd0..e4daf69c5e8a 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -425,6 +425,7 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals TSRMLS compiler_globals->static_members_table = NULL; } compiler_globals->script_encoding_list = NULL; + compiler_globals->anon_class_id = 0L; #ifdef ZTS zend_interned_empty_string_init(&compiler_globals->empty_string TSRMLS_CC); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 9604079a9eb0..572e20240c69 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -393,7 +393,9 @@ static void zend_ast_destroy_ex(zend_ast *ast, zend_bool free) { case ZEND_AST_CLASS: { zend_ast_decl *decl = (zend_ast_decl *) ast; - zend_string_release(decl->name); + if (decl->name) { + zend_string_release(decl->name); + } if (decl->doc_comment) { zend_string_release(decl->doc_comment); } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5d3ee24c84fa..a9d1b111da4f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2935,6 +2935,189 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type TSRMLS } /* }}} */ +zend_string* zend_name_anon_class(TSRMLS_D) { + size_t len; + char *val; + zend_string *anon; + + len = spprintf + (&val, 0, "{anonymous}@%d", CG(anon_class_id)); + anon = zend_string_alloc(len, 0); + memcpy(&anon->val, val, len); + efree(val); + + CG(anon_class_id)++; + + return anon; +} + +zend_class_entry* zend_compile_class_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ +{ + zend_ast_decl *decl = (zend_ast_decl *) ast; + zend_ast *extends_ast = decl->child[0]; + zend_ast *implements_ast = decl->child[1]; + zend_ast *stmt_ast = decl->child[2]; + + zend_string *name = decl->name, *lcname, *import_name = NULL; + zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); + zend_op *opline; + znode declare_node, extends_node; + zend_class_entry *active = CG(active_class_entry); + + if (!name) { + name = zend_name_anon_class(TSRMLS_C); + } + + if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", + name->val); + } + + lcname = zend_string_alloc(name->len, 0); + zend_str_tolower_copy(lcname->val, name->val, name->len); + + + if (CG(current_import)) { + import_name = zend_hash_find_ptr(CG(current_import), lcname); + } + + if (CG(current_namespace)) { + name = zend_prefix_with_ns(name TSRMLS_CC); + + zend_string_release(lcname); + lcname = zend_string_alloc(name->len, 0); + zend_str_tolower_copy(lcname->val, name->val, name->len); + } else { + zend_string_addref(name); + } + + if (import_name && !zend_string_equals_str_ci(lcname, import_name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare class %s " + "because the name is already in use", name->val); + } + + name = zend_new_interned_string(name TSRMLS_CC); + lcname = zend_new_interned_string(lcname TSRMLS_CC); + + ce->type = ZEND_USER_CLASS; + ce->name = name; + zend_initialize_class_data(ce, 1 TSRMLS_CC); + + ce->ce_flags |= decl->flags; + ce->info.user.filename = zend_get_compiled_filename(TSRMLS_C); + ce->info.user.line_start = decl->start_lineno; + ce->info.user.line_end = decl->end_lineno; + if (decl->doc_comment) { + ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); + } + + if (extends_ast) { + if (ZEND_CE_IS_TRAIT(ce)) { + zend_error_noreturn(E_COMPILE_ERROR, "A trait (%s) cannot extend a class. " + "Traits can only be composed from other traits with the 'use' keyword. Error", + name->val); + } + + if (!zend_is_const_default_class_ref(extends_ast)) { + zend_string *extends_name = zend_ast_get_str(extends_ast); + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use '%s' as class name as it is reserved", extends_name->val); + } + + zend_compile_class_ref(&extends_node, extends_ast TSRMLS_CC); + } + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + zend_make_var_result(&declare_node, opline TSRMLS_CC); + + // TODO.AST drop this + GET_NODE(&CG(implementing_class), opline->result); + + opline->op2_type = IS_CONST; + LITERAL_STR(opline->op2, lcname); + + if (extends_ast) { + opline->opcode = ZEND_DECLARE_INHERITED_CLASS; + opline->extended_value = extends_node.u.op.var; + } else { + opline->opcode = ZEND_DECLARE_CLASS; + } + + { + zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos TSRMLS_CC); + + opline->op1_type = IS_CONST; + LITERAL_STR(opline->op1, key); + + zend_hash_update_ptr(CG(class_table), key, ce); + } + + CG(active_class_entry) = ce; + + if (implements_ast) { + zend_compile_implements(&declare_node, implements_ast TSRMLS_CC); + } + + zend_compile_stmt(stmt_ast TSRMLS_CC); + + if (ce->constructor) { + ce->constructor->common.fn_flags |= ZEND_ACC_CTOR; + if (ce->constructor->common.fn_flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Constructor %s::%s() cannot be static", + ce->name->val, ce->constructor->common.function_name->val); + } + } + if (ce->destructor) { + ce->destructor->common.fn_flags |= ZEND_ACC_DTOR; + if (ce->destructor->common.fn_flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Destructor %s::%s() cannot be static", + ce->name->val, ce->destructor->common.function_name->val); + } + } + if (ce->clone) { + ce->clone->common.fn_flags |= ZEND_ACC_CLONE; + if (ce->clone->common.fn_flags & ZEND_ACC_STATIC) { + zend_error_noreturn(E_COMPILE_ERROR, "Clone method %s::%s() cannot be static", + ce->name->val, ce->clone->common.function_name->val); + } + } + + /* Check for traits and proceed like with interfaces. + * The only difference will be a combined handling of them in the end. + * Thus, we need another opcode here. */ + if (ce->num_traits > 0) { + ce->traits = NULL; + ce->num_traits = 0; + ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS; + + zend_emit_op(NULL, ZEND_BIND_TRAITS, &declare_node, NULL TSRMLS_CC); + } + + if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) + && (extends_ast || ce->num_interfaces > 0) + ) { + zend_verify_abstract_class(ce TSRMLS_CC); + if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { + zend_emit_op(NULL, ZEND_VERIFY_ABSTRACT_CLASS, &declare_node, NULL TSRMLS_CC); + } + } + + /* Inherit interfaces; reset number to zero, we need it for above check and + * will restore it during actual implementation. + * The ZEND_ACC_IMPLEMENT_INTERFACES flag disables double call to + * zend_verify_abstract_class() */ + if (ce->num_interfaces > 0) { + ce->interfaces = NULL; + ce->num_interfaces = 0; + ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES; + } + + CG(active_class_entry) = active; + + return ce; +} +/* }}} */ + void zend_compile_new(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ { zend_ast *class_ast = ast->child[0]; @@ -2943,8 +3126,27 @@ void zend_compile_new(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ znode class_node, ctor_result; zend_op *opline; uint32_t opnum; - - zend_compile_class_ref(&class_node, class_ast TSRMLS_CC); + + if (class_ast->kind == ZEND_AST_CLASS) { + zend_class_entry *ce = + zend_compile_class_decl(class_ast TSRMLS_CC); + zend_string *name = ce->name; + uint32_t fetch_type = zend_get_class_fetch_type(name); + + opline = zend_emit_op(&class_node, + ZEND_FETCH_CLASS, NULL, NULL TSRMLS_CC); + opline->extended_value = fetch_type; + + if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { + opline->op2_type = IS_CONST; + opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), + zend_resolve_class_name(name, ZEND_NAME_FQ TSRMLS_CC) TSRMLS_CC); + } + + zend_string_release(name); + } else { + zend_compile_class_ref(&class_node, class_ast TSRMLS_CC); + } opnum = get_next_op_number(CG(active_op_array)); zend_emit_op(result, ZEND_NEW, &class_node, NULL TSRMLS_CC); @@ -4448,170 +4650,6 @@ void zend_compile_implements(znode *class_node, zend_ast *ast TSRMLS_DC) /* {{{ } /* }}} */ -void zend_compile_class_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ -{ - zend_ast_decl *decl = (zend_ast_decl *) ast; - zend_ast *extends_ast = decl->child[0]; - zend_ast *implements_ast = decl->child[1]; - zend_ast *stmt_ast = decl->child[2]; - - zend_string *name = decl->name, *lcname, *import_name = NULL; - zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); - zend_op *opline; - znode declare_node, extends_node; - - if (CG(active_class_entry)) { - zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested"); - return; - } - - if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", - name->val); - } - - lcname = zend_string_alloc(name->len, 0); - zend_str_tolower_copy(lcname->val, name->val, name->len); - - if (CG(current_import)) { - import_name = zend_hash_find_ptr(CG(current_import), lcname); - } - - if (CG(current_namespace)) { - name = zend_prefix_with_ns(name TSRMLS_CC); - - zend_string_release(lcname); - lcname = zend_string_alloc(name->len, 0); - zend_str_tolower_copy(lcname->val, name->val, name->len); - } else { - zend_string_addref(name); - } - - if (import_name && !zend_string_equals_str_ci(lcname, import_name)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare class %s " - "because the name is already in use", name->val); - } - - name = zend_new_interned_string(name TSRMLS_CC); - lcname = zend_new_interned_string(lcname TSRMLS_CC); - - ce->type = ZEND_USER_CLASS; - ce->name = name; - zend_initialize_class_data(ce, 1 TSRMLS_CC); - - ce->ce_flags |= decl->flags; - ce->info.user.filename = zend_get_compiled_filename(TSRMLS_C); - ce->info.user.line_start = decl->start_lineno; - ce->info.user.line_end = decl->end_lineno; - if (decl->doc_comment) { - ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); - } - - if (extends_ast) { - if (ZEND_CE_IS_TRAIT(ce)) { - zend_error_noreturn(E_COMPILE_ERROR, "A trait (%s) cannot extend a class. " - "Traits can only be composed from other traits with the 'use' keyword. Error", - name->val); - } - - if (!zend_is_const_default_class_ref(extends_ast)) { - zend_string *extends_name = zend_ast_get_str(extends_ast); - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use '%s' as class name as it is reserved", extends_name->val); - } - - zend_compile_class_ref(&extends_node, extends_ast TSRMLS_CC); - } - - opline = get_next_op(CG(active_op_array) TSRMLS_CC); - zend_make_var_result(&declare_node, opline TSRMLS_CC); - - // TODO.AST drop this - GET_NODE(&CG(implementing_class), opline->result); - - opline->op2_type = IS_CONST; - LITERAL_STR(opline->op2, lcname); - - if (extends_ast) { - opline->opcode = ZEND_DECLARE_INHERITED_CLASS; - opline->extended_value = extends_node.u.op.var; - } else { - opline->opcode = ZEND_DECLARE_CLASS; - } - - { - zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos TSRMLS_CC); - - opline->op1_type = IS_CONST; - LITERAL_STR(opline->op1, key); - - zend_hash_update_ptr(CG(class_table), key, ce); - } - - CG(active_class_entry) = ce; - - if (implements_ast) { - zend_compile_implements(&declare_node, implements_ast TSRMLS_CC); - } - - zend_compile_stmt(stmt_ast TSRMLS_CC); - - if (ce->constructor) { - ce->constructor->common.fn_flags |= ZEND_ACC_CTOR; - if (ce->constructor->common.fn_flags & ZEND_ACC_STATIC) { - zend_error_noreturn(E_COMPILE_ERROR, "Constructor %s::%s() cannot be static", - ce->name->val, ce->constructor->common.function_name->val); - } - } - if (ce->destructor) { - ce->destructor->common.fn_flags |= ZEND_ACC_DTOR; - if (ce->destructor->common.fn_flags & ZEND_ACC_STATIC) { - zend_error_noreturn(E_COMPILE_ERROR, "Destructor %s::%s() cannot be static", - ce->name->val, ce->destructor->common.function_name->val); - } - } - if (ce->clone) { - ce->clone->common.fn_flags |= ZEND_ACC_CLONE; - if (ce->clone->common.fn_flags & ZEND_ACC_STATIC) { - zend_error_noreturn(E_COMPILE_ERROR, "Clone method %s::%s() cannot be static", - ce->name->val, ce->clone->common.function_name->val); - } - } - - /* Check for traits and proceed like with interfaces. - * The only difference will be a combined handling of them in the end. - * Thus, we need another opcode here. */ - if (ce->num_traits > 0) { - ce->traits = NULL; - ce->num_traits = 0; - ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS; - - zend_emit_op(NULL, ZEND_BIND_TRAITS, &declare_node, NULL TSRMLS_CC); - } - - if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) - && (extends_ast || ce->num_interfaces > 0) - ) { - zend_verify_abstract_class(ce TSRMLS_CC); - if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { - zend_emit_op(NULL, ZEND_VERIFY_ABSTRACT_CLASS, &declare_node, NULL TSRMLS_CC); - } - } - - /* Inherit interfaces; reset number to zero, we need it for above check and - * will restore it during actual implementation. - * The ZEND_ACC_IMPLEMENT_INTERFACES flag disables double call to - * zend_verify_abstract_class() */ - if (ce->num_interfaces > 0) { - ce->interfaces = NULL; - ce->num_interfaces = 0; - ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES; - } - - CG(active_class_entry) = NULL; -} -/* }}} */ - static HashTable *zend_get_import_ht(uint32_t type TSRMLS_DC) /* {{{ */ { switch (type) { diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 43abffd1cedb..26ca2738f1c7 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -140,7 +140,9 @@ struct _zend_compiler_globals { zend_arena *ast_arena; zend_stack delayed_oplines_stack; - + + uint32_t anon_class_id; + #ifdef ZTS zval **static_members_table; int last_static_member; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 97fc4de54647..b61205aaf2e7 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -233,7 +233,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type extends_from parameter optional_type argument expr_without_variable global_var %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr -%type new_expr class_name class_name_reference simple_variable internal_functions_in_yacc +%type new_expr anonymous_class class_name class_name_reference simple_variable internal_functions_in_yacc %type exit_expr scalar backticks_expr lexical_var function_call member_name %type variable_class_name dereferencable_scalar class_name_scalar constant dereferencable %type callable_expr callable_variable static_member new_variable @@ -736,9 +736,19 @@ non_empty_for_exprs: | expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); } ; +anonymous_class: + T_CLASS { + $$ = CG(zend_lineno); + } extends_from implements_list backup_doc_comment '{' class_statement_list '}' { + $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $5, NULL, $3, $4, $7); + } +; + new_expr: T_NEW class_name_reference ctor_arguments { $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); } + | T_NEW anonymous_class ctor_arguments + { $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); } ; expr_without_variable: From dd7c61f4044eb0af35d94eda5ffaebe38e71cc34 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Thu, 16 Oct 2014 06:33:43 +0100 Subject: [PATCH 02/21] tests for anons --- Zend/tests/anon/001.phpt | 11 ++++++++ Zend/tests/anon/002.phpt | 10 ++++++++ Zend/tests/anon/003.phpt | 54 ++++++++++++++++++++++++++++++++++++++++ Zend/tests/anon/004.phpt | 32 ++++++++++++++++++++++++ Zend/tests/anon/005.phpt | 36 +++++++++++++++++++++++++++ Zend/tests/anon/006.phpt | 16 ++++++++++++ Zend/tests/anon/007.phpt | 24 ++++++++++++++++++ 7 files changed, 183 insertions(+) create mode 100644 Zend/tests/anon/001.phpt create mode 100644 Zend/tests/anon/002.phpt create mode 100644 Zend/tests/anon/003.phpt create mode 100644 Zend/tests/anon/004.phpt create mode 100644 Zend/tests/anon/005.phpt create mode 100644 Zend/tests/anon/006.phpt create mode 100644 Zend/tests/anon/007.phpt diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt new file mode 100644 index 000000000000..0b8d23308c60 --- /dev/null +++ b/Zend/tests/anon/001.phpt @@ -0,0 +1,11 @@ +--TEST-- +declare bare anonymous class +--FILE-- + +--EXPECTF-- +object(%s@%d)#%d (0) { +} + + diff --git a/Zend/tests/anon/002.phpt b/Zend/tests/anon/002.phpt new file mode 100644 index 000000000000..31c2eaf646bd --- /dev/null +++ b/Zend/tests/anon/002.phpt @@ -0,0 +1,10 @@ +--TEST-- +declare anonymous class extending another +--FILE-- + +--EXPECTF-- +object(%s@%d)#%d (0) { +} + diff --git a/Zend/tests/anon/003.phpt b/Zend/tests/anon/003.phpt new file mode 100644 index 000000000000..6db0bc4a89ff --- /dev/null +++ b/Zend/tests/anon/003.phpt @@ -0,0 +1,54 @@ +--TEST-- +reusing anonymous classes +--FILE-- +i = $i; + } + }($i)); +} +?> +--EXPECTF-- +object(%s@%d)#1 (1) { + ["i"]=> + int(1) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(2) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(3) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(4) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(5) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(6) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(7) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(8) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(9) +} +object(%s@%d)#1 (1) { + ["i"]=> + int(10) +} + diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt new file mode 100644 index 000000000000..b1b3d265a1c5 --- /dev/null +++ b/Zend/tests/anon/004.phpt @@ -0,0 +1,32 @@ +--TEST-- +testing anonymous inheritance +--FILE-- +data = $data; + } + + public function getArrayAccess() { + /* create a proxy object implementing array access */ + return new class extends Outer implements ArrayAccess { + public function offsetGet($offset) { return $this->data[$offset]; } + public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); } + public function offsetUnset($offset) { unset($this->data[$offset]); } + public function offsetExists($offset) { return isset($this->data[$offset]); } + }($this->data); + } +} + +$outer = new Outer(array( + rand(1, 100) +)); + +$proxy = $outer->getArrayAccess(); + +var_dump($proxy[0]); +?> +--EXPECTF-- +int(%d) diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt new file mode 100644 index 000000000000..474bbea0fb53 --- /dev/null +++ b/Zend/tests/anon/005.phpt @@ -0,0 +1,36 @@ +--TEST-- +testing reusing anons that implement an interface +--FILE-- +data = &$data; + } + + public function getArrayAccess() { + /* create a child object implementing array access */ + /* this grants you access to protected methods and members */ + return new class extends Outer implements ArrayAccess { + public function offsetGet($offset) { return $this->data[$offset]; } + public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); } + public function offsetUnset($offset) { unset($this->data[$offset]); } + public function offsetExists($offset) { return isset($this->data[$offset]); } + }($this->data); + } +} + +$data = array( + rand(1, 100), + rand(2, 200) +); +$outer = new Outer($data); +$proxy = $outer->getArrayAccess(); + +unset($proxy[0]); +var_dump(@$outer->getArrayAccess()[0]); +?> +--EXPECT-- +NULL diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt new file mode 100644 index 000000000000..e25aa3f6f068 --- /dev/null +++ b/Zend/tests/anon/006.phpt @@ -0,0 +1,16 @@ +--TEST-- +testing anons inside namespaces +--FILE-- + +--EXPECTF-- +object(%s@%d)#1 (0) { +} + diff --git a/Zend/tests/anon/007.phpt b/Zend/tests/anon/007.phpt new file mode 100644 index 000000000000..8c89d27f552e --- /dev/null +++ b/Zend/tests/anon/007.phpt @@ -0,0 +1,24 @@ +--TEST-- +testing anons in functions outside of classes in namespaces +--FILE-- + +--EXPECTF-- +object(%s@%d)#2 (0) { +} + From 39e352861190006a06a8415ccddadceb8e05725e Mon Sep 17 00:00:00 2001 From: krakjoe Date: Thu, 16 Oct 2014 06:51:25 +0100 Subject: [PATCH 03/21] fix nesting error --- Zend/zend_compile.c | 20 +++++++++++++++++++- Zend/zend_compile.h | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a9d1b111da4f..b0903f63e02b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -990,6 +990,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot extend from trait %s", ce->name->val, parent_ce->name->val); } + /* Reuse anonymous finalized class */ + if (ce->ce_flags & (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS) == (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) { + ce->refcount--; + return ce; + } + zend_do_inheritance(ce, parent_ce TSRMLS_CC); ce->refcount++; @@ -998,6 +1004,11 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare class %s", ce->name->val); } + + /* Set final on anonymous class */ + if ((ce->ce_flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS) { + ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + } return ce; } /* }}} */ @@ -2965,8 +2976,15 @@ zend_class_entry* zend_compile_class_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ zend_class_entry *active = CG(active_class_entry); if (!name) { - name = zend_name_anon_class(TSRMLS_C); + name = + zend_name_anon_class(TSRMLS_C); + decl->flags |= ZEND_ACC_ANON_CLASS; } + + if (CG(active_class_entry) && !((decl->flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)) { + zend_error(E_COMPILE_ERROR, "Class declarations may not be nested"); + return; + } if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 089c15f48a94..256205b85171 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -165,6 +165,7 @@ typedef struct _zend_try_catch_element { #define ZEND_ACC_FINAL_CLASS 0x40 #define ZEND_ACC_INTERFACE 0x80 #define ZEND_ACC_TRAIT 0x120 +#define ZEND_ACC_ANON_CLASS 0x400 /* method flags (visibility) */ /* The order of those must be kept - public < protected < private */ From e90e66e9a3f95618bdac9440f5d8fd34dba85476 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Thu, 16 Oct 2014 08:26:58 +0100 Subject: [PATCH 04/21] moar tests passing --- Zend/zend_compile.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index b0903f63e02b..465e13530330 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -936,9 +936,16 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze zend_error_noreturn(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(op1)); return NULL; } + ce->refcount++; if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) { ce->refcount--; + + if ((ce->ce_flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS) { + ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + return ce; + } + if (!compile_time) { /* If we're in compile time, in practice, it's quite possible * that we'll never reach this class declaration at runtime, @@ -2953,7 +2960,7 @@ zend_string* zend_name_anon_class(TSRMLS_D) { len = spprintf (&val, 0, "{anonymous}@%d", CG(anon_class_id)); - anon = zend_string_alloc(len, 0); + anon = zend_string_alloc(len, 1); memcpy(&anon->val, val, len); efree(val); From 86536eff4f718f907b87f2ff4c25e053c3dde8a1 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Thu, 16 Oct 2014 13:06:00 +0100 Subject: [PATCH 05/21] ... --- Zend/tests/anon/005.phpt | 2 +- Zend/zend_compile.c | 37 +++++++++++++++++++------------------ Zend/zend_compile.h | 3 ++- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt index 474bbea0fb53..7c561f716193 100644 --- a/Zend/tests/anon/005.phpt +++ b/Zend/tests/anon/005.phpt @@ -30,7 +30,7 @@ $outer = new Outer($data); $proxy = $outer->getArrayAccess(); unset($proxy[0]); -var_dump(@$outer->getArrayAccess()[0]); +var_dump($outer->getArrayAccess()[0]); ?> --EXPECT-- NULL diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 465e13530330..5a0b0e702d03 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -937,15 +937,15 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze return NULL; } + if (ce->ce_flags & ZEND_ACC_ANON_BOUND) { + return ce; + } + ce->refcount++; + if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) { ce->refcount--; - - if ((ce->ce_flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS) { - ce->ce_flags |= ZEND_ACC_FINAL_CLASS; - return ce; - } - + if (!compile_time) { /* If we're in compile time, in practice, it's quite possible * that we'll never reach this class declaration at runtime, @@ -956,6 +956,10 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze } return NULL; } else { + if (ce->ce_flags & ZEND_ACC_ANON_CLASS) { + ce->ce_flags |= ZEND_ACC_ANON_BOUND; + } + if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) { zend_verify_abstract_class(ce TSRMLS_CC); } @@ -997,13 +1001,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot extend from trait %s", ce->name->val, parent_ce->name->val); } - /* Reuse anonymous finalized class */ - if (ce->ce_flags & (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS) == (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) { - ce->refcount--; + /* Reuse anonymous bound class */ + if (ce->ce_flags & ZEND_ACC_ANON_BOUND) { return ce; } - zend_do_inheritance(ce, parent_ce TSRMLS_CC); + zend_do_inheritance(ce, parent_ce TSRMLS_CC); ce->refcount++; @@ -1013,9 +1016,10 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array } /* Set final on anonymous class */ - if ((ce->ce_flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS) { - ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + if (ce->ce_flags & ZEND_ACC_ANON_CLASS) { + ce->ce_flags |= ZEND_ACC_ANON_BOUND; } + return ce; } /* }}} */ @@ -2959,13 +2963,10 @@ zend_string* zend_name_anon_class(TSRMLS_D) { zend_string *anon; len = spprintf - (&val, 0, "{anonymous}@%d", CG(anon_class_id)); - anon = zend_string_alloc(len, 1); - memcpy(&anon->val, val, len); + (&val, 0, "{anonymous}@%d", ++CG(anon_class_id)); + anon = zend_string_init(val, len, 1); efree(val); - - CG(anon_class_id)++; - + return anon; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 256205b85171..b8dd7f3ef7b2 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -165,7 +165,8 @@ typedef struct _zend_try_catch_element { #define ZEND_ACC_FINAL_CLASS 0x40 #define ZEND_ACC_INTERFACE 0x80 #define ZEND_ACC_TRAIT 0x120 -#define ZEND_ACC_ANON_CLASS 0x400 +#define ZEND_ACC_ANON_CLASS 0x200 +#define ZEND_ACC_ANON_BOUND 0x400 /* method flags (visibility) */ /* The order of those must be kept - public < protected < private */ From fd98e202fd4c79bba21a93c5c1b3c8577b07dcdb Mon Sep 17 00:00:00 2001 From: krakjoe Date: Fri, 17 Oct 2014 05:32:48 +0100 Subject: [PATCH 06/21] pass all tests --- Zend/tests/anon/005.phpt | 2 +- Zend/zend_compile.c | 9 --------- Zend/zend_vm_def.h | 18 ++++++++++++++++++ Zend/zend_vm_execute.h | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt index 7c561f716193..474bbea0fb53 100644 --- a/Zend/tests/anon/005.phpt +++ b/Zend/tests/anon/005.phpt @@ -30,7 +30,7 @@ $outer = new Outer($data); $proxy = $outer->getArrayAccess(); unset($proxy[0]); -var_dump($outer->getArrayAccess()[0]); +var_dump(@$outer->getArrayAccess()[0]); ?> --EXPECT-- NULL diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5a0b0e702d03..b9ceff51d4da 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -956,10 +956,6 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze } return NULL; } else { - if (ce->ce_flags & ZEND_ACC_ANON_CLASS) { - ce->ce_flags |= ZEND_ACC_ANON_BOUND; - } - if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) { zend_verify_abstract_class(ce TSRMLS_CC); } @@ -1014,11 +1010,6 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array if (zend_hash_add_ptr(class_table, Z_STR_P(op2), ce) == NULL) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare class %s", ce->name->val); } - - /* Set final on anonymous class */ - if (ce->ce_flags & ZEND_ACC_ANON_CLASS) { - ce->ce_flags |= ZEND_ACC_ANON_BOUND; - } return ce; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index af532e88e40d..2087662184ed 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5266,6 +5266,15 @@ ZEND_VM_HANDLER(139, ZEND_DECLARE_CLASS, ANY, ANY) SAVE_OPLINE(); Z_CE_P(EX_VAR(opline->result.var)) = do_bind_class(&EX(func)->op_array, opline, EG(class_table), 0 TSRMLS_CC); + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_CLASS) { + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_BOUND) { + while (opline->opcode != ZEND_FETCH_CLASS) { + opline++; + } + ZEND_VM_SET_OPCODE(opline); + ZEND_VM_CONTINUE(); + } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; + } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -5276,6 +5285,15 @@ ZEND_VM_HANDLER(140, ZEND_DECLARE_INHERITED_CLASS, ANY, ANY) SAVE_OPLINE(); Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0 TSRMLS_CC); + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_CLASS) { + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_BOUND) { + while (opline->opcode != ZEND_FETCH_CLASS) { + opline++; + } + ZEND_VM_SET_OPCODE(opline); + ZEND_VM_CONTINUE(); + } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; + } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0768aa6df27b..f741aad9f065 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1149,6 +1149,15 @@ static int ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_AR SAVE_OPLINE(); Z_CE_P(EX_VAR(opline->result.var)) = do_bind_class(&EX(func)->op_array, opline, EG(class_table), 0 TSRMLS_CC); + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_CLASS) { + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_BOUND) { + while (opline->opcode != ZEND_FETCH_CLASS) { + opline++; + } + ZEND_VM_SET_OPCODE(opline); + ZEND_VM_CONTINUE(); + } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; + } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -1159,6 +1168,15 @@ static int ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER(ZEND_OPCODE_ SAVE_OPLINE(); Z_CE_P(EX_VAR(opline->result.var)) = do_bind_inherited_class(&EX(func)->op_array, opline, EG(class_table), Z_CE_P(EX_VAR(opline->extended_value)), 0 TSRMLS_CC); + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_CLASS) { + if (Z_CE_P(EX_VAR(opline->result.var))->ce_flags & ZEND_ACC_ANON_BOUND) { + while (opline->opcode != ZEND_FETCH_CLASS) { + opline++; + } + ZEND_VM_SET_OPCODE(opline); + ZEND_VM_CONTINUE(); + } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; + } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } From b0c6856c83d9a8df5239d25e3b37af5ef3b4e148 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 19 Oct 2014 17:55:46 +0100 Subject: [PATCH 07/21] ... --- Zend/tests/anon/005.phpt | 2 +- Zend/zend_vm_def.h | 6 ++---- Zend/zend_vm_execute.h | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt index 474bbea0fb53..287ef6c1a886 100644 --- a/Zend/tests/anon/005.phpt +++ b/Zend/tests/anon/005.phpt @@ -9,7 +9,7 @@ class Outer { /* array access will be implemented by the time we get to here */ $this->data = &$data; } - + public function getArrayAccess() { /* create a child object implementing array access */ /* this grants you access to protected methods and members */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 2087662184ed..a71fe82ccd0d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5271,8 +5271,7 @@ ZEND_VM_HANDLER(139, ZEND_DECLARE_CLASS, ANY, ANY) while (opline->opcode != ZEND_FETCH_CLASS) { opline++; } - ZEND_VM_SET_OPCODE(opline); - ZEND_VM_CONTINUE(); + ZEND_VM_JMP(opline); } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; } CHECK_EXCEPTION(); @@ -5290,8 +5289,7 @@ ZEND_VM_HANDLER(140, ZEND_DECLARE_INHERITED_CLASS, ANY, ANY) while (opline->opcode != ZEND_FETCH_CLASS) { opline++; } - ZEND_VM_SET_OPCODE(opline); - ZEND_VM_CONTINUE(); + ZEND_VM_JMP(opline); } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; } CHECK_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f741aad9f065..b13ca46d2c58 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1154,8 +1154,7 @@ static int ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_AR while (opline->opcode != ZEND_FETCH_CLASS) { opline++; } - ZEND_VM_SET_OPCODE(opline); - ZEND_VM_CONTINUE(); + ZEND_VM_JMP(opline); } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; } CHECK_EXCEPTION(); @@ -1173,8 +1172,7 @@ static int ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_SPEC_HANDLER(ZEND_OPCODE_ while (opline->opcode != ZEND_FETCH_CLASS) { opline++; } - ZEND_VM_SET_OPCODE(opline); - ZEND_VM_CONTINUE(); + ZEND_VM_JMP(opline); } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; } CHECK_EXCEPTION(); From 54d3370ac813a978585c87ce88b380a475bdf023 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 19 Oct 2014 18:56:17 +0100 Subject: [PATCH 08/21] drop extends use parent from name node --- Zend/tests/anon/001.phpt | 4 +- Zend/tests/anon/002.phpt | 17 +++++-- Zend/tests/anon/003.phpt | 20 ++++----- Zend/tests/anon/004.phpt | 2 +- Zend/tests/anon/005.phpt | 2 +- Zend/tests/anon/006.phpt | 2 +- Zend/tests/anon/007.phpt | 2 +- Zend/zend_compile.c | 90 ++++++++++++++++++++----------------- Zend/zend_language_parser.y | 13 ++++-- 9 files changed, 89 insertions(+), 63 deletions(-) diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt index 0b8d23308c60..51403a08afcd 100644 --- a/Zend/tests/anon/001.phpt +++ b/Zend/tests/anon/001.phpt @@ -4,8 +4,8 @@ declare bare anonymous class ---EXPECTF-- -object(%s@%d)#%d (0) { +--EXPECTF-- +object(class@%s.%d)#%d (0) { } diff --git a/Zend/tests/anon/002.phpt b/Zend/tests/anon/002.phpt index 31c2eaf646bd..6585ff3be788 100644 --- a/Zend/tests/anon/002.phpt +++ b/Zend/tests/anon/002.phpt @@ -2,9 +2,20 @@ declare anonymous class extending another --FILE-- --EXPECTF-- -object(%s@%d)#%d (0) { -} +bool(true) +bool(true) diff --git a/Zend/tests/anon/003.phpt b/Zend/tests/anon/003.phpt index 6db0bc4a89ff..7c5684812407 100644 --- a/Zend/tests/anon/003.phpt +++ b/Zend/tests/anon/003.phpt @@ -11,43 +11,43 @@ while (@$i++<10) { } ?> --EXPECTF-- -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(1) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(2) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(3) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(4) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(5) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(6) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(7) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(8) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(9) } -object(%s@%d)#1 (1) { +object(class@%s.%d)#1 (1) { ["i"]=> int(10) } diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt index b1b3d265a1c5..95623692c77a 100644 --- a/Zend/tests/anon/004.phpt +++ b/Zend/tests/anon/004.phpt @@ -11,7 +11,7 @@ class Outer { public function getArrayAccess() { /* create a proxy object implementing array access */ - return new class extends Outer implements ArrayAccess { + return new Outer implements ArrayAccess { public function offsetGet($offset) { return $this->data[$offset]; } public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); } public function offsetUnset($offset) { unset($this->data[$offset]); } diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt index 287ef6c1a886..bf87f5650a28 100644 --- a/Zend/tests/anon/005.phpt +++ b/Zend/tests/anon/005.phpt @@ -13,7 +13,7 @@ class Outer { public function getArrayAccess() { /* create a child object implementing array access */ /* this grants you access to protected methods and members */ - return new class extends Outer implements ArrayAccess { + return new Outer implements ArrayAccess { public function offsetGet($offset) { return $this->data[$offset]; } public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); } public function offsetUnset($offset) { unset($this->data[$offset]); } diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt index e25aa3f6f068..0f7edc9e8264 100644 --- a/Zend/tests/anon/006.phpt +++ b/Zend/tests/anon/006.phpt @@ -11,6 +11,6 @@ namespace { } ?> --EXPECTF-- -object(%s@%d)#1 (0) { +object(lone\class@%s.%d)#1 (0) { } diff --git a/Zend/tests/anon/007.phpt b/Zend/tests/anon/007.phpt index 8c89d27f552e..0bcc6577636b 100644 --- a/Zend/tests/anon/007.phpt +++ b/Zend/tests/anon/007.phpt @@ -19,6 +19,6 @@ namespace lone { } ?> --EXPECTF-- -object(%s@%d)#2 (0) { +object(lone\class@%s.%d)#2 (0) { } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index b9ceff51d4da..481076567841 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2948,18 +2948,61 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type TSRMLS } /* }}} */ -zend_string* zend_name_anon_class(TSRMLS_D) { +zend_string* zend_name_anon_class(zend_ast *parent TSRMLS_DC) { size_t len; char *val; zend_string *anon; - len = spprintf - (&val, 0, "{anonymous}@%d", ++CG(anon_class_id)); - anon = zend_string_init(val, len, 1); - efree(val); + if (parent) { + zval *extends = zend_ast_get_zval(parent); + len = spprintf( + &val, 0, "%s@%p.%d", + Z_STR_P(extends), CG(active_op_array), ++CG(anon_class_id)); + anon = zend_string_init(val, len, 1); + Z_DELREF_P(extends); /* ?? */ + efree(val); + } else { + len = spprintf( + &val, 0, "class@%p.%d", + CG(active_op_array), ++CG(anon_class_id)); + anon = zend_string_init(val, len, 1); + efree(val); + } return anon; +} /* }}} */ + +void zend_compile_implements(znode *class_node, zend_ast *ast TSRMLS_DC) /* {{{ */ +{ + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t i; + for (i = 0; i < list->children; ++i) { + zend_ast *class_ast = list->child[i]; + zend_string *name = zend_ast_get_str(class_ast); + + zend_op *opline; + + /* Traits can not implement interfaces */ + if (ZEND_CE_IS_TRAIT(CG(active_class_entry))) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as interface on '%s' " + "since it is a Trait", name->val, CG(active_class_entry)->name->val); + } + + if (!zend_is_const_default_class_ref(class_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use '%s' as interface name as it is reserved", name->val); + } + + opline = zend_emit_op(NULL, ZEND_ADD_INTERFACE, class_node, NULL TSRMLS_CC); + opline->extended_value = ZEND_FETCH_CLASS_INTERFACE; + opline->op2_type = IS_CONST; + opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), + zend_resolve_class_name_ast(class_ast TSRMLS_CC) TSRMLS_CC); + + CG(active_class_entry)->num_interfaces++; + } } +/* }}} */ zend_class_entry* zend_compile_class_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ { @@ -2974,10 +3017,9 @@ zend_class_entry* zend_compile_class_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ znode declare_node, extends_node; zend_class_entry *active = CG(active_class_entry); - if (!name) { + if (decl->flags & ZEND_ACC_ANON_CLASS) { name = - zend_name_anon_class(TSRMLS_C); - decl->flags |= ZEND_ACC_ANON_CLASS; + zend_name_anon_class((zend_ast*)name TSRMLS_CC); } if (CG(active_class_entry) && !((decl->flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)) { @@ -4635,38 +4677,6 @@ void zend_compile_use_trait(zend_ast *ast TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_compile_implements(znode *class_node, zend_ast *ast TSRMLS_DC) /* {{{ */ -{ - zend_ast_list *list = zend_ast_get_list(ast); - uint32_t i; - for (i = 0; i < list->children; ++i) { - zend_ast *class_ast = list->child[i]; - zend_string *name = zend_ast_get_str(class_ast); - - zend_op *opline; - - /* Traits can not implement interfaces */ - if (ZEND_CE_IS_TRAIT(CG(active_class_entry))) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as interface on '%s' " - "since it is a Trait", name->val, CG(active_class_entry)->name->val); - } - - if (!zend_is_const_default_class_ref(class_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use '%s' as interface name as it is reserved", name->val); - } - - opline = zend_emit_op(NULL, ZEND_ADD_INTERFACE, class_node, NULL TSRMLS_CC); - opline->extended_value = ZEND_FETCH_CLASS_INTERFACE; - opline->op2_type = IS_CONST; - opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), - zend_resolve_class_name_ast(class_ast TSRMLS_CC) TSRMLS_CC); - - CG(active_class_entry)->num_interfaces++; - } -} -/* }}} */ - static HashTable *zend_get_import_ht(uint32_t type TSRMLS_DC) /* {{{ */ { switch (type) { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index b61205aaf2e7..7dbd1030b39c 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -233,7 +233,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type extends_from parameter optional_type argument expr_without_variable global_var %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr -%type new_expr anonymous_class class_name class_name_reference simple_variable internal_functions_in_yacc +%type new_expr anonymous_class anonymous_class_parent class_name class_name_reference simple_variable internal_functions_in_yacc %type exit_expr scalar backticks_expr lexical_var function_call member_name %type variable_class_name dereferencable_scalar class_name_scalar constant dereferencable %type callable_expr callable_variable static_member new_variable @@ -736,11 +736,16 @@ non_empty_for_exprs: | expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); } ; +anonymous_class_parent: + name { $$ = $1; } + | T_CLASS { $$ = NULL; } +; + anonymous_class: - T_CLASS { + anonymous_class_parent { $$ = CG(zend_lineno); - } extends_from implements_list backup_doc_comment '{' class_statement_list '}' { - $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $5, NULL, $3, $4, $7); + } implements_list backup_doc_comment '{' class_statement_list '}' { + $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $4, $1, $1, $3, $6); } ; From 84e5225dbcbc6b1538ea01c2b88a61a61a62ac65 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 19 Oct 2014 19:08:45 +0100 Subject: [PATCH 09/21] ... --- Zend/tests/anon/001.phpt | 2 +- Zend/tests/anon/003.phpt | 20 ++++++++++---------- Zend/tests/anon/006.phpt | 2 +- Zend/tests/anon/007.phpt | 2 +- Zend/zend_compile.c | 11 ++++++----- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt index 51403a08afcd..9e5547694f6e 100644 --- a/Zend/tests/anon/001.phpt +++ b/Zend/tests/anon/001.phpt @@ -5,7 +5,7 @@ declare bare anonymous class var_dump(new class{}); ?> --EXPECTF-- -object(class@%s.%d)#%d (0) { +object(class@%s)#%d (0) { } diff --git a/Zend/tests/anon/003.phpt b/Zend/tests/anon/003.phpt index 7c5684812407..91d01201e19b 100644 --- a/Zend/tests/anon/003.phpt +++ b/Zend/tests/anon/003.phpt @@ -11,43 +11,43 @@ while (@$i++<10) { } ?> --EXPECTF-- -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(1) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(2) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(3) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(4) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(5) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(6) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(7) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(8) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(9) } -object(class@%s.%d)#1 (1) { +object(class@%s)#1 (1) { ["i"]=> int(10) } diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt index 0f7edc9e8264..0b73e8bedf56 100644 --- a/Zend/tests/anon/006.phpt +++ b/Zend/tests/anon/006.phpt @@ -11,6 +11,6 @@ namespace { } ?> --EXPECTF-- -object(lone\class@%s.%d)#1 (0) { +object(lone\class@%s)#1 (0) { } diff --git a/Zend/tests/anon/007.phpt b/Zend/tests/anon/007.phpt index 0bcc6577636b..a86fc81b4dd3 100644 --- a/Zend/tests/anon/007.phpt +++ b/Zend/tests/anon/007.phpt @@ -19,6 +19,6 @@ namespace lone { } ?> --EXPECTF-- -object(lone\class@%s.%d)#2 (0) { +object(lone\class@%s)#2 (0) { } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 481076567841..5b3b4b7cc759 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2952,19 +2952,20 @@ zend_string* zend_name_anon_class(zend_ast *parent TSRMLS_DC) { size_t len; char *val; zend_string *anon; - + uint32_t next = get_next_op_number(CG(active_op_array)); + if (parent) { zval *extends = zend_ast_get_zval(parent); len = spprintf( - &val, 0, "%s@%p.%d", - Z_STR_P(extends), CG(active_op_array), ++CG(anon_class_id)); + &val, 0, "%s@%p", + Z_STR_P(extends), &CG(active_op_array)->opcodes[next-1]); anon = zend_string_init(val, len, 1); Z_DELREF_P(extends); /* ?? */ efree(val); } else { len = spprintf( - &val, 0, "class@%p.%d", - CG(active_op_array), ++CG(anon_class_id)); + &val, 0, "class@%p", + CG(active_op_array), &CG(active_op_array)->opcodes[next-1]); anon = zend_string_init(val, len, 1); efree(val); } From ff02ee37d58d0e2966165531791d922c5d2e7574 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 19 Oct 2014 19:10:43 +0100 Subject: [PATCH 10/21] remove unused global --- Zend/zend.c | 1 - Zend/zend_globals.h | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index e4daf69c5e8a..d5a933b47cd0 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -425,7 +425,6 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals TSRMLS compiler_globals->static_members_table = NULL; } compiler_globals->script_encoding_list = NULL; - compiler_globals->anon_class_id = 0L; #ifdef ZTS zend_interned_empty_string_init(&compiler_globals->empty_string TSRMLS_CC); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 26ca2738f1c7..43abffd1cedb 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -140,9 +140,7 @@ struct _zend_compiler_globals { zend_arena *ast_arena; zend_stack delayed_oplines_stack; - - uint32_t anon_class_id; - + #ifdef ZTS zval **static_members_table; int last_static_member; From 4b74a9aab2d054169384ca4ec362aed14093d4f9 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 19 Oct 2014 19:58:38 +0100 Subject: [PATCH 11/21] add reflection method --- Zend/zend_compile.c | 2 +- ext/reflection/php_reflection.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5b3b4b7cc759..5e29aa2b856e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2961,7 +2961,7 @@ zend_string* zend_name_anon_class(zend_ast *parent TSRMLS_DC) { Z_STR_P(extends), &CG(active_op_array)->opcodes[next-1]); anon = zend_string_init(val, len, 1); Z_DELREF_P(extends); /* ?? */ - efree(val); + efree(val); } else { len = spprintf( &val, 0, "class@%p", diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 57e11945c1f8..13bea2c41750 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3540,6 +3540,21 @@ ZEND_METHOD(reflection_class, isUserDefined) } /* }}} */ +/* {{{ proto public bool ReflectionClass::isAnonymous() + Returns whether this class is anonymous */ +ZEND_METHOD(reflection_class, isAnonymous) +{ + reflection_object *intern; + zend_class_entry *ce; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ce); + RETURN_BOOL(ce->ce_flags & ZEND_ACC_ANON_CLASS); +} +/* }}} */ + /* {{{ proto public string ReflectionClass::getFileName() Returns the filename of the file this class was declared in */ ZEND_METHOD(reflection_class, getFileName) @@ -5869,6 +5884,7 @@ static const zend_function_entry reflection_class_functions[] = { ZEND_ME(reflection_class, getName, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isInternal, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isUserDefined, arginfo_reflection__void, 0) + ZEND_ME(reflection_class, isAnonymous, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isInstantiable, arginfo_reflection__void, 0) ZEND_ME(reflection_class, isCloneable, arginfo_reflection__void, 0) ZEND_ME(reflection_class, getFileName, arginfo_reflection__void, 0) From 6e8e84dde936a49cd7c21d190e2bae6380420c6e Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 19 Oct 2014 20:01:38 +0100 Subject: [PATCH 12/21] ... --- Zend/zend_compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5e29aa2b856e..605a511c05d9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2958,7 +2958,7 @@ zend_string* zend_name_anon_class(zend_ast *parent TSRMLS_DC) { zval *extends = zend_ast_get_zval(parent); len = spprintf( &val, 0, "%s@%p", - Z_STR_P(extends), &CG(active_op_array)->opcodes[next-1]); + Z_STRVAL_P(extends), &CG(active_op_array)->opcodes[next-1]); anon = zend_string_init(val, len, 1); Z_DELREF_P(extends); /* ?? */ efree(val); From 95a0f5c93466d0cb3b663e00186075dd87f8bee4 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 19 Oct 2014 20:18:32 +0100 Subject: [PATCH 13/21] ... --- Zend/zend_compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 605a511c05d9..e5495e3fc0d7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2965,7 +2965,7 @@ zend_string* zend_name_anon_class(zend_ast *parent TSRMLS_DC) { } else { len = spprintf( &val, 0, "class@%p", - CG(active_op_array), &CG(active_op_array)->opcodes[next-1]); + &CG(active_op_array)->opcodes[next-1]); anon = zend_string_init(val, len, 1); efree(val); } From fa5fa072609a44f1913c564a506e1331121f7863 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Mon, 20 Oct 2014 10:28:27 +0100 Subject: [PATCH 14/21] cleanup warnings --- Zend/zend_compile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e5495e3fc0d7..cc622b81c89e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2956,14 +2956,14 @@ zend_string* zend_name_anon_class(zend_ast *parent TSRMLS_DC) { if (parent) { zval *extends = zend_ast_get_zval(parent); - len = spprintf( + len = zend_spprintf( &val, 0, "%s@%p", Z_STRVAL_P(extends), &CG(active_op_array)->opcodes[next-1]); anon = zend_string_init(val, len, 1); Z_DELREF_P(extends); /* ?? */ efree(val); } else { - len = spprintf( + len = zend_spprintf( &val, 0, "class@%p", &CG(active_op_array)->opcodes[next-1]); anon = zend_string_init(val, len, 1); @@ -3025,7 +3025,7 @@ zend_class_entry* zend_compile_class_decl(zend_ast *ast TSRMLS_DC) /* {{{ */ if (CG(active_class_entry) && !((decl->flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)) { zend_error(E_COMPILE_ERROR, "Class declarations may not be nested"); - return; + return NULL; } if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) { From 0125d225459fd6372d89f8ca6625bd1d5cc2a5e3 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Mon, 20 Oct 2014 11:30:48 +0100 Subject: [PATCH 15/21] move constructor args --- Zend/tests/anon/002.phpt | 3 ++- Zend/tests/anon/003.phpt | 5 +++-- Zend/tests/anon/004.phpt | 4 ++-- Zend/tests/anon/005.phpt | 4 ++-- Zend/zend_language_parser.y | 21 ++++++++++----------- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Zend/tests/anon/002.phpt b/Zend/tests/anon/002.phpt index 6585ff3be788..dcc5683ea2ef 100644 --- a/Zend/tests/anon/002.phpt +++ b/Zend/tests/anon/002.phpt @@ -3,11 +3,12 @@ declare anonymous class extending another --FILE-- i = $i; } - }($i)); + }); } ?> --EXPECTF-- diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt index 95623692c77a..1b8c3f726218 100644 --- a/Zend/tests/anon/004.phpt +++ b/Zend/tests/anon/004.phpt @@ -11,12 +11,12 @@ class Outer { public function getArrayAccess() { /* create a proxy object implementing array access */ - return new Outer implements ArrayAccess { + return new class($this->data) extends Outer implements ArrayAccess { public function offsetGet($offset) { return $this->data[$offset]; } public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); } public function offsetUnset($offset) { unset($this->data[$offset]); } public function offsetExists($offset) { return isset($this->data[$offset]); } - }($this->data); + }; } } diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt index bf87f5650a28..57c00e73a0b0 100644 --- a/Zend/tests/anon/005.phpt +++ b/Zend/tests/anon/005.phpt @@ -13,12 +13,12 @@ class Outer { public function getArrayAccess() { /* create a child object implementing array access */ /* this grants you access to protected methods and members */ - return new Outer implements ArrayAccess { + return new class($this->data) implements ArrayAccess { public function offsetGet($offset) { return $this->data[$offset]; } public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); } public function offsetUnset($offset) { unset($this->data[$offset]); } public function offsetExists($offset) { return isset($this->data[$offset]); } - }($this->data); + }; } } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 7dbd1030b39c..def1a25cbd17 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -233,7 +233,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type extends_from parameter optional_type argument expr_without_variable global_var %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr -%type new_expr anonymous_class anonymous_class_parent class_name class_name_reference simple_variable internal_functions_in_yacc +%type new_expr anonymous_class class_name class_name_reference simple_variable internal_functions_in_yacc %type exit_expr scalar backticks_expr lexical_var function_call member_name %type variable_class_name dereferencable_scalar class_name_scalar constant dereferencable %type callable_expr callable_variable static_member new_variable @@ -736,24 +736,23 @@ non_empty_for_exprs: | expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); } ; -anonymous_class_parent: - name { $$ = $1; } - | T_CLASS { $$ = NULL; } -; - anonymous_class: - anonymous_class_parent { + T_CLASS ctor_arguments { $$ = CG(zend_lineno); - } implements_list backup_doc_comment '{' class_statement_list '}' { - $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $4, $1, $1, $3, $6); + } extends_from implements_list backup_doc_comment '{' class_statement_list '}' { + zend_ast *decl = zend_ast_create_decl( + ZEND_AST_CLASS, + ZEND_ACC_ANON_CLASS, + $3, $6, $4, $4, $5, $8); + $$ = zend_ast_create(ZEND_AST_NEW, decl, $2); } ; new_expr: T_NEW class_name_reference ctor_arguments { $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); } - | T_NEW anonymous_class ctor_arguments - { $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); } + | T_NEW anonymous_class + { $$ = $2; } ; expr_without_variable: From 84ec1805c316b48dddb0300eff80b99258603fdb Mon Sep 17 00:00:00 2001 From: krakjoe Date: Mon, 20 Oct 2014 11:36:09 +0100 Subject: [PATCH 16/21] update tests for clarity --- Zend/tests/anon/004.phpt | 5 ++--- Zend/tests/anon/005.phpt | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt index 1b8c3f726218..0da90e1cdb3b 100644 --- a/Zend/tests/anon/004.phpt +++ b/Zend/tests/anon/004.phpt @@ -24,9 +24,8 @@ $outer = new Outer(array( rand(1, 100) )); -$proxy = $outer->getArrayAccess(); - -var_dump($proxy[0]); +/* not null because inheritance */ +var_dump($outer->getArrayAccess()[0]); ?> --EXPECTF-- int(%d) diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt index 57c00e73a0b0..c166c3730da2 100644 --- a/Zend/tests/anon/005.phpt +++ b/Zend/tests/anon/005.phpt @@ -26,10 +26,11 @@ $data = array( rand(1, 100), rand(2, 200) ); + $outer = new Outer($data); $proxy = $outer->getArrayAccess(); -unset($proxy[0]); +/* null because no inheritance, so no access to protected member */ var_dump(@$outer->getArrayAccess()[0]); ?> --EXPECT-- From e1125d693ab0ac401bfde404f2b662874cd683f5 Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Tue, 24 Feb 2015 11:30:56 -0500 Subject: [PATCH 17/21] Added test for ReflectionClass::isAnonymous() --- .../tests/ReflectionClass_isAnonymous.phpt | 19 +++++++++++++++++++ .../tests/ReflectionClass_toString_001.phpt | 8 +++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/ReflectionClass_isAnonymous.phpt diff --git a/ext/reflection/tests/ReflectionClass_isAnonymous.phpt b/ext/reflection/tests/ReflectionClass_isAnonymous.phpt new file mode 100644 index 000000000000..19a29c8ba95b --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_isAnonymous.phpt @@ -0,0 +1,19 @@ +--TEST-- +ReflectionClass::isAnonymous() method +--CREDITS-- +Phil Sturgeon +--FILE-- +isAnonymous()); +var_dump($anonymousClass->isAnonymous()); + +?> +--EXPECT-- +bool(false) +bool(true) diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index 8dd571c3a9cc..277cd74f6c96 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -34,7 +34,7 @@ Class [ class ReflectionClass implements Reflector ] { Property [ public $name ] } - - Methods [49] { + - Methods [50] { Method [ final private method __clone ] { - Parameters [0] { @@ -72,6 +72,12 @@ Class [ class ReflectionClass implements Reflector ] { } } + Method [ public method isAnonymous ] { + + - Parameters [0] { + } + } + Method [ public method isInstantiable ] { - Parameters [0] { From dc5dc2ce7441ceb1142976ea6f82def7461a260d Mon Sep 17 00:00:00 2001 From: Phil Sturgeon Date: Tue, 24 Feb 2015 11:56:23 -0500 Subject: [PATCH 18/21] testing static access for methods and properties in anon classes --- Zend/tests/anon/001.phpt | 1 - Zend/tests/anon/002.phpt | 3 +-- Zend/tests/anon/003.phpt | 5 ++-- Zend/tests/anon/004.phpt | 7 +++--- Zend/tests/anon/005.phpt | 7 +++--- Zend/tests/anon/006.phpt | 3 +-- Zend/tests/anon/007.phpt | 11 ++++----- Zend/tests/anon/008.phpt | 23 +++++++++++++++++++ .../tests/ReflectionClass_isAnonymous.phpt | 2 -- 9 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 Zend/tests/anon/008.phpt diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt index 9e5547694f6e..75589550a817 100644 --- a/Zend/tests/anon/001.phpt +++ b/Zend/tests/anon/001.phpt @@ -3,7 +3,6 @@ declare bare anonymous class --FILE-- --EXPECTF-- object(class@%s)#%d (0) { } diff --git a/Zend/tests/anon/002.phpt b/Zend/tests/anon/002.phpt index dcc5683ea2ef..1cbaf81a9fb8 100644 --- a/Zend/tests/anon/002.phpt +++ b/Zend/tests/anon/002.phpt @@ -15,8 +15,7 @@ $a = new class extends A implements B { }; var_dump($a instanceof A, $a instanceof B); -?> ---EXPECTF-- +--EXPECTF-- bool(true) bool(true) diff --git a/Zend/tests/anon/003.phpt b/Zend/tests/anon/003.phpt index c144efc6c216..ac023f43a544 100644 --- a/Zend/tests/anon/003.phpt +++ b/Zend/tests/anon/003.phpt @@ -4,14 +4,13 @@ reusing anonymous classes i = $i; } }); } -?> ---EXPECTF-- +--EXPECTF-- object(class@%s)#1 (1) { ["i"]=> int(1) diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt index 0da90e1cdb3b..f72e7255de8b 100644 --- a/Zend/tests/anon/004.phpt +++ b/Zend/tests/anon/004.phpt @@ -4,11 +4,11 @@ testing anonymous inheritance data = $data; } - + public function getArrayAccess() { /* create a proxy object implementing array access */ return new class($this->data) extends Outer implements ArrayAccess { @@ -26,6 +26,5 @@ $outer = new Outer(array( /* not null because inheritance */ var_dump($outer->getArrayAccess()[0]); -?> ---EXPECTF-- +--EXPECTF-- int(%d) diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt index c166c3730da2..7f1ff0755ae5 100644 --- a/Zend/tests/anon/005.phpt +++ b/Zend/tests/anon/005.phpt @@ -4,7 +4,7 @@ testing reusing anons that implement an interface data = &$data; @@ -25,13 +25,12 @@ class Outer { $data = array( rand(1, 100), rand(2, 200) -); +); $outer = new Outer($data); $proxy = $outer->getArrayAccess(); /* null because no inheritance, so no access to protected member */ var_dump(@$outer->getArrayAccess()[0]); -?> ---EXPECT-- +--EXPECT-- NULL diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt index 0b73e8bedf56..e5dc1226d8a2 100644 --- a/Zend/tests/anon/006.phpt +++ b/Zend/tests/anon/006.phpt @@ -1,5 +1,5 @@ --TEST-- -testing anons inside namespaces +testing anon classes inside namespaces --FILE-- --EXPECTF-- object(lone\class@%s)#1 (0) { } diff --git a/Zend/tests/anon/007.phpt b/Zend/tests/anon/007.phpt index a86fc81b4dd3..12f4da66534a 100644 --- a/Zend/tests/anon/007.phpt +++ b/Zend/tests/anon/007.phpt @@ -1,24 +1,23 @@ --TEST-- -testing anons in functions outside of classes in namespaces +testing anon classes in functions outside of classes in namespaces --FILE-- ---EXPECTF-- +--EXPECTF-- object(lone\class@%s)#2 (0) { } diff --git a/Zend/tests/anon/008.phpt b/Zend/tests/anon/008.phpt new file mode 100644 index 000000000000..315163ca5f3a --- /dev/null +++ b/Zend/tests/anon/008.phpt @@ -0,0 +1,23 @@ +--TEST-- +testing static access for methods and properties in anon classes +--FILE-- + --FILE-- Date: Wed, 4 Mar 2015 13:13:13 -0500 Subject: [PATCH 19/21] Added test showing traits in anon classes. --- Zend/tests/anon/009.phpt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Zend/tests/anon/009.phpt diff --git a/Zend/tests/anon/009.phpt b/Zend/tests/anon/009.phpt new file mode 100644 index 000000000000..4a67cdd30d99 --- /dev/null +++ b/Zend/tests/anon/009.phpt @@ -0,0 +1,18 @@ +--TEST-- +testing traits in anon classes +--FILE-- +someMethod()); +--EXPECT-- +string(3) "bar" From 2dbd04dcdefab0efd82d36de7c3a03de3431e139 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Fri, 13 Mar 2015 17:54:17 +0000 Subject: [PATCH 20/21] deny serialization --- Zend/zend_compile.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 265cb5e50095..ff4882f8c937 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3053,6 +3053,10 @@ zend_class_entry* zend_compile_class_decl(zend_ast *ast) /* {{{ */ if (decl->flags & ZEND_ACC_ANON_CLASS) { name = zend_name_anon_class((zend_ast*)name TSRMLS_CC); + + /* do not support serial classes */ + ce->serialize = zend_class_serialize_deny; + ce->unserialize = zend_class_unserialize_deny; } if (CG(active_class_entry) && !((decl->flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)) { From 3254e98d40c2fb8467b0db58eed8c8e8656748a3 Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 15 Mar 2015 05:07:04 +0000 Subject: [PATCH 21/21] include interfaces header --- Zend/zend_compile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ff4882f8c937..0035c252741a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -27,6 +27,7 @@ #include "zend_llist.h" #include "zend_API.h" #include "zend_exceptions.h" +#include "zend_interfaces.h" #include "zend_virtual_cwd.h" #include "zend_multibyte.h" #include "zend_language_scanner.h"