From: Zeev Suraski Date: Wed, 5 Mar 2003 11:14:44 +0000 (+0000) Subject: Add support for interfaces X-Git-Tag: RELEASE_0_5~609 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=26dd8492ed791824ea55456bff30ec1c12e09e82;p=php Add support for interfaces --- diff --git a/Zend/zend.h b/Zend/zend.h index f8111bb74b..3cf15371f0 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -332,6 +332,9 @@ struct _zend_class_entry { /* handlers */ zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); + zend_class_entry **interfaces; + zend_uint num_interfaces; + /* old handlers */ #if 0 void (*handle_function_call)(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 571b591fd6..8d889ff456 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -590,7 +590,9 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool only_subclass) { zval **obj, **class_name; char *lcname; - zend_class_entry *ce = NULL; + zend_class_entry *instance_ce; + zend_class_entry **ce; + zend_bool retval; if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &obj, &class_name)==FAILURE) { ZEND_WRONG_PARAM_COUNT(); @@ -610,18 +612,26 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool only_subclass) lcname = estrndup(Z_STRVAL_PP(class_name), Z_STRLEN_PP(class_name)); zend_str_tolower(lcname, Z_STRLEN_PP(class_name)); - if (only_subclass) - ce = Z_OBJCE_PP(obj)->parent; - else - ce = Z_OBJCE_PP(obj); - for (; ce != NULL; ce = ce->parent) { - if ((ce->name_length == Z_STRLEN_PP(class_name)) && !memcmp(ce->name, lcname, ce->name_length)) { - efree(lcname); - RETURN_TRUE; + if (zend_hash_find(EG(class_table), lcname, Z_STRLEN_PP(class_name)+1, (void **) &ce)==FAILURE) { + efree(lcname); + retval = 0; + } else { + if (only_subclass) { + instance_ce = Z_OBJCE_PP(obj)->parent; + } else { + instance_ce = Z_OBJCE_PP(obj); + } + + if (instanceof_function(instance_ce, *ce TSRMLS_CC)) { + retval = 1; + } else { + retval = 0; } } + efree(lcname); - RETURN_FALSE; + + RETURN_BOOL(retval); } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 1d05de1eda..1840f6fbbb 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -377,8 +377,17 @@ void zend_do_echo(znode *arg TSRMLS_DC) void zend_do_abstract_method(znode *function_name, znode *modifiers, znode *body TSRMLS_DC) { + char *method_type; + + if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { + modifiers->u.constant.value.lval |= ZEND_ACC_ABSTRACT; + method_type = "Interface"; + } else { + method_type = "Abstract"; + } + if (modifiers->u.constant.value.lval & ZEND_ACC_ABSTRACT) { - if (body->u.constant.value.lval & ZEND_ACC_ABSTRACT) { + if (body->u.constant.value.lval == ZEND_ACC_ABSTRACT) { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_RAISE_ABSTRACT_ERROR; @@ -386,11 +395,11 @@ void zend_do_abstract_method(znode *function_name, znode *modifiers, znode *body SET_UNUSED(opline->op2); } else { /* we had code in the function body */ - zend_error(E_COMPILE_ERROR, "Abstract function %s() cannot contain body", function_name->u.constant.value.str.val); + zend_error(E_COMPILE_ERROR, "%s function %s::%s() cannot contain body", method_type, CG(active_class_entry)->name, function_name->u.constant.value.str.val); } } else { - if (body->u.constant.value.lval & ZEND_ACC_ABSTRACT) { - zend_error(E_COMPILE_ERROR, "Non-abstract method %s() must contain body", function_name->u.constant.value.str.val); + if (body->u.constant.value.lval == ZEND_ACC_ABSTRACT) { + zend_error(E_COMPILE_ERROR, "Non-abstract method %s::%s() must contain body", CG(active_class_entry)->name, function_name->u.constant.value.str.val); } } } @@ -665,11 +674,13 @@ zend_bool zend_is_function_or_method_call(znode *variable) return ((type & ZEND_PARSED_METHOD_CALL) || (type == ZEND_PARSED_FUNCTION_CALL)); } + void zend_do_begin_import(TSRMLS_D) { zend_llist_init(&CG(import_commands), sizeof(zend_op), NULL, 0); } + void zend_do_import(int type, znode *what TSRMLS_DC) { zend_op opline; @@ -700,11 +711,11 @@ void zend_do_import(int type, znode *what TSRMLS_DC) zend_llist_add_element(&CG(import_commands), &opline); } + void zend_do_end_import(znode *import_from TSRMLS_DC) { zend_llist_element *le; zend_op *opline, *opline_ptr; - le = CG(import_commands).head; @@ -719,7 +730,6 @@ void zend_do_end_import(znode *import_from TSRMLS_DC) } - void zend_do_begin_variable_parse(TSRMLS_D) { zend_llist fetch_list; @@ -933,12 +943,25 @@ int zend_do_verify_access_types(znode *current_access_type, znode *new_modifier) } -void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, zend_uint fn_flags TSRMLS_DC) +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) { zend_op_array op_array; char *name = function_name->u.constant.value.str.val; int name_len = function_name->u.constant.value.str.len; int function_begin_line = function_token->u.opline_num; + zend_uint fn_flags; + + if (is_method) { + if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { + if (!(fn_flags_znode->u.constant.value.lval & ZEND_ACC_PUBLIC)) { + zend_error(E_COMPILE_ERROR, "Access type for interface method %s::%s() must be omitted or declared public", CG(active_class_entry)->name, function_name->u.constant.value.str.val); + } + fn_flags_znode->u.constant.value.lval |= ZEND_ACC_ABSTRACT; /* propagates to the rest of the parser */ + } + fn_flags = fn_flags_znode->u.constant.value.lval; /* must be done *after* the above check */ + } else { + fn_flags = 0; + } function_token->u.op_array = CG(active_op_array); zend_str_tolower(name, name_len); @@ -972,7 +995,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n && (child_op_array == parent_op_array)) { zend_hash_update(&CG(active_class_entry)->function_table, name, name_len+1, &op_array, sizeof(zend_op_array), (void **) &CG(active_op_array)); } else { - zend_error(E_COMPILE_ERROR, "Cannot redeclare %s()", name); + zend_error(E_COMPILE_ERROR, "Cannot redeclare %s::%s()", CG(active_class_entry)->name, name); } } @@ -1680,6 +1703,11 @@ static zend_bool do_inherit_property_access_check(HashTable *target_ht, zend_pro void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) { + if ((ce->ce_flags & ZEND_ACC_INTERFACE) + && !(parent_ce->ce_flags & ZEND_ACC_INTERFACE)) { + zend_error(E_ERROR, "Interface %s may not inherit from class (%s)", ce->name, parent_ce->name); + } + ce->parent = parent_ce; /* Inherit properties */ @@ -1693,6 +1721,14 @@ void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) do_inherit_parent_constructor(ce); } + +void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface) +{ + zend_hash_merge(&ce->constants_table, &iface->constants_table, (void (*)(void *)) zval_add_ref, NULL, sizeof(zval *), 0); + zend_hash_merge_ex(&ce->function_table, &iface->function_table, (copy_ctor_func_t) do_inherit_method, sizeof(zend_function), (merge_checker_func_t) do_inherit_method_check, ce); +} + + static void create_class(HashTable *class_table, char *name, int name_length, zend_class_entry **ce TSRMLS_DC) { zend_class_entry *new_class_entry; @@ -1716,7 +1752,7 @@ static void create_class(HashTable *class_table, char *name, int name_length, ze #include "../TSRM/tsrm_strtok_r.h" -static int create_nested_class(HashTable *class_table, char *path, zend_class_entry *new_ce TSRMLS_DC) +static zend_class_entry *create_nested_class(HashTable *class_table, char *path, zend_class_entry *new_ce TSRMLS_DC) { char *cur, *temp; char *last; @@ -1749,9 +1785,9 @@ static int create_nested_class(HashTable *class_table, char *path, zend_class_en if (zend_hash_add(&ce->class_table, last, strlen(last)+1, &new_ce, sizeof(zend_class_entry *), NULL) == FAILURE) { new_ce->refcount--; zend_error(E_COMPILE_ERROR, "Cannot redeclare class '%s' - class or namespace with this name already exist.", last); - return FAILURE; + return NULL; } - return SUCCESS; + return new_ce; } ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time) @@ -1786,13 +1822,13 @@ ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, HashTa } -ZEND_API int 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 *function_table, HashTable *class_table TSRMLS_DC) { zend_class_entry *ce, **pce; if (zend_hash_find(class_table, opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, (void **) &pce)==FAILURE) { zend_error(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", opline->op1.u.constant.value.str.val); - return FAILURE; + return NULL; } else { ce = *pce; } @@ -1803,13 +1839,13 @@ ZEND_API int do_bind_class(zend_op *opline, HashTable *function_table, HashTable 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); - return FAILURE; + return NULL; } else { - return SUCCESS; + return ce; } } -ZEND_API int 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 *function_table, HashTable *class_table, zend_class_entry *parent_ce TSRMLS_DC) { zend_class_entry *ce, **pce; int found_ce; @@ -1818,7 +1854,7 @@ ZEND_API int do_bind_inherited_class(zend_op *opline, HashTable *function_table, if (found_ce == FAILURE) { zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", (*pce)->name); - return FAILURE; + return NULL; } else { ce = *pce; } @@ -1840,9 +1876,9 @@ ZEND_API int do_bind_inherited_class(zend_op *opline, HashTable *function_table, zend_hash_destroy(&ce->properties_info); zend_hash_destroy(ce->static_members); zend_hash_destroy(&ce->constants_table); - return FAILURE; + return NULL; } - return SUCCESS; + return ce; } @@ -2157,9 +2193,13 @@ void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znod 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->type = ZEND_USER_CLASS; new_class_entry->parent = NULL; + new_class_entry->num_interfaces = 0; + zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC); + if (class_token->u.constant.value.lval == T_INTERFACE) { + new_class_entry->ce_flags |= ZEND_ACC_INTERFACE; + } if (parent_class_name->op_type != IS_UNUSED) { doing_inheritance = 1; @@ -2186,15 +2226,34 @@ void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znod 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 *), NULL); CG(active_class_entry) = new_class_entry; + + opline->result.u.var = get_temporary_variable(CG(active_op_array)); + opline->result.op_type = IS_CONST; + CG(implementing_class) = opline->result; } void zend_do_end_class_declaration(znode *class_token TSRMLS_DC) { do_inherit_parent_constructor(CG(active_class_entry)); + if (CG(active_class_entry)->num_interfaces > 0) { + CG(active_class_entry)->interfaces = (zend_class_entry **) emalloc(sizeof(zend_class_entry *)*CG(active_class_entry)->num_interfaces); + } CG(active_class_entry) = NULL; } + +void zend_do_implements_interface(znode *interface_znode TSRMLS_DC) +{ + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_ADD_INTERFACE; + opline->op1 = CG(implementing_class); + opline->op2 = *interface_znode; + opline->extended_value = CG(active_class_entry)->num_interfaces++; +} + + void mangle_property_name(char **dest, int *dest_length, char *src1, int src1_length, char *src2, int src2_length) { char *prop_name; @@ -2219,6 +2278,10 @@ void zend_do_declare_property(znode *var_name, znode *value, zend_uint access_ty HashTable *target_symbol_table; zend_bool free_var_name = 0; + if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { + zend_error(E_COMPILE_ERROR, "Interfaces may not include member variables"); + } + if (access_type & ZEND_ACC_ABSTRACT) { zend_error(E_COMPILE_ERROR, "Properties cannot be declared abstract"); } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 70f9ead01f..f24e14b8c8 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -92,6 +92,7 @@ typedef struct _zend_brk_cont_element { #define ZEND_ACC_STATIC 0x01 #define ZEND_ACC_ABSTRACT 0x02 #define ZEND_ACC_FINAL 0x04 +#define ZEND_ACC_INTERFACE 0x08 /* The order of those must be kept - public < protected < private */ #define ZEND_ACC_PUBLIC 0x10 @@ -308,7 +309,7 @@ void zend_do_add_string(znode *result, znode *op1, znode *op2 TSRMLS_DC); void zend_do_add_variable(znode *result, znode *op1, znode *op2 TSRMLS_DC); int zend_do_verify_access_types(znode *current_access_type, znode *new_modifier); -void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, zend_uint fn_flags TSRMLS_DC); +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC); void zend_do_end_function_declaration(znode *function_token TSRMLS_DC); void zend_do_receive_arg(zend_uchar op, znode *var, znode *offset, znode *initialization, zend_uchar pass_type TSRMLS_DC); int zend_do_begin_function_call(znode *function_name TSRMLS_DC); @@ -326,10 +327,12 @@ 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 int do_bind_class(zend_op *opline, HashTable *function_table, HashTable *class_table TSRMLS_DC); -ZEND_API int 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_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); +void zend_do_implements_interface(znode *interface_znode TSRMLS_DC); void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce); +void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface); void zend_do_early_binding(TSRMLS_D); void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC); @@ -658,6 +661,8 @@ int zendlex(znode *zendlval TSRMLS_DC); #define ZEND_START_NAMESPACE 143 +#define ZEND_ADD_INTERFACE 144 + /* end of block */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 44f9a43d00..39a7b01b3d 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -3113,7 +3113,14 @@ int zend_switch_free_handler(ZEND_OPCODE_HANDLER_ARGS) int zend_new_handler(ZEND_OPCODE_HANDLER_ARGS) { if (EX_T(EX(opline)->op1.u.var).EA.class_entry->ce_flags & ZEND_ACC_ABSTRACT) { - zend_error(E_ERROR, "Cannot instantiate abstract class %s", EX_T(EX(opline)->op1.u.var).EA.class_entry->name); + char *class_type; + + if (EX_T(EX(opline)->op1.u.var).EA.class_entry->ce_flags & ZEND_ACC_INTERFACE) { + class_type = "interface"; + } else { + class_type = "abstract_class"; + } + zend_error(E_ERROR, "Cannot instantiate %s %s", class_type, EX_T(EX(opline)->op1.u.var).EA.class_entry->name); } EX_T(EX(opline)->result.u.var).var.ptr_ptr = &EX_T(EX(opline)->result.u.var).var.ptr; ALLOC_ZVAL(EX_T(EX(opline)->result.u.var).var.ptr); @@ -3875,14 +3882,14 @@ int zend_ext_fcall_end_handler(ZEND_OPCODE_HANDLER_ARGS) int zend_declare_class_handler(ZEND_OPCODE_HANDLER_ARGS) { - do_bind_class(EX(opline), EG(function_table), EG(class_table) TSRMLS_CC); + EX_T(EX(opline)->result.u.var).EA.class_entry = do_bind_class(EX(opline), EG(function_table), EG(class_table) TSRMLS_CC); NEXT_OPCODE(); } int zend_declare_inherited_class_handler(ZEND_OPCODE_HANDLER_ARGS) { - do_bind_inherited_class(EX(opline), EG(function_table), EG(class_table), EX_T(EX(opline)->extended_value).EA.class_entry TSRMLS_CC); + EX_T(EX(opline)->result.u.var).EA.class_entry = do_bind_inherited_class(EX(opline), EG(function_table), EG(class_table), EX_T(EX(opline)->extended_value).EA.class_entry TSRMLS_CC); NEXT_OPCODE(); } @@ -3909,8 +3916,14 @@ int zend_ticks_handler(ZEND_OPCODE_HANDLER_ARGS) int zend_instanceof_handler(ZEND_OPCODE_HANDLER_ARGS) { zval *expr = get_zval_ptr(&EX(opline)->op1, EX(Ts), &EG(free_op1), BP_VAR_R); - instanceof_function(&EX_T(EX(opline)->result.u.var).tmp_var, expr, - EX_T(EX(opline)->op2.u.var).EA.class_entry TSRMLS_CC); + zend_bool result; + + if (Z_TYPE_P(expr) == IS_OBJECT) { + result = instanceof_function(Z_OBJCE_P(expr), EX_T(EX(opline)->op2.u.var).EA.class_entry TSRMLS_CC); + } else { + result = 0; + } + ZVAL_BOOL(&EX_T(EX(opline)->result.u.var).tmp_var, result); FREE_OP(EX(Ts), &EX(opline)->op1, EG(free_op1)); NEXT_OPCODE(); } @@ -3951,6 +3964,23 @@ int zend_start_namespace_handler(ZEND_OPCODE_HANDLER_ARGS) NEXT_OPCODE(); } + +int zend_add_interface_handler(ZEND_OPCODE_HANDLER_ARGS) +{ + zend_class_entry *ce = EX_T(EX(opline)->op1.u.var).EA.class_entry; + zend_class_entry *iface = EX_T(EX(opline)->op2.u.var).EA.class_entry; + + if (!(iface->ce_flags & ZEND_ACC_INTERFACE)) { + zend_error(E_ERROR, "%s cannot implement %s - it is not an interface", ce->name, iface->name); + } + + ce->interfaces[EX(opline)->extended_value] = iface; + + zend_do_implement_interface(ce, iface); + + NEXT_OPCODE(); +} + void zend_init_opcodes_handlers() { zend_opcode_handlers[ZEND_NOP] = zend_nop_handler; @@ -4126,6 +4156,8 @@ void zend_init_opcodes_handlers() zend_opcode_handlers[ZEND_RAISE_ABSTRACT_ERROR] = zend_raise_abstract_error_handler; zend_opcode_handlers[ZEND_START_NAMESPACE] = zend_start_namespace_handler; + + zend_opcode_handlers[ZEND_ADD_INTERFACE] = zend_add_interface_handler; } /* diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 016c935cef..ac335fc473 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -127,6 +127,7 @@ struct _zend_compiler_globals { zend_bool increment_lineno; zend_llist import_commands; + znode implementing_class; zend_uint access_type; }; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 7accb8e069..31506e11db 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -118,7 +118,9 @@ %token T_ISSET %token T_EMPTY %token T_CLASS +%token T_INTERFACE %token T_EXTENDS +%token T_IMPLEMENTS %token T_OBJECT_OPERATOR %token T_DOUBLE_ARROW %token T_LIST @@ -214,7 +216,7 @@ unticked_statement: | T_DECLARE { $1.u.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(&$1 TSRMLS_CC); } | ';' /* empty statement */ | T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}' - T_CATCH '(' catch_class_entry T_VARIABLE ')' { zend_do_begin_catch(&$1, &$8, &$9, 1 TSRMLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$1 TSRMLS_CC); } + T_CATCH '(' fully_qualified_class_name T_VARIABLE ')' { zend_do_begin_catch(&$1, &$8, &$9, 1 TSRMLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$1 TSRMLS_CC); } additional_catches | T_THROW expr ';' { zend_do_throw(&$2 TSRMLS_CC); } | T_IMPORT { zend_do_begin_import(TSRMLS_C); } import_rule T_FROM import_namespace { zend_do_end_import(&$5 TSRMLS_CC); } ';' @@ -246,8 +248,8 @@ additional_catches: ; non_empty_additional_catches: - non_empty_additional_catches T_CATCH '(' catch_class_entry T_VARIABLE ')' { zend_do_begin_catch(&$2, &$4, &$5, 0 TSRMLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$2 TSRMLS_CC); } - | T_CATCH '(' catch_class_entry T_VARIABLE ')' { zend_do_begin_catch(&$1, &$3, &$4, 0 TSRMLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$1 TSRMLS_CC); } + non_empty_additional_catches T_CATCH '(' fully_qualified_class_name T_VARIABLE ')' { zend_do_begin_catch(&$2, &$4, &$5, 0 TSRMLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$2 TSRMLS_CC); } + | T_CATCH '(' fully_qualified_class_name T_VARIABLE ')' { zend_do_begin_catch(&$1, &$3, &$4, 0 TSRMLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$1 TSRMLS_CC); } ; @@ -282,12 +284,23 @@ is_reference: unticked_function_declaration_statement: - T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type, 0 TSRMLS_CC); } + T_FUNCTION { $1.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $3.op_type, NULL TSRMLS_CC); } '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } ; unticked_class_declaration_statement: - T_CLASS T_STRING extends_from '{' { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } class_statement_list '}' { zend_do_end_class_declaration(&$1 TSRMLS_CC); } + class_entry_type T_STRING extends_from + { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } + implements_list + '{' + class_statement_list + '}' { zend_do_end_class_declaration(&$1 TSRMLS_CC); } +; + + +class_entry_type: + T_CLASS { $$.u.constant.value.lval = T_CLASS; } + | T_INTERFACE { $$.u.constant.value.lval = T_INTERFACE; } ; namespace_declaration_statement: @@ -324,12 +337,17 @@ namespace_const_declaration: extends_from: /* empty */ { $$.op_type = IS_UNUSED; } - | T_EXTENDS catch_class_entry { $$ = $2; } + | T_EXTENDS fully_qualified_class_name { $$ = $2; } +; + +implements_list: + /* empty */ + | T_IMPLEMENTS interface_list ; -declaration_class_name: - namespace_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { do_fetch_class_name(&$$, &$1, &$3, 0 TSRMLS_CC); } - | +interface_list: + fully_qualified_class_name { zend_do_implements_interface(&$1 TSRMLS_CC); } + | interface_list ',' fully_qualified_class_name { zend_do_implements_interface(&$3 TSRMLS_CC); } ; foreach_optional_arg: @@ -479,7 +497,7 @@ class_statement_list: class_statement: variable_modifiers { CG(access_type) = $1.u.constant.value.lval; } class_variable_declaration ';' | class_constant_declaration ';' - | method_modifiers T_FUNCTION { $2.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$5, 1, $4.op_type, $1.u.constant.value.lval TSRMLS_CC); } '(' + | method_modifiers T_FUNCTION { $2.u.opline_num = CG(zend_lineno); } is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$5, 1, $4.op_type, &$1 TSRMLS_CC); } '(' parameter_list ')' method_body { zend_do_abstract_method(&$5, &$1, &$10 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } ; @@ -629,7 +647,7 @@ parse_class_entry: | T_PAAMAYIM_NEKUDOTAYIM { do_fetch_class(&$$, NULL, NULL TSRMLS_CC); } ; -catch_class_entry: +fully_qualified_class_name: namespace_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { do_fetch_class(&$$, &$1, &$3 TSRMLS_CC); } | T_STRING { do_fetch_class(&$$, NULL, &$1 TSRMLS_CC); } ; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index e062689b97..61025d507d 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -614,10 +614,18 @@ NAMESPACE_NAME ({LABEL}":")+{LABEL} return T_CLASS; } +"interface" { + return T_INTERFACE; +} + "extends" { return T_EXTENDS; } +"implements" { + return T_IMPLEMENTS; +} + "namespace" { return T_NAMESPACE; } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 334788a908..2611996d91 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -154,6 +154,9 @@ ZEND_API void destroy_zend_class(zend_class_entry **pce) FREE_HASHTABLE(ce->static_members); zend_hash_destroy(&ce->constants_table); zend_hash_destroy(&ce->class_table); + if (ce->num_interfaces > 0) { + efree(ce->interfaces); + } efree(ce); break; case ZEND_INTERNAL_CLASS: diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 5374bfb4e7..5a216afc93 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1362,19 +1362,24 @@ ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 TSR } -ZEND_API int instanceof_function(zval *result, zval *op1, zend_class_entry *class TSRMLS_DC) +ZEND_API zend_bool instanceof_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC) { - if (Z_TYPE_P(op1) == IS_OBJECT) { - zend_class_entry *ce; - for (ce = Z_OBJCE_P(op1); ce != NULL; ce = ce->parent) { - if (ce == class) { - ZVAL_BOOL(result, 1); - return SUCCESS; + zend_uint i; + + while (instance_ce) { + if (instance_ce == ce) { + return 1; + } + for (i=0; inum_interfaces; i++) { + + if (instanceof_function(instance_ce->interfaces[i], ce TSRMLS_CC)) { + return 1; } } + instance_ce = instance_ce->parent; } - ZVAL_BOOL(result, 0); - return SUCCESS; + + return 0; } #define LOWER_CASE 1 diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 9320ab9b21..b5edcdfb85 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -59,7 +59,7 @@ ZEND_API int is_not_equal_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) ZEND_API int is_smaller_function(zval *result, zval *op1, zval *op2 TSRMLS_DC); ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 TSRMLS_DC); -ZEND_API int instanceof_function(zval *result, zval *op1, zend_class_entry *ce TSRMLS_DC); +ZEND_API zend_bool instanceof_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC); static inline zend_bool is_numeric_string(char *str, int length, long *lval, double *dval, zend_bool allow_errors) {