]> granicus.if.org Git - php/commitdiff
- Initial support for nested class definitions
authorAndi Gutmans <andi@php.net>
Mon, 29 Oct 2001 17:19:02 +0000 (17:19 +0000)
committerAndi Gutmans <andi@php.net>
Mon, 29 Oct 2001 17:19:02 +0000 (17:19 +0000)
Zend/zend.h
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_execute.h
Zend/zend_globals.h
Zend/zend_language_parser.y
Zend/zend_opcode.c

index 8169ff329c6afb4e22ddb61c38db5f96a6aaaa1d..4b055d8ace423689dc52a395adf381c2d8f90e8b 100644 (file)
@@ -277,6 +277,7 @@ struct _zend_class_entry {
 
        HashTable function_table;
        HashTable default_properties;
+       HashTable class_table;
        zend_function_entry *builtin_functions;
 
        /* handlers */
index 4afdea8939fef7c4c2d7ca3b155b096b6a8167ca..0c7ae5c161176704928910d2e2f740327ac4eb9c 100644 (file)
@@ -901,6 +901,22 @@ void zend_do_begin_dynamic_function_call(znode *function_name TSRMLS_DC)
 }
 
 
+void do_fetch_class(znode *result, znode *class_entry, znode *class_name TSRMLS_DC)
+{
+       zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+
+       opline->opcode = ZEND_FETCH_CLASS;
+       if (class_entry) {
+               opline->op1 = *class_entry;
+       } else {
+               SET_UNUSED(opline->op1);
+       }
+       opline->op2 = *class_name;
+       opline->result.u.var = get_temporary_variable(CG(active_op_array));
+       opline->result.op_type = IS_CONST; /* FIXME: Hack so that INIT_FCALL_BY_NAME still knows this is a class */
+       *result = opline->result;
+}
+
 void zend_do_begin_class_member_function_call(znode *class_name, znode *function_name TSRMLS_DC)
 {
        unsigned char *ptr = NULL;
@@ -1644,30 +1660,29 @@ void zend_do_default_before_statement(znode *case_list, znode *default_token TSR
 }
 
 
-void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name TSRMLS_DC)
+void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znode *parent_class_name TSRMLS_DC)
 {
-       zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+       zend_op *opline;
        int runtime_inheritance = 0;
-
-       if (CG(active_class_entry)) {
-               zend_error(E_COMPILE_ERROR, "Class declarations may not be nested");
-               return;
-       }
-       CG(class_entry).type = ZEND_USER_CLASS;
-       CG(class_entry).name = class_name->u.constant.value.str.val;
-       CG(class_entry).name_length = class_name->u.constant.value.str.len;
-       CG(class_entry).refcount = (int *) emalloc(sizeof(int));
-       *CG(class_entry).refcount = 1;
-       CG(class_entry).constants_updated = 0;
+       zend_class_entry new_class_entry;
+
+       class_token->u.previously_active_class_entry = CG(active_class_entry);
+       new_class_entry.type = ZEND_USER_CLASS;
+       new_class_entry.name = class_name->u.constant.value.str.val;
+       new_class_entry.name_length = class_name->u.constant.value.str.len;
+       new_class_entry.refcount = (int *) emalloc(sizeof(int));
+       *new_class_entry.refcount = 1;
+       new_class_entry.constants_updated = 0;
        
-       zend_str_tolower(CG(class_entry).name, CG(class_entry).name_length);
+       zend_str_tolower(new_class_entry.name, new_class_entry.name_length);
 
-       zend_hash_init(&CG(class_entry).function_table, 10, NULL, ZEND_FUNCTION_DTOR, 0);
-       zend_hash_init(&CG(class_entry).default_properties, 10, NULL, ZVAL_PTR_DTOR, 0);
+       zend_hash_init(&new_class_entry.function_table, 10, NULL, ZEND_FUNCTION_DTOR, 0);
+       zend_hash_init(&new_class_entry.class_table, 10, NULL, ZEND_CLASS_DTOR, 0);
+       zend_hash_init(&new_class_entry.default_properties, 10, NULL, ZVAL_PTR_DTOR, 0);
 
-       CG(class_entry).handle_function_call = NULL;
-       CG(class_entry).handle_property_set = NULL;
-       CG(class_entry).handle_property_get = NULL;
+       new_class_entry.handle_function_call = NULL;
+       new_class_entry.handle_property_set = NULL;
+       new_class_entry.handle_property_get = NULL;
 
        /* code for inheritance from parent class */
        if (parent_class_name) {
@@ -1679,29 +1694,38 @@ void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name
                CG(active_ce_parent_class_name).value.str.val = estrndup(parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len);
                CG(active_ce_parent_class_name).value.str.len = parent_class_name->u.constant.value.str.len;
 
-               if (zend_hash_find(CG(class_table), parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len+1, (void **) &parent_class)==SUCCESS) {
+               if (zend_hash_find(CG(active_class_entry)?&CG(active_class_entry)->class_table:CG(class_table), parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len+1, (void **) &parent_class)==SUCCESS) {
                        /* copy functions */
-                       zend_hash_copy(&CG(class_entry).function_table, &parent_class->function_table, (copy_ctor_func_t) function_add_ref, &tmp_zend_function, sizeof(zend_function));
+                       zend_hash_copy(&new_class_entry.function_table, &parent_class->function_table, (copy_ctor_func_t) function_add_ref, &tmp_zend_function, sizeof(zend_function));
 
                        /* copy default properties */
-                       zend_hash_copy(&CG(class_entry).default_properties, &parent_class->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
+                       zend_hash_copy(&new_class_entry.default_properties, &parent_class->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
 
                        /* copy overloaded handlers */
-                       CG(class_entry).handle_function_call = parent_class->handle_function_call;
-                       CG(class_entry).handle_property_get  = parent_class->handle_property_get;
-                       CG(class_entry).handle_property_set  = parent_class->handle_property_set;
+                       new_class_entry.handle_function_call = parent_class->handle_function_call;
+                       new_class_entry.handle_property_get  = parent_class->handle_property_get;
+                       new_class_entry.handle_property_set  = parent_class->handle_property_set;
 
-                       CG(class_entry).parent = parent_class;
+                       new_class_entry.parent = parent_class;
 
                        zval_dtor(&parent_class_name->u.constant);
                } else {
                        runtime_inheritance = 1;
-                       CG(class_entry).parent = NULL;
+                       new_class_entry.parent = NULL;
                }
        } else {
-               CG(class_entry).parent = NULL;
+               new_class_entry.parent = NULL;
+       }
+
+       if (CG(active_class_entry)) {
+               if (runtime_inheritance) {
+                       zend_error(E_ERROR, "Only first level classes can inherit from undefined classes");
+               }
+               zend_hash_update(&CG(active_class_entry)->class_table, new_class_entry.name, new_class_entry.name_length+1, &new_class_entry, sizeof(zend_class_entry), (void **) &CG(active_class_entry));
+               return;
        }
 
+       opline = get_next_op(CG(active_op_array) TSRMLS_CC);
        opline->opcode = ZEND_DECLARE_FUNCTION_OR_CLASS;
        opline->op1.op_type = IS_CONST;
        build_runtime_defined_function_key(&opline->op1.u.constant, &class_name->u.constant, opline TSRMLS_CC);
@@ -1711,32 +1735,32 @@ void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name
        if (runtime_inheritance) {
                char *full_class_name;
 
-               opline->op2.u.constant.value.str.len = parent_class_name->u.constant.value.str.len+1+CG(class_entry).name_length;
+               opline->op2.u.constant.value.str.len = parent_class_name->u.constant.value.str.len+1+new_class_entry.name_length;
                full_class_name = opline->op2.u.constant.value.str.val = (char *) emalloc(opline->op2.u.constant.value.str.len+1);
 
                memcpy(full_class_name, parent_class_name->u.constant.value.str.val, parent_class_name->u.constant.value.str.len);
                full_class_name += parent_class_name->u.constant.value.str.len;
                full_class_name[0] = ':';
                full_class_name++;
-               memcpy(full_class_name, CG(class_entry).name, CG(class_entry).name_length);
+               memcpy(full_class_name, new_class_entry.name, new_class_entry.name_length);
                zval_dtor(&parent_class_name->u.constant);
-               full_class_name += CG(class_entry).name_length;
+               full_class_name += new_class_entry.name_length;
                full_class_name[0] = 0;
                opline->extended_value = ZEND_DECLARE_INHERITED_CLASS;
        } else {
-               opline->op2.u.constant.value.str.val = estrndup(CG(class_entry).name, CG(class_entry).name_length);
-               opline->op2.u.constant.value.str.len = CG(class_entry).name_length;
+               opline->op2.u.constant.value.str.val = estrndup(new_class_entry.name, new_class_entry.name_length);
+               opline->op2.u.constant.value.str.len = new_class_entry.name_length;
                opline->extended_value = ZEND_DECLARE_CLASS;
        }
        
-       zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &CG(class_entry), sizeof(zend_class_entry), (void **) &CG(active_class_entry));
+       zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &new_class_entry, sizeof(zend_class_entry), (void **) &CG(active_class_entry));
 }
 
 
-void zend_do_end_class_declaration(TSRMLS_D)
+void zend_do_end_class_declaration(znode *class_token TSRMLS_DC)
 {
        do_inherit_parent_constructor(CG(active_class_entry));
-       CG(active_class_entry) = NULL;
+       CG(active_class_entry) = class_token->u.previously_active_class_entry;
        if (CG(active_ce_parent_class_name).value.str.val) {
                efree(CG(active_ce_parent_class_name).value.str.val);
                CG(active_ce_parent_class_name).value.str.val = NULL;
index e90099005120e1a319fb9486df1e18b3aa73b460..17d88c85e92fbc1d6c3b204b9cd0089601d5c929 100644 (file)
@@ -52,6 +52,7 @@ typedef struct _znode {
                zend_uint opline_num; /*  Needs to be signed */
                zend_uint fetch_type;
                zend_op_array *op_array;
+               zend_class_entry *previously_active_class_entry; /* Used at compile-time */
                struct {
                        zend_uint var;  /* dummy */
                        zend_uint type;
@@ -271,6 +272,7 @@ void zend_do_receive_arg(int op, znode *var, znode *offset, znode *initializatio
 int zend_do_begin_function_call(znode *function_name TSRMLS_DC);
 void zend_do_begin_method_call(znode *object, znode *function_name TSRMLS_DC);
 void zend_do_begin_dynamic_function_call(znode *function_name TSRMLS_DC);
+void do_fetch_class(znode *result, znode *class_entry, znode *class_name TSRMLS_DC);
 void zend_do_begin_class_member_function_call(znode *class_name, znode *function_name TSRMLS_DC);
 void zend_do_end_function_call(znode *function_name, znode *result, znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC);
 void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC);
@@ -300,8 +302,8 @@ void zend_do_case_before_statement(znode *case_list, znode *case_token, znode *c
 void zend_do_case_after_statement(znode *result, znode *case_token TSRMLS_DC);
 void zend_do_default_before_statement(znode *case_list, znode *default_token TSRMLS_DC);
 
-void zend_do_begin_class_declaration(znode *class_name, znode *parent_class_name TSRMLS_DC);
-void zend_do_end_class_declaration(TSRMLS_D);
+void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znode *parent_class_name TSRMLS_DC);
+void zend_do_end_class_declaration(znode *class_token TSRMLS_DC);
 void zend_do_declare_property(znode *var_name, znode *value TSRMLS_DC);
 
 void zend_do_fetch_property(znode *result, znode *object, znode *property TSRMLS_DC);
@@ -536,7 +538,7 @@ int zendlex(znode *zendlval TSRMLS_DC);
 #define ZEND_THROW                                     108
 
 #define ZEND_NAMESPACE                         109
-
+#define ZEND_FETCH_CLASS                       110
 /* end of block */
 
 
index 01a75dfd835c571c2863a707bc87ee05bb9b1fa1..aed95cef975f96123853c489f0530ebb231aabf5 100644 (file)
@@ -1484,6 +1484,19 @@ binary_assign_op_addr: {
                                        FREE_OP(EX(Ts), &EX(opline)->op2, EG(free_op2));
                                }
                                NEXT_OPCODE();
+                       case ZEND_FETCH_CLASS:
+                               {
+                                       if (EX(opline)->op1.op_type == IS_UNUSED) {
+                                               if (zend_hash_find(EG(class_table), EX(opline)->op2.u.constant.value.str.val, EX(opline)->op2.u.constant.value.str.len+1, &EX(Ts)[EX(opline)->result.u.var].EA.class_entry) == FAILURE) {
+                                                       zend_error(E_ERROR, "Class '%s' not found", EX(opline)->op2.u.constant.value.str.val);
+                                               }
+                                       } else {
+                                               if (zend_hash_find(&EX(Ts)[EX(opline)->op1.u.var].EA.class_entry->class_table, EX(opline)->op2.u.constant.value.str.val, EX(opline)->op2.u.constant.value.str.len+1, &EX(Ts)[EX(opline)->result.u.var].EA.class_entry) == FAILURE) {
+                                                       zend_error(E_ERROR, "Class '%s' not found", EX(opline)->op2.u.constant.value.str.val);
+                                               }
+                                       }
+                                       NEXT_OPCODE();
+                               }
                        case ZEND_INIT_FCALL_BY_NAME: {
                                        zval *function_name;
                                        zend_function *function;
@@ -1510,8 +1523,7 @@ binary_assign_op_addr: {
                                        zend_str_tolower(tmp.value.str.val, tmp.value.str.len);
                                                
                                        if (EX(opline)->op1.op_type != IS_UNUSED) {
-                                               if (EX(opline)->op1.op_type==IS_CONST) { /* used for class_name::function() */
-                                                       zend_class_entry *ce;
+                                               if (EX(opline)->op1.op_type==IS_CONST) { /* used for class::function() */
                                                        zval **object_ptr_ptr;
 
                                                        if (zend_hash_find(EG(active_symbol_table), "this", sizeof("this"), (void **) &object_ptr_ptr)==FAILURE) {
@@ -1522,10 +1534,7 @@ binary_assign_op_addr: {
                                                                EX(object).ptr = *object_ptr_ptr;
                                                                EX(object).ptr->refcount++; /* For this pointer */
                                                        }
-                                                       if (zend_hash_find(EG(class_table), EX(opline)->op1.u.constant.value.str.val, EX(opline)->op1.u.constant.value.str.len+1, (void **) &ce)==FAILURE) { /* class doesn't exist */
-                                                               zend_error(E_ERROR, "Undefined class name '%s'", EX(opline)->op1.u.constant.value.str.val);
-                                                       }
-                                                       active_function_table = &ce->function_table;
+                                                       active_function_table = &EX(Ts)[EX(opline)->op1.u.var].EA.class_entry->function_table;
                                                } else { /* used for member function calls */
                                                        EX(object).ptr = get_zval_ptr(&EX(opline)->op1, EX(Ts), &EG(free_op1), BP_VAR_R);
                                                        
index 9c25e8c0ef4b9548dc4c62e08a1bb3f7b8602fec..7d78cda3984b21f0a86d97c44355eeca472af3eb 100644 (file)
@@ -44,6 +44,7 @@ typedef union _temp_variable {
                } data;
                        
                unsigned char type;
+               zend_class_entry *class_entry;
        } EA;
 } temp_variable;
 
index 67d15c314652545bd5fa5e2ffe1f17bac85e6839..10694bd2c9e8a5fe98984fdfa27b74fefff70e8a 100644 (file)
@@ -66,7 +66,7 @@ struct _zend_compiler_globals {
        zend_stack object_stack;
        zend_stack declare_stack;
 
-       zend_class_entry class_entry, *active_class_entry;
+       zend_class_entry *active_class_entry;
        zval active_ce_parent_class_name;
 
        /* variables for list() compilation */
index aa2e5f268730d4b03f3439d75118739f2eba4b76..67b6af8f994d4b37709d688ed24088e8c3a83bf1 100644 (file)
@@ -237,8 +237,8 @@ unticked_declaration_statement:
                        '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); }
        |       T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING  { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type TSRMLS_CC); }
                        parameter_list '(' inner_statement_list ')' ';' { zend_do_end_function_declaration(&$1 TSRMLS_CC); }
-       |       T_CLASS T_STRING { zend_do_begin_class_declaration(&$2, NULL TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(TSRMLS_C); }
-       |       T_CLASS T_STRING T_EXTENDS T_STRING { zend_do_begin_class_declaration(&$2, &$4 TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(TSRMLS_C); }
+       |       T_CLASS T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); }
+       |       T_CLASS T_STRING T_EXTENDS T_STRING { zend_do_begin_class_declaration(&$1, &$2, &$4 TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); }
 ;
 
 
@@ -391,7 +391,8 @@ class_statement:
                        parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); }
        |       T_OLD_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 1, $3.op_type TSRMLS_CC); }
                        parameter_list '(' inner_statement_list ')' ';' { zend_do_end_function_declaration(&$1 TSRMLS_CC); }
-
+       |       T_CLASS T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); }
+       |       T_CLASS T_STRING T_EXTENDS T_STRING { zend_do_begin_class_declaration(&$1, &$2, &$4 TSRMLS_CC); } '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, TSRMLS_C); }
 ;
 
 is_reference:
@@ -498,7 +499,7 @@ function_call:
                T_STRING        '(' { $2.u.opline_num = zend_do_begin_function_call(&$1 TSRMLS_CC); }
                                function_call_parameter_list
                                ')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
-       |       T_STRING T_PAAMAYIM_NEKUDOTAYIM static_or_variable_string '(' { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } 
+       |       parse_class_entry T_PAAMAYIM_NEKUDOTAYIM static_or_variable_string '(' { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } 
                                                                                        function_call_parameter_list 
                                                                                        ')' { zend_do_end_function_call(&$3, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
        |       cvar_without_objects  '(' { zend_do_end_variable_parse(BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1 TSRMLS_CC); }
@@ -506,6 +507,10 @@ function_call:
                        { zend_do_end_function_call(&$1, &$$, &$4, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
 ;
 
+parse_class_entry:
+               parse_class_entry T_PAAMAYIM_NEKUDOTAYIM T_STRING { do_fetch_class(&$$, &$1, &$3 TSRMLS_CC); }
+       |       T_STRING { do_fetch_class(&$$, NULL, &$1 TSRMLS_CC); }
+;
 
 static_or_variable_string:
                T_STRING        { $$ = $1; }
index 6462d3e4979298cf8016203353dab03609709aa6..08da904c35151c87f35c561ddb5f09bf42030b7f 100644 (file)
@@ -117,12 +117,14 @@ ZEND_API void destroy_zend_class(zend_class_entry *ce)
                        efree(ce->refcount);
                        zend_hash_destroy(&ce->function_table);
                        zend_hash_destroy(&ce->default_properties);
+                       zend_hash_destroy(&ce->class_table);
                        break;
                case ZEND_INTERNAL_CLASS:
                        free(ce->name);
                        free(ce->refcount);
                        zend_hash_destroy(&ce->function_table);
                        zend_hash_destroy(&ce->default_properties);
+                       /* zend_hash_destroy(&ce->class_table); FIXME: Make sure this is initialized */
                        break;
        }
 }