]> granicus.if.org Git - php/commitdiff
Rebase Joe's anon classes implementation
authorkrakjoe <joe.watkins@live.co.uk>
Sun, 26 Apr 2015 13:02:57 +0000 (15:02 +0200)
committerNikita Popov <nikic@php.net>
Sun, 26 Apr 2015 13:04:22 +0000 (15:04 +0200)
19 files changed:
Zend/tests/anon/001.phpt [new file with mode: 0644]
Zend/tests/anon/002.phpt [new file with mode: 0644]
Zend/tests/anon/003.phpt [new file with mode: 0644]
Zend/tests/anon/004.phpt [new file with mode: 0644]
Zend/tests/anon/005.phpt [new file with mode: 0644]
Zend/tests/anon/006.phpt [new file with mode: 0644]
Zend/tests/anon/007.phpt [new file with mode: 0644]
Zend/tests/anon/008.phpt [new file with mode: 0644]
Zend/tests/anon/009.phpt [new file with mode: 0644]
Zend/zend_ast.c
Zend/zend_builtin_functions.c
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_language_parser.y
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
ext/reflection/php_reflection.c
ext/reflection/tests/ReflectionClass_isAnonymous.phpt [new file with mode: 0644]
ext/reflection/tests/ReflectionClass_toString_001.phpt

diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt
new file mode 100644 (file)
index 0000000..7558955
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+declare bare anonymous class
+--FILE--
+<?php
+var_dump(new class{});
+--EXPECTF--
+object(class@%s)#%d (0) {
+}
+
+
diff --git a/Zend/tests/anon/002.phpt b/Zend/tests/anon/002.phpt
new file mode 100644 (file)
index 0000000..1cbaf81
--- /dev/null
@@ -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)
+
diff --git a/Zend/tests/anon/003.phpt b/Zend/tests/anon/003.phpt
new file mode 100644 (file)
index 0000000..ac023f4
--- /dev/null
@@ -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)
+}
+
diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt
new file mode 100644 (file)
index 0000000..f72e725
--- /dev/null
@@ -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)
diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt
new file mode 100644 (file)
index 0000000..7f1ff07
--- /dev/null
@@ -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
diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt
new file mode 100644 (file)
index 0000000..e5dc122
--- /dev/null
@@ -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) {
+}
+
diff --git a/Zend/tests/anon/007.phpt b/Zend/tests/anon/007.phpt
new file mode 100644 (file)
index 0000000..12f4da6
--- /dev/null
@@ -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) {
+}
+
diff --git a/Zend/tests/anon/008.phpt b/Zend/tests/anon/008.phpt
new file mode 100644 (file)
index 0000000..315163c
--- /dev/null
@@ -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"
diff --git a/Zend/tests/anon/009.phpt b/Zend/tests/anon/009.phpt
new file mode 100644 (file)
index 0000000..4a67cdd
--- /dev/null
@@ -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"
index 4eaad31a748013466b054ef79e019211e831c7f7..34a2e132f94e5d005ad0c0807ba7b57dde221099 100644 (file)
@@ -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);
                        }
index 7313b3206584461cc7a7d0b8c9e94842a8615c00..e36dbfd6e7761a7679903ccab942dbeb79400b36 100644 (file)
@@ -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);
index 65677f7d4791579e78319353be4083308b88d34f..93f99c862884a0a8fb1dc5ae9d3425a5f03b8cb8 100644 (file)
@@ -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;
 }
 /* }}} */
 
index efc2f820e16cb1f109679cdc2a19a5baa666e0d4..575dbe0803f7a8cd0af1ec7a802a213c0534a923 100644 (file)
@@ -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 */
index 0aba08121866a71274d6ddda8e5ec718c8a10a3b..b197bde9915862b1702f80cf7e7d0258bd7e47f5 100644 (file)
@@ -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:
index e16cbd55290f292d61476bbc5b4c1f1a4892ec54..4c8f9880c0027148d192f74e2ea840d859f6625c 100644 (file)
@@ -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();
 }
index 2802c6015c361e376c968af465b835e45770f43d..0dfe42a275a87520430da26c0e81cb71de7ed03f 100644 (file)
@@ -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();
 }
index bec69b2bbd20afd28e908fd1f1ed6b062d0674d4..76159479f2482054ee0b256e972bd985f2234f01 100644 (file)
@@ -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 (file)
index 0000000..deff25a
--- /dev/null
@@ -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)
index c47aba8b8d622b2f3c6a96daad0a7bff4177ffa8..b9a9b0d559cb82937e13307b4dd46df71b7f3502 100644 (file)
@@ -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] {