Browse Source

Rebase Joe's anon classes implementation

pull/1249/head
krakjoe 11 years ago
committed by Nikita Popov
parent
commit
49608e0608
  1. 10
      Zend/tests/anon/001.phpt
  2. 21
      Zend/tests/anon/002.phpt
  3. 54
      Zend/tests/anon/003.phpt
  4. 30
      Zend/tests/anon/004.phpt
  5. 36
      Zend/tests/anon/005.phpt
  6. 15
      Zend/tests/anon/006.phpt
  7. 23
      Zend/tests/anon/007.phpt
  8. 23
      Zend/tests/anon/008.phpt
  9. 18
      Zend/tests/anon/009.phpt
  10. 4
      Zend/zend_ast.c
  11. 23
      Zend/zend_builtin_functions.c
  12. 134
      Zend/zend_compile.c
  13. 2
      Zend/zend_compile.h
  14. 16
      Zend/zend_language_parser.y
  15. 16
      Zend/zend_vm_def.h
  16. 16
      Zend/zend_vm_execute.h
  17. 16
      ext/reflection/php_reflection.c
  18. 17
      ext/reflection/tests/ReflectionClass_isAnonymous.phpt
  19. 8
      ext/reflection/tests/ReflectionClass_toString_001.phpt

10
Zend/tests/anon/001.phpt

@ -0,0 +1,10 @@
--TEST--
declare bare anonymous class
--FILE--
<?php
var_dump(new class{});
--EXPECTF--
object(class@%s)#%d (0) {
}

21
Zend/tests/anon/002.phpt

@ -0,0 +1,21 @@
--TEST--
declare anonymous class extending another
--FILE--
<?php
class A{}
interface B{
public function method();
}
$a = new class extends A implements B {
public function method(){
return true;
}
};
var_dump($a instanceof A, $a instanceof B);
--EXPECTF--
bool(true)
bool(true)

54
Zend/tests/anon/003.phpt

@ -0,0 +1,54 @@
--TEST--
reusing anonymous classes
--FILE--
<?php
while (@$i++<10) {
var_dump(new class($i) {
public function __construct($i) {
$this->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)
}

30
Zend/tests/anon/004.phpt

@ -0,0 +1,30 @@
--TEST--
testing anonymous inheritance
--FILE--
<?php
class Outer {
protected $data;
public function __construct($data) {
$this->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)

36
Zend/tests/anon/005.phpt

@ -0,0 +1,36 @@
--TEST--
testing reusing anons that implement an interface
--FILE--
<?php
class Outer {
protected $data;
public function __construct(&$data) {
/* 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 */
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

15
Zend/tests/anon/006.phpt

@ -0,0 +1,15 @@
--TEST--
testing anon classes inside namespaces
--FILE--
<?php
namespace lone {
$hello = new class{} ;
}
namespace {
var_dump ($hello);
}
--EXPECTF--
object(lone\class@%s)#1 (0) {
}

23
Zend/tests/anon/007.phpt

@ -0,0 +1,23 @@
--TEST--
testing anon classes in functions outside of classes in namespaces
--FILE--
<?php
namespace lone {
function my_factory() {
return new class{};
}
class Outer {
public function __construct() {
var_dump(
my_factory());
}
}
new Outer();
}
--EXPECTF--
object(lone\class@%s)#2 (0) {
}

23
Zend/tests/anon/008.phpt

@ -0,0 +1,23 @@
--TEST--
testing static access for methods and properties in anon classes
--FILE--
<?php
$anonClass = new class("cats", "dogs") {
public static $foo;
private static $bar;
public function __construct($foo, $bar) {
static::$foo = $foo;
static::$bar = $bar;
}
public static function getBar() {
return static::$bar;
}
};
var_dump($anonClass::$foo);
var_dump($anonClass::getBar());
--EXPECT--
string(4) "cats"
string(4) "dogs"

18
Zend/tests/anon/009.phpt

@ -0,0 +1,18 @@
--TEST--
testing traits in anon classes
--FILE--
<?php
trait Foo {
public function someMethod() {
return "bar";
}
}
$anonClass = new class {
use Foo;
};
var_dump($anonClass->someMethod());
--EXPECT--
string(3) "bar"

4
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);
}

23
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);

134
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;
}
/* }}} */

2
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 */

16
Zend/zend_language_parser.y

@ -241,7 +241,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> extends_from parameter optional_type argument expr_without_variable global_var
%type <ast> static_var class_statement trait_adaptation trait_precedence trait_alias
%type <ast> absolute_trait_method_reference trait_method_reference property echo_expr
%type <ast> new_expr class_name class_name_reference simple_variable internal_functions_in_yacc
%type <ast> new_expr anonymous_class class_name class_name_reference simple_variable internal_functions_in_yacc
%type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name
%type <ast> variable_class_name dereferencable_scalar class_name_scalar constant dereferencable
%type <ast> 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 {
$<num>$ = 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,
$<num>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:

16
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();
}

16
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();
}

16
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)

17
ext/reflection/tests/ReflectionClass_isAnonymous.phpt

@ -0,0 +1,17 @@
--TEST--
ReflectionClass::isAnonymous() method
--FILE--
<?php
class TestClass {}
$declaredClass = new ReflectionClass('TestClass');
$anonymousClass = new ReflectionClass(new class {});
var_dump($declaredClass->isAnonymous());
var_dump($anonymousClass->isAnonymous());
?>
--EXPECT--
bool(false)
bool(true)

8
ext/reflection/tests/ReflectionClass_toString_001.phpt

@ -34,7 +34,7 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
Property [ <default> public $name ]
}
- Methods [49] {
- Methods [50] {
Method [ <internal:Reflection> final private method __clone ] {
- Parameters [0] {
@ -72,6 +72,12 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector ] {
}
}
Method [ <internal:Reflection> public method isAnonymous ] {
- Parameters [0] {
}
}
Method [ <internal:Reflection> public method isInstantiable ] {
- Parameters [0] {

Loading…
Cancel
Save