]> granicus.if.org Git - php/commitdiff
Reinstate early-binding for classes.
authorZeev Suraski <zeev@php.net>
Wed, 4 Feb 2004 16:30:15 +0000 (16:30 +0000)
committerZeev Suraski <zeev@php.net>
Wed, 4 Feb 2004 16:30:15 +0000 (16:30 +0000)
Note that this is available for downwards compatibility only - and it doesn't
work if you use new features (namely, interfaces).  Generally, people should
declare their classes before using them, but we just didn't want hell to break
loose (c)

Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_execute.h
Zend/zend_execute_API.c
Zend/zend_language_parser.y

index e979bb78835a9bf97d9fd5ccef32f951d68bdd51..022ed12a2db169d0b15bc73a5066851881eabd22 100644 (file)
@@ -1980,7 +1980,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
 }
 
 
-ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time)
+ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, zend_bool compile_time)
 {
        zend_function *function;
 
@@ -2012,7 +2012,7 @@ ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTa
 }
                        
 
-ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *function_table, HashTable *class_table TSRMLS_DC)
+ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *class_table, zend_bool compile_time TSRMLS_DC)
 {
        zend_class_entry *ce, **pce;
 
@@ -2025,14 +2025,23 @@ ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *function_ta
        ce->refcount++;
        if (zend_hash_add(class_table, opline->op2.u.constant.value.str.val, opline->op2.u.constant.value.str.len+1, &ce, sizeof(zend_class_entry *), NULL)==FAILURE) {
                ce->refcount--;
-               zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val);
+               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,
+                        * so we shut up about it.  This allows the if (!defined('FOO')) { return; }
+                        * approach to work.
+                        */
+                       zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val);
+               }
                return NULL;
        } else {
+               zend_verify_abstract_class(ce TSRMLS_CC);
                return ce;
        }
 }
 
-ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *function_table, HashTable *class_table, zend_class_entry *parent_ce TSRMLS_DC)
+
+ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time TSRMLS_DC)
 {
        zend_class_entry *ce, **pce;
        int found_ce;
@@ -2040,7 +2049,14 @@ ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *f
        found_ce = zend_hash_find(class_table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, (void **) &pce);
 
        if (found_ce == FAILURE) {
-               zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val);
+               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,
+                        * so we shut up about it.  This allows the if (!defined('FOO')) { return; }
+                        * approach to work.
+                        */
+                       zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", opline->op2.u.constant.value.str.val);
+               }
                return NULL;
        } else {
                ce = *pce;
@@ -2068,16 +2084,56 @@ ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *f
 void zend_do_early_binding(TSRMLS_D)
 {
        zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
+       HashTable *table;
 
        while (opline->opcode == ZEND_TICKS && opline > CG(active_op_array)->opcodes) {
                opline--;
        }
 
-       if (do_bind_function(opline, CG(function_table), CG(class_table), 1) == FAILURE) {
-               return;
+       switch (opline->opcode) {
+               case ZEND_DECLARE_FUNCTION:
+                       if (do_bind_function(opline, CG(function_table), 1) == FAILURE) {
+                               return;
+                       }
+                       table = CG(function_table);
+                       break;
+               case ZEND_VERIFY_ABSTRACT_CLASS: {
+                               zend_op *verify_abstract_class_op = opline;
+
+                               opline--;
+                               if (opline->opcode == ZEND_DECLARE_CLASS) {
+                                       if (do_bind_class(opline, CG(class_table), 1 TSRMLS_CC) == NULL) {
+                                               return;
+                                       }
+                               } else if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS) {
+                                       zval *parent_name = &(opline-1)->op2.u.constant;
+                                       zend_class_entry **pce;
+
+                                       if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSRMLS_CC) == FAILURE) {
+                                               return;
+                                       }
+                                       if (do_bind_inherited_class(opline, CG(class_table), *pce, 1 TSRMLS_CC) == NULL) {
+                                               return;
+                                       }
+                               } else {
+                                       /* We currently don't early-bind classes that implement interfaces */
+                                       return;
+                               }
+                               table = CG(class_table);
+                               /* clear the verify_abstract_class op */
+                               init_op(verify_abstract_class_op TSRMLS_CC);
+                               SET_UNUSED(verify_abstract_class_op->op1);
+                               SET_UNUSED(verify_abstract_class_op->op2);
+                               verify_abstract_class_op->opcode = ZEND_NOP;
+                       }
+
+                       break;
+               default:
+                       zend_error(E_COMPILE_ERROR, "Invalid binding type");
+                       return;
        }
 
