From: krakjoe Date: Sun, 26 Apr 2015 13:02:57 +0000 (+0200) Subject: Rebase Joe's anon classes implementation X-Git-Tag: PRE_PHP7_NSAPI_REMOVAL~149^2~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=49608e0608857638fdc978333764cf52ba270913;p=php Rebase Joe's anon classes implementation --- diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt new file mode 100644 index 0000000000..75589550a8 --- /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 0000000000..f72e7255de --- /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 0000000000..7f1ff0755a --- /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 0000000000..e5dc1226d8 --- /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 4eaad31a74..34a2e132f9 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -457,7 +457,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 7313b32065..e36dbfd6e7 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -217,6 +217,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() @@ -302,7 +306,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) @@ -1860,15 +1864,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)); } @@ -1877,13 +1885,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; } @@ -1891,7 +1900,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 65677f7d47..93f99c8628 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" @@ -1054,9 +1055,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, @@ -1106,7 +1114,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), 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++; @@ -1114,6 +1127,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 declare %s %s, because the name is already in use", zend_get_object_type(ce), ce->name->val); } + return ce; } /* }}} */ @@ -3228,7 +3242,34 @@ 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; +} /* }}} */ + +zend_class_entry *zend_compile_class_decl(zend_ast *ast); + +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]; @@ -3241,7 +3282,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, 1); + 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, 1); + } } opnum = get_next_op_number(CG(active_op_array)); @@ -4884,7 +4944,7 @@ void zend_compile_implements(znode *class_node, zend_ast *ast) /* {{{ */ } /* }}} */ -void zend_compile_class_decl(zend_ast *ast) /* {{{ */ +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]; @@ -4895,11 +4955,21 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ 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); - if (CG(active_class_entry)) { - zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested"); - return; + /* 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; + } zend_assert_valid_class_name(name); @@ -4913,7 +4983,8 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ name = zend_prefix_with_ns(name); zend_string_release(lcname); - lcname = zend_string_tolower(name); + lcname = zend_string_alloc(name->len, 0); + zend_str_tolower_copy(lcname->val, name->val, name->len); } else { zend_string_addref(name); } @@ -4923,15 +4994,15 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ "because the name is already in use", name->val); } - name = zend_new_interned_string(name); - lcname = zend_new_interned_string(lcname); + 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); + zend_initialize_class_data(ce, 1 TSRMLS_CC); ce->ce_flags |= decl->flags; - ce->info.user.filename = zend_get_compiled_filename(); + 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) { @@ -4939,6 +5010,12 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ } if (extends_ast) { + if (ce->ce_flags & ZEND_ACC_TRAIT) { + 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, @@ -4948,8 +5025,8 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ zend_compile_class_ref(&extends_node, extends_ast, 0); } - opline = get_next_op(CG(active_op_array)); - zend_make_var_result(&declare_node, opline); + 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(&FC(implementing_class), opline->result); @@ -4965,7 +5042,7 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ } { - zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos); + zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos TSRMLS_CC); opline->op1_type = IS_CONST; LITERAL_STR(opline->op1, key); @@ -4976,10 +5053,10 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ CG(active_class_entry) = ce; if (implements_ast) { - zend_compile_implements(&declare_node, implements_ast); + zend_compile_implements(&declare_node, implements_ast TSRMLS_CC); } - zend_compile_stmt(stmt_ast); + zend_compile_stmt(stmt_ast TSRMLS_CC); if (ce->num_traits == 0) { /* For traits this check is delayed until after trait binding */ @@ -4992,21 +5069,12 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ 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) { @@ -5014,10 +5082,6 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ 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); } } @@ -5029,15 +5093,15 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ ce->num_traits = 0; ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS; - zend_emit_op(NULL, ZEND_BIND_TRAITS, &declare_node, NULL); + 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); + 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); + zend_emit_op(NULL, ZEND_VERIFY_ABSTRACT_CLASS, &declare_node, NULL TSRMLS_CC); } } @@ -5051,7 +5115,9 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES; } - CG(active_class_entry) = NULL; + CG(active_class_entry) = active; + + return ce; } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index efc2f820e1..575dbe0803 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -206,6 +206,8 @@ typedef struct _zend_try_catch_element { #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 #define ZEND_ACC_INTERFACE 0x40 #define ZEND_ACC_TRAIT 0x80 +#define ZEND_ACC_ANON_CLASS 0x100 +#define ZEND_ACC_ANON_BOUND 0x200 /* 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 0aba081218..b197bde991 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -241,7 +241,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 @@ -798,9 +798,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 e16cbd5529..4c8f9880c0 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6723,6 +6723,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(); } @@ -6733,6 +6741,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 2802c6015c..0dfe42a275 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1305,6 +1305,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_HANDLER(ZEN 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(); } @@ -1315,6 +1323,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_SPEC_H 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 bec69b2bbd..76159479f2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3590,6 +3590,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) @@ -5943,6 +5958,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 0000000000..deff25aec2 --- /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 c47aba8b8d..b9a9b0d559 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] {