diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt new file mode 100644 index 0000000000000..75589550a817e --- /dev/null +++ b/Zend/tests/anon/001.phpt @@ -0,0 +1,10 @@ +--TEST-- +declare bare anonymous class +--FILE-- +i = $i; + } + }); +} +--EXPECTF-- +object(class@%s)#1 (1) { + ["i"]=> + int(1) +} +object(class@%s)#1 (1) { + ["i"]=> + int(2) +} +object(class@%s)#1 (1) { + ["i"]=> + int(3) +} +object(class@%s)#1 (1) { + ["i"]=> + int(4) +} +object(class@%s)#1 (1) { + ["i"]=> + int(5) +} +object(class@%s)#1 (1) { + ["i"]=> + int(6) +} +object(class@%s)#1 (1) { + ["i"]=> + int(7) +} +object(class@%s)#1 (1) { + ["i"]=> + int(8) +} +object(class@%s)#1 (1) { + ["i"]=> + int(9) +} +object(class@%s)#1 (1) { + ["i"]=> + int(10) +} + diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt new file mode 100644 index 0000000000000..f72e7255de8b2 --- /dev/null +++ b/Zend/tests/anon/004.phpt @@ -0,0 +1,30 @@ +--TEST-- +testing anonymous inheritance +--FILE-- +data = $data; + } + + public function getArrayAccess() { + /* create a proxy object implementing array access */ + 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]); } + }; + } +} + +$outer = new Outer(array( + rand(1, 100) +)); + +/* 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 new file mode 100644 index 0000000000000..7f1ff0755ae59 --- /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($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]); } + }; + } +} + +$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-- +NULL diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt new file mode 100644 index 0000000000000..e5dc1226d8a23 --- /dev/null +++ b/Zend/tests/anon/006.phpt @@ -0,0 +1,15 @@ +--TEST-- +testing anon classes inside namespaces +--FILE-- +someMethod()); +--EXPECT-- +string(3) "bar" diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 242c99ff7f528..cfad1e9d41c2a 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -390,7 +390,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_builtin_functions.c b/Zend/zend_builtin_functions.c index fe7c8f7556580..4b2ce0a44cf42 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -216,6 +216,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_create_function, 0, 0, 2) ZEND_ARG_INFO(0, code) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_get_defined_functions, 0, 0, 0) + ZEND_ARG_INFO(0, disabled) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_get_resource_type, 0, 0, 1) ZEND_ARG_INFO(0, res) ZEND_END_ARG_INFO() @@ -301,7 +305,7 @@ static const zend_function_entry builtin_functions[] = { /* {{{ */ ZEND_FE(get_declared_classes, arginfo_zend__void) ZEND_FE(get_declared_traits, arginfo_zend__void) ZEND_FE(get_declared_interfaces, arginfo_zend__void) - ZEND_FE(get_defined_functions, arginfo_zend__void) + ZEND_FE(get_defined_functions, arginfo_get_defined_functions) ZEND_FE(get_defined_vars, arginfo_zend__void) ZEND_FE(create_function, arginfo_create_function) ZEND_FE(get_resource_type, arginfo_get_resource_type) @@ -1837,15 +1841,19 @@ ZEND_FUNCTION(get_declared_interfaces) static int copy_function_name(zval *zv, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ { zend_function *func = Z_PTR_P(zv); - zval *internal_ar = va_arg(args, zval *), - *user_ar = va_arg(args, zval *); + zval *internal_ar = va_arg(args, zval *), + *user_ar = va_arg(args, zval *); + zend_bool *disabled = va_arg(args, zend_bool*); if (hash_key->key == NULL || hash_key->key->val[0] == 0) { return 0; } if (func->type == ZEND_INTERNAL_FUNCTION) { - add_next_index_str(internal_ar, zend_string_copy(hash_key->key)); + zend_internal_function *intern = (zend_internal_function*) func; + if ((*disabled) || intern->handler != ZEND_FN(display_disabled_function)) { + add_next_index_str(internal_ar, zend_string_copy(hash_key->key)); + } } else if (func->type == ZEND_USER_FUNCTION) { add_next_index_str(user_ar, zend_string_copy(hash_key->key)); } @@ -1854,13 +1862,14 @@ static int copy_function_name(zval *zv, int num_args, va_list args, zend_hash_ke } /* }}} */ -/* {{{ proto array get_defined_functions(void) +/* {{{ proto array get_defined_functions(bool disabled = false) Returns an array of all defined functions */ ZEND_FUNCTION(get_defined_functions) { zval internal, user; + zend_bool disabled = 0; - if (zend_parse_parameters_none() == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &disabled) == FAILURE) { return; } @@ -1868,7 +1877,7 @@ ZEND_FUNCTION(get_defined_functions) array_init(&user); array_init(return_value); - zend_hash_apply_with_arguments(EG(function_table), copy_function_name, 2, &internal, &user); + zend_hash_apply_with_arguments(EG(function_table), copy_function_name, 3, &internal, &user, &disabled); zend_hash_str_add_new(Z_ARRVAL_P(return_value), "internal", sizeof("internal")-1, &internal); zend_hash_str_add_new(Z_ARRVAL_P(return_value), "user", sizeof("user")-1, &user); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index bdd0c17583b9c..0035c252741a9 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" @@ -934,9 +935,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; } + + 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 (!compile_time) { /* If we're in compile time, in practice, it's quite possible * that we'll never reach this class declaration at runtime, @@ -988,7 +996,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); } - zend_do_inheritance(ce, parent_ce); + /* Reuse anonymous bound class */ + if (ce->ce_flags & ZEND_ACC_ANON_BOUND) { + return ce; + } + + zend_do_inheritance(ce, parent_ce TSRMLS_CC); ce->refcount++; @@ -996,6 +1009,7 @@ 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); } + return ce; } /* }}} */ @@ -2974,7 +2988,234 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ } /* }}} */ -void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ +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 = 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 = zend_spprintf( + &val, 0, "class@%p", + &CG(active_op_array)->opcodes[next-1]); + anon = zend_string_init(val, len, 1); + efree(val); + } + + return anon; +} /* }}} */ + +void zend_compile_implements(znode *class_node, zend_ast *ast) /* {{{ */ +{ + 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; + + 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); + opline->op2_type = IS_CONST; + opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), + zend_resolve_class_name_ast(class_ast)); + + CG(active_class_entry)->num_interfaces++; + } +} +/* }}} */ + +zend_class_entry* zend_compile_class_decl(zend_ast *ast) /* {{{ */ +{ + 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 (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)) { + zend_error(E_COMPILE_ERROR, "Class declarations may not be nested"); + return NULL; + } + + 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]; zend_ast *args_ast = ast->child[1]; @@ -2987,7 +3228,26 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ class_node.op_type = IS_CONST; ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast)); } else { - zend_compile_class_ref(&class_node, class_ast); + 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)); @@ -4522,200 +4782,6 @@ void zend_compile_use_trait(zend_ast *ast) /* {{{ */ } /* }}} */ -void zend_compile_implements(znode *class_node, zend_ast *ast) /* {{{ */ -{ - 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; - - 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); - opline->op2_type = IS_CONST; - opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), - zend_resolve_class_name_ast(class_ast)); - - CG(active_class_entry)->num_interfaces++; - } -} -/* }}} */ - -void zend_compile_class_decl(zend_ast *ast) /* {{{ */ -{ - 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_tolower(name); - - if (CG(current_import)) { - import_name = zend_hash_find_ptr(CG(current_import), lcname); - } - - if (CG(current_namespace)) { - name = zend_prefix_with_ns(name); - - zend_string_release(lcname); - lcname = zend_string_tolower(name); - } 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); - lcname = zend_new_interned_string(lcname); - - ce->type = ZEND_USER_CLASS; - ce->name = name; - zend_initialize_class_data(ce, 1); - - ce->ce_flags |= decl->flags; - ce->info.user.filename = zend_get_compiled_filename(); - 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_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); - } - - opline = get_next_op(CG(active_op_array)); - zend_make_var_result(&declare_node, opline); - - // 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); - - 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); - } - - zend_compile_stmt(stmt_ast); - - 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->constructor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_error_noreturn(E_COMPILE_ERROR, - "Constructor %s::%s() cannot declare a return type", - 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); - } else if (ce->destructor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_error_noreturn(E_COMPILE_ERROR, - "Destructor %s::%s() cannot declare a return type", - 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); - } else if (ce->clone->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_error_noreturn(E_COMPILE_ERROR, - "%s::%s() cannot declare a return type", - 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); - } - - if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) - && (extends_ast || ce->num_interfaces > 0) - ) { - zend_verify_abstract_class(ce); - if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { - zend_emit_op(NULL, ZEND_VERIFY_ABSTRACT_CLASS, &declare_node, NULL); - } - } - - /* 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) /* {{{ */ { switch (type) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8a9b37cc6c6f8..4c4eb9c126a3b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -192,6 +192,8 @@ typedef struct _zend_try_catch_element { #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 #define ZEND_ACC_INTERFACE 0x80 #define ZEND_ACC_TRAIT 0x120 +#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 */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 009d7bae0b33f..842bb46088582 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -234,7 +234,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 @@ -763,9 +763,23 @@ non_empty_for_exprs: | expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); } ; +anonymous_class: + T_CLASS ctor_arguments { + $$ = CG(zend_lineno); + } 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, NULL); + $$ = 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 + { $$ = $2; } ; expr_without_variable: diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 8a1bda8cd76fa..7ccc27a1ce730 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5530,6 +5530,14 @@ 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); + 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_JMP(opline); + } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; + } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -5540,6 +5548,14 @@ 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); + 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_JMP(opline); + } 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 19e1a741b5df6..e4697b5edbc67 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1106,6 +1106,14 @@ 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); + 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_JMP(opline); + } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; + } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -1116,6 +1124,14 @@ 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); + 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_JMP(opline); + } else Z_CE_P(EX_VAR(opline->result.var))->ce_flags |= ZEND_ACC_ANON_BOUND; + } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 194c7ee99b999..8fb57f399bfad 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3611,6 +3611,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) @@ -5947,6 +5962,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) diff --git a/ext/reflection/tests/ReflectionClass_isAnonymous.phpt b/ext/reflection/tests/ReflectionClass_isAnonymous.phpt new file mode 100644 index 0000000000000..deff25aec299d --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_isAnonymous.phpt @@ -0,0 +1,17 @@ +--TEST-- +ReflectionClass::isAnonymous() method +--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 c47aba8b8d622..b9a9b0d559cb8 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] {