-       zend_hash_del(CG(function_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len);
+       zend_hash_del(table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len);
        zval_dtor(&opline->op1.u.constant);
        zval_dtor(&opline->op2.u.constant);
        opline->opcode = ZEND_NOP;
index 98226c21cfb2ed7385e645ac867d279e00f726d2..f1385616718f930717866c51bd7de1ee485b89a0 100644 (file)
@@ -367,9 +367,9 @@ void zend_do_begin_catch(znode *try_token, znode *catch_class, znode *catch_var,
 void zend_do_end_catch(znode *try_token TSRMLS_DC);
 void zend_do_throw(znode *expr TSRMLS_DC);
 
-ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time);
-ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *function_table, HashTable *class_table TSRMLS_DC);
-ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *function_table, HashTable *class_table, zend_class_entry *parent_ce TSRMLS_DC);
+ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, zend_bool compile_time);
+ZEND_API zend_class_entry *do_bind_class(zend_op *opline, HashTable *class_table, zend_bool compile_time TSRMLS_DC);
+ZEND_API zend_class_entry *do_bind_inherited_class(zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time TSRMLS_DC);
 ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC);
 ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC);
 void zend_do_implements_interface(znode *interface_znode TSRMLS_DC);
index 7ac7a1910ce200cf3ed02839823f23d1a634b321..5e8209e3d2256b47943339a8e7ed2bac5c0f615b 100644 (file)
@@ -4006,21 +4006,21 @@ int zend_ext_fcall_end_handler(ZEND_OPCODE_HANDLER_ARGS)
 
 int zend_declare_class_handler(ZEND_OPCODE_HANDLER_ARGS)
 {
-       EX_T(opline->result.u.var).class_entry = do_bind_class(opline, EG(function_table), EG(class_table) TSRMLS_CC);
+       EX_T(opline->result.u.var).class_entry = do_bind_class(opline, EG(class_table), 0 TSRMLS_CC);
        NEXT_OPCODE();
 }
 
 
 int zend_declare_inherited_class_handler(ZEND_OPCODE_HANDLER_ARGS)
 {
-       EX_T(opline->result.u.var).class_entry = do_bind_inherited_class(opline, EG(function_table), EG(class_table), EX_T(opline->extended_value).class_entry TSRMLS_CC);
+       EX_T(opline->result.u.var).class_entry = do_bind_inherited_class(opline, EG(class_table), EX_T(opline->extended_value).class_entry, 0 TSRMLS_CC);
        NEXT_OPCODE();
 }
 
 
 int zend_declare_function_handler(ZEND_OPCODE_HANDLER_ARGS)
 {
-       do_bind_function(opline, EG(function_table), EG(class_table), 0);
+       do_bind_function(opline, EG(function_table), 0);
        NEXT_OPCODE();
 }
 
@@ -4107,52 +4107,9 @@ int zend_handle_exception_handler(ZEND_OPCODE_HANDLER_ARGS)
 }
 
 
-#define MAX_ABSTRACT_INFO_CNT 3
-#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s"
-
-#define DISPLAY_ABSTRACT_FN(idx) \
-       ai.afn[idx] ? ZEND_FN_SCOPE_NAME(ai.afn[idx]) : "", \
-       ai.afn[idx] ? "::" : "", \
-       ai.afn[idx] ? ai.afn[idx]->common.function_name : "", \
-       ai.afn[idx] && ai.afn[idx+1] ? ", " : (ai.afn[idx] && ai.cnt >= MAX_ABSTRACT_INFO_CNT ? ", ..." : "")
-
-
-typedef struct _zend_abstract_info {
-       zend_function *afn[MAX_ABSTRACT_INFO_CNT+1];
-       int cnt;
-} zend_abstract_info;
-
-
-int zend_verify_abstract_class_function(zend_function *fn, zend_abstract_info *ai TSRMLS_DC)
-{
-       if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
-               if (ai->cnt < MAX_ABSTRACT_INFO_CNT) {
-                       ai->afn[ai->cnt] = fn;
-               }
-               ai->cnt++;
-       }
-       return 0;
-}
-
-
 int zend_verify_abstract_class_handler(ZEND_OPCODE_HANDLER_ARGS)
 {
-       zend_class_entry *ce = EX_T(opline->op1.u.var).class_entry;
-       zend_abstract_info ai;
-
-       if ((ce->ce_flags & ZEND_ACC_ABSTRACT) && !(ce->ce_flags & ZEND_ACC_ABSTRACT_CLASS)) {
-               memset(&ai, 0, sizeof(ai));
-
-               zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC);
-               
-               zend_error(E_ERROR, "Class %s contains %d abstract methods and must therefore be declared abstract (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", 
-                       ce->name, ai.cnt, 
-                       DISPLAY_ABSTRACT_FN(0),
-                       DISPLAY_ABSTRACT_FN(1),
-                       DISPLAY_ABSTRACT_FN(2)
-                       );
-       }
-
+       zend_verify_abstract_class(EX_T(opline->op1.u.var).class_entry TSRMLS_CC);
        NEXT_OPCODE();
 }
 
index 1c3af83f6a02fcac44635812f01bc45c4b62d931..3f5d5cc4f8d82aefc0a7dbeb9b83e89b798e2528 100644 (file)
@@ -145,7 +145,7 @@ ZEND_API void zend_timeout(int dummy);
 ZEND_API zend_class_entry *zend_fetch_class(char *class_name, uint class_name_len, int fetch_type TSRMLS_DC);
 void zend_throw_exception_internal(zval *exception TSRMLS_DC);
 ZEND_API void zend_clear_exception(TSRMLS_D);
-
+void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC);
 
 #ifdef ZEND_WIN32
 void zend_init_timeout_thread();
@@ -161,8 +161,6 @@ void zend_shutdown_timeout_thread();
 
 void zend_assign_to_variable_reference(znode *result, zval **variable_ptr_ptr, zval **value_ptr_ptr, temp_variable *Ts TSRMLS_DC);
 
-#define IS_STRING_OFFSET 2
-
 /* The following tries to resolve the classname of a zval of type object.
  * Since it is slow it should be only used in error messages.
  */
index 3267829f7d2801f80be54d65e6feeccf40eef259..c443678f473021d9f6caae937e6965a2623daf56 100644 (file)
@@ -1225,6 +1225,51 @@ ZEND_API void zend_clear_exception(TSRMLS_D)
 }
 
 
+#define MAX_ABSTRACT_INFO_CNT 3
+#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s"
+#define DISPLAY_ABSTRACT_FN(idx) \
+       ai.afn[idx] ? ZEND_FN_SCOPE_NAME(ai.afn[idx]) : "", \
+       ai.afn[idx] ? "::" : "", \
+       ai.afn[idx] ? ai.afn[idx]->common.function_name : "", \
+       ai.afn[idx] && ai.afn[idx+1] ? ", " : (ai.afn[idx] && ai.cnt >= MAX_ABSTRACT_INFO_CNT ? ", ..." : "")
+
+typedef struct _zend_abstract_info {
+       zend_function *afn[MAX_ABSTRACT_INFO_CNT+1];
+       int cnt;
+} zend_abstract_info;
+
+
+static int zend_verify_abstract_class_function(zend_function *fn, zend_abstract_info *ai TSRMLS_DC)
+{
+       if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
+               if (ai->cnt < MAX_ABSTRACT_INFO_CNT) {
+                       ai->afn[ai->cnt] = fn;
+               }
+               ai->cnt++;
+       }
+       return 0;
+}
+
+
+void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC)
+{
+       zend_abstract_info ai;
+
+       if ((ce->ce_flags & ZEND_ACC_ABSTRACT) && !(ce->ce_flags & ZEND_ACC_ABSTRACT_CLASS)) {
+               memset(&ai, 0, sizeof(ai));
+
+               zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC);
+               
+               zend_error(E_ERROR, "Class %s contains %d abstract methods and must therefore be declared abstract (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", 
+                       ce->name, ai.cnt, 
+                       DISPLAY_ABSTRACT_FN(0),
+                       DISPLAY_ABSTRACT_FN(1),
+                       DISPLAY_ABSTRACT_FN(2)
+                       );
+       }
+}
+
+
 /*
  * Local variables:
  * tab-width: 4
index 8540ded223286f2a4a6b628f6c32dd49dc5bde41..d127af4297e738d874dd80019d52732ad2ab7529 100644 (file)
@@ -156,7 +156,7 @@ top_statement_list:
 top_statement:
                statement
        |       function_declaration_statement  { zend_do_early_binding(TSRMLS_C); }
-       |       class_declaration_statement
+       |       class_declaration_statement             { zend_do_early_binding(TSRMLS_C); }
 ;