version 1.1.1.2, 2012/05/29 12:34:35
|
version 1.1.1.3, 2013/07/22 01:32:15
|
Line 2
|
Line 2
|
+----------------------------------------------------------------------+ |
+----------------------------------------------------------------------+ |
| Zend Engine | |
| Zend Engine | |
+----------------------------------------------------------------------+ |
+----------------------------------------------------------------------+ |
| Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) | | | Copyright (c) 1998-2013 Zend Technologies Ltd. (http://www.zend.com) | |
+----------------------------------------------------------------------+ |
+----------------------------------------------------------------------+ |
| This source file is subject to version 2.00 of the Zend license, | |
| This source file is subject to version 2.00 of the Zend license, | |
| that is bundled with this package in the file LICENSE, and is | |
| that is bundled with this package in the file LICENSE, and is | |
Line 219 ZEND_API void file_handle_dtor(zend_file_handle *fh) /
|
Line 219 ZEND_API void file_handle_dtor(zend_file_handle *fh) /
|
void init_compiler(TSRMLS_D) /* {{{ */ |
void init_compiler(TSRMLS_D) /* {{{ */ |
{ |
{ |
CG(active_op_array) = NULL; |
CG(active_op_array) = NULL; |
|
memset(&CG(context), 0, sizeof(CG(context))); |
zend_init_compiler_data_structures(TSRMLS_C); |
zend_init_compiler_data_structures(TSRMLS_C); |
zend_init_rsrc_list(TSRMLS_C); |
zend_init_rsrc_list(TSRMLS_C); |
zend_hash_init(&CG(filenames_table), 5, NULL, (dtor_func_t) free_estring, 0); |
zend_hash_init(&CG(filenames_table), 5, NULL, (dtor_func_t) free_estring, 0); |
Line 1685 void zend_do_begin_function_declaration(znode *functio
|
Line 1686 void zend_do_begin_function_declaration(znode *functio
|
zval key; |
zval key; |
|
|
if (CG(current_namespace)) { |
if (CG(current_namespace)) { |
/* Prefix function name with current namespcae name */ | /* Prefix function name with current namespace name */ |
znode tmp; |
znode tmp; |
|
|
tmp.u.constant = *CG(current_namespace); |
tmp.u.constant = *CG(current_namespace); |
Line 1797 void zend_do_end_function_declaration(const znode *fun
|
Line 1798 void zend_do_end_function_declaration(const znode *fun
|
zend_do_return(NULL, 0 TSRMLS_CC); |
zend_do_return(NULL, 0 TSRMLS_CC); |
|
|
pass_two(CG(active_op_array) TSRMLS_CC); |
pass_two(CG(active_op_array) TSRMLS_CC); |
zend_release_labels(TSRMLS_C); | zend_release_labels(0 TSRMLS_CC); |
|
|
if (CG(active_class_entry)) { |
if (CG(active_class_entry)) { |
zend_check_magic_method_implementation(CG(active_class_entry), (zend_function*)CG(active_op_array), E_COMPILE_ERROR TSRMLS_CC); |
zend_check_magic_method_implementation(CG(active_class_entry), (zend_function*)CG(active_op_array), E_COMPILE_ERROR TSRMLS_CC); |
Line 2319 void zend_do_goto(const znode *label TSRMLS_DC) /* {{{
|
Line 2320 void zend_do_goto(const znode *label TSRMLS_DC) /* {{{
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
void zend_release_labels(TSRMLS_D) /* {{{ */ | void zend_release_labels(int temporary TSRMLS_DC) /* {{{ */ |
{ |
{ |
if (CG(context).labels) { |
if (CG(context).labels) { |
zend_hash_destroy(CG(context).labels); |
zend_hash_destroy(CG(context).labels); |
FREE_HASHTABLE(CG(context).labels); |
FREE_HASHTABLE(CG(context).labels); |
|
CG(context).labels = NULL; |
} |
} |
if (!zend_stack_is_empty(&CG(context_stack))) { | if (!temporary && !zend_stack_is_empty(&CG(context_stack))) { |
zend_compiler_context *ctx; |
zend_compiler_context *ctx; |
|
|
zend_stack_top(&CG(context_stack), (void**)&ctx); |
zend_stack_top(&CG(context_stack), (void**)&ctx); |
Line 2935 static zend_bool zend_do_perform_implementation_check(
|
Line 2937 static zend_bool zend_do_perform_implementation_check(
|
return 1; |
return 1; |
} |
} |
|
|
|
/* If both methods are private do not enforce a signature */ |
|
if ((fe->common.fn_flags & ZEND_ACC_PRIVATE) && (proto->common.fn_flags & ZEND_ACC_PRIVATE)) { |
|
return 1; |
|
} |
|
|
/* check number of arguments */ |
/* check number of arguments */ |
if (proto->common.required_num_args < fe->common.required_num_args |
if (proto->common.required_num_args < fe->common.required_num_args |
|| proto->common.num_args > fe->common.num_args) { |
|| proto->common.num_args > fe->common.num_args) { |
Line 3264 static void do_inheritance_check_on_method(zend_functi
|
Line 3271 static void do_inheritance_check_on_method(zend_functi
|
} |
} |
} else if (EG(error_reporting) & E_STRICT || EG(user_error_handler)) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */ |
} else if (EG(error_reporting) & E_STRICT || EG(user_error_handler)) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */ |
if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) { |
if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) { |
char *method_prototype = zend_get_function_declaration(child->common.prototype TSRMLS_CC); | char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC); |
zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype); |
zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype); |
efree(method_prototype); |
efree(method_prototype); |
} |
} |
Line 3327 static zend_bool do_inherit_property_access_check(Hash
|
Line 3334 static zend_bool do_inherit_property_access_check(Hash
|
if ((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK)) { |
if ((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK)) { |
zend_error(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ce->name, hash_key->arKey, zend_visibility_string(parent_info->flags), parent_ce->name, (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); |
zend_error(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ce->name, hash_key->arKey, zend_visibility_string(parent_info->flags), parent_ce->name, (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); |
} else if ((child_info->flags & ZEND_ACC_STATIC) == 0) { |
} else if ((child_info->flags & ZEND_ACC_STATIC) == 0) { |
Z_DELREF_P(ce->default_properties_table[parent_info->offset]); | zval_ptr_dtor(&(ce->default_properties_table[parent_info->offset])); |
ce->default_properties_table[parent_info->offset] = ce->default_properties_table[child_info->offset]; |
ce->default_properties_table[parent_info->offset] = ce->default_properties_table[child_info->offset]; |
ce->default_properties_table[child_info->offset] = NULL; |
ce->default_properties_table[child_info->offset] = NULL; |
child_info->offset = parent_info->offset; |
child_info->offset = parent_info->offset; |
Line 3521 ZEND_API void zend_do_inheritance(zend_class_entry *ce
|
Line 3528 ZEND_API void zend_do_inheritance(zend_class_entry *ce
|
|
|
if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) { |
if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) { |
ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; |
ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; |
} else if (!(ce->ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES) | } else if (!(ce->ce_flags & (ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) { |
&& !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { | |
/* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */ |
/* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */ |
zend_verify_abstract_class(ce TSRMLS_CC); |
zend_verify_abstract_class(ce TSRMLS_CC); |
} |
} |
Line 3629 static zend_bool zend_traits_method_compatibility_chec
|
Line 3635 static zend_bool zend_traits_method_compatibility_chec
|
zend_uint other_flags = other_fn->common.scope->ce_flags; |
zend_uint other_flags = other_fn->common.scope->ce_flags; |
|
|
return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC) |
return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC) |
&& zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC) | && ((other_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) || zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC)) |
&& ((fn_flags & ZEND_ACC_FINAL) == (other_flags & ZEND_ACC_FINAL)) /* equal final qualifier */ | && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == |
&& ((fn_flags & ZEND_ACC_STATIC)== (other_flags & ZEND_ACC_STATIC)); /* equal static qualifier */ | (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */ |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
static int zend_traits_merge_functions(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
|
{ |
|
size_t current; |
|
size_t i; |
|
size_t count; |
|
HashTable* resulting_table; |
|
HashTable** function_tables; |
|
zend_class_entry *ce; |
|
size_t collision = 0; |
|
size_t abstract_solved = 0; |
|
zend_function* other_trait_fn; |
|
|
|
current = va_arg(args, size_t); /* index of current trait */ |
|
count = va_arg(args, size_t); |
|
resulting_table = va_arg(args, HashTable*); |
|
function_tables = va_arg(args, HashTable**); |
|
ce = va_arg(args, zend_class_entry*); |
|
|
|
for (i = 0; i < count; i++) { |
|
if (i == current) { |
|
continue; /* just skip this, cause its the table this function is applied on */ |
|
} |
|
|
|
if (zend_hash_quick_find(function_tables[i], hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **)&other_trait_fn) == SUCCESS) { |
|
/* if it is an abstract method, there is no collision */ |
|
if (other_trait_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
|
/* Make sure they are compatible */ |
|
/* In case both are abstract, just check prototype, but need to do that in both directions */ |
|
if (!zend_traits_method_compatibility_check(fn, other_trait_fn TSRMLS_CC)) { |
|
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", |
|
zend_get_function_declaration(fn TSRMLS_CC), |
|
zend_get_function_declaration(other_trait_fn TSRMLS_CC)); |
|
} |
|
|
|
/* we can savely free and remove it from other table */ |
|
zend_function_dtor(other_trait_fn); |
|
zend_hash_quick_del(function_tables[i], hash_key->arKey, hash_key->nKeyLength, hash_key->h); |
|
} else { |
|
/* if it is not an abstract method, there is still no collision */ |
|
/* if fn is an abstract method */ |
|
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
|
/* Make sure they are compatible. |
|
Here, we already know other_trait_fn cannot be abstract, full check ok. */ |
|
if (!zend_traits_method_compatibility_check(fn, other_trait_fn TSRMLS_CC)) { |
|
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", |
|
zend_get_function_declaration(fn TSRMLS_CC), |
|
zend_get_function_declaration(other_trait_fn TSRMLS_CC)); |
|
} |
|
|
|
/* just mark as solved, will be added if its own trait is processed */ |
|
abstract_solved = 1; |
|
} else { |
|
/* but else, we have a collision of non-abstract methods */ |
|
collision++; |
|
zend_function_dtor(other_trait_fn); |
|
zend_hash_quick_del(function_tables[i], hash_key->arKey, hash_key->nKeyLength, hash_key->h); |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (collision) { |
|
zend_function* class_fn; |
|
|
|
/* make sure method is not already overridden in class */ |
|
if (zend_hash_quick_find(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **)&class_fn) == FAILURE |
|
|| class_fn->common.scope != ce) { |
|
zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s", fn->common.function_name, ce->name); |
|
} |
|
|
|
zend_function_dtor(fn); |
|
} else if (abstract_solved) { |
|
zend_function_dtor(fn); |
|
} else { |
|
/* Add it to result function table */ |
|
if (zend_hash_quick_add(resulting_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, fn, sizeof(zend_function), NULL)==FAILURE) { |
|
zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occured during updating resulting trait method table", fn->common.function_name); |
|
} |
|
} |
|
|
|
return ZEND_HASH_APPLY_REMOVE; |
|
} |
|
/* }}} */ |
|
|
|
static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint mname_len, zend_function* fe TSRMLS_DC) /* {{{ */ |
static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint mname_len, zend_function* fe TSRMLS_DC) /* {{{ */ |
{ |
{ |
if (!strncmp(mname, ZEND_CLONE_FUNC_NAME, mname_len)) { |
if (!strncmp(mname, ZEND_CLONE_FUNC_NAME, mname_len)) { |
Line 3760 static void zend_add_magic_methods(zend_class_entry* c
|
Line 3682 static void zend_add_magic_methods(zend_class_entry* c
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
static int zend_traits_merge_functions_to_class(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ | static void zend_add_trait_method(zend_class_entry *ce, const char *name, const char *arKey, uint nKeyLength, zend_function *fn, HashTable **overriden TSRMLS_DC) /* {{{ */ |
{ |
{ |
zend_class_entry *ce = va_arg(args, zend_class_entry*); | zend_function *existing_fn = NULL; |
zend_function* existing_fn = NULL; | ulong h = zend_hash_func(arKey, nKeyLength); |
zend_function fn_copy, *fn_copy_p; | |
zend_function* prototype = NULL; /* is used to determine the prototype according to the inheritance chain */ | |
|
|
if (zend_hash_quick_find(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &existing_fn) == FAILURE || | if (zend_hash_quick_find(&ce->function_table, arKey, nKeyLength, h, (void**) &existing_fn) == SUCCESS) { |
existing_fn->common.scope != ce) { | if (existing_fn->common.scope == ce) { |
/* not found or inherited from other class or interface */ | /* members from the current class override trait methods */ |
zend_function* parent_function; | /* use temporary *overriden HashTable to detect hidden conflict */ |
| if (*overriden) { |
if (ce->parent && zend_hash_quick_find(&ce->parent->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &parent_function) != FAILURE) { | if (zend_hash_quick_find(*overriden, arKey, nKeyLength, h, (void**) &existing_fn) == SUCCESS) { |
prototype = parent_function; /* ->common.fn_flags |= ZEND_ACC_ABSTRACT; */ | if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
| /* Make sure the trait method is compatible with previosly declared abstract method */ |
/* we got that method in the parent class, and are going to override it, | if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) { |
except, if the trait is just asking to have an abstract method implemented. */ | zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", |
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { | zend_get_function_declaration(fn TSRMLS_CC), |
/* then we clean up an skip this method */ | zend_get_function_declaration(existing_fn TSRMLS_CC)); |
zend_function_dtor(fn); | } |
return ZEND_HASH_APPLY_REMOVE; | } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
| /* Make sure the abstract declaration is compatible with previous declaration */ |
| if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) { |
| zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", |
| zend_get_function_declaration(fn TSRMLS_CC), |
| zend_get_function_declaration(existing_fn TSRMLS_CC)); |
| } |
| return; |
| } |
| } |
| } else { |
| ALLOC_HASHTABLE(*overriden); |
| zend_hash_init_ex(*overriden, 2, NULL, NULL, 0, 0); |
} |
} |
} | zend_hash_quick_update(*overriden, arKey, nKeyLength, h, fn, sizeof(zend_function), (void**)&fn); |
| return; |
fn->common.scope = ce; | } else if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
fn->common.prototype = prototype; | /* Make sure the trait method is compatible with previosly declared abstract method */ |
| if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) { |
if (prototype | zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", |
&& (prototype->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT | zend_get_function_declaration(fn TSRMLS_CC), |
|| prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { | zend_get_function_declaration(existing_fn TSRMLS_CC)); |
fn->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; | } |
} else if (fn->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT) { | } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
/* remove ZEND_ACC_IMPLEMENTED_ABSTRACT flag, think it shouldn't be copied to class */ | /* Make sure the abstract declaration is compatible with previous declaration */ |
fn->common.fn_flags = fn->common.fn_flags - ZEND_ACC_IMPLEMENTED_ABSTRACT; | if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) { |
} | zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", |
| zend_get_function_declaration(fn TSRMLS_CC), |
/* check whether the trait method fullfills the inheritance requirements */ | zend_get_function_declaration(existing_fn TSRMLS_CC)); |
if (prototype) { | } |
do_inheritance_check_on_method(fn, prototype TSRMLS_CC); | return; |
} | } else if ((existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { |
/* one more thing: make sure we properly implement an abstract method */ | /* two trais can't define the same non-abstract method */ |
if (existing_fn && existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { | #if 1 |
| zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s", |
| name, ce->name); |
| #else /* TODO: better errot message */ |
| zend_error(E_COMPILE_ERROR, "Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s", |
| fn->common.scope->name, fn->common.function_name, |
| ce->name, name, |
| existing_fn->common.scope->name, existing_fn->common.function_name); |
| #endif |
| } else { |
| /* inherited members are overridden by members inserted by traits */ |
| /* check whether the trait method fulfills the inheritance requirements */ |
do_inheritance_check_on_method(fn, existing_fn TSRMLS_CC); |
do_inheritance_check_on_method(fn, existing_fn TSRMLS_CC); |
} |
} |
|
} |
|
|
/* delete inherited fn if the function to be added is not abstract */ | function_add_ref(fn); |
if (existing_fn | zend_hash_quick_update(&ce->function_table, arKey, nKeyLength, h, fn, sizeof(zend_function), (void**)&fn); |
&& existing_fn->common.scope != ce | zend_add_magic_methods(ce, arKey, nKeyLength, fn TSRMLS_CC); |
&& (fn->common.fn_flags & ZEND_ACC_ABSTRACT) == 0) { | } |
/* it is just a reference which was added to the subclass while doing | /* }}} */ |
the inheritance, so we can deleted now, and will add the overriding | |
method afterwards. | |
Except, if we try to add an abstract function, then we should not | |
delete the inherited one */ | |
zend_hash_quick_del(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h); | |
} | |
|
|
|
static int zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce TSRMLS_DC) /* {{{ */ |
|
{ |
|
if ((fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { |
|
|
|
fn->common.scope = ce; |
|
|
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { |
ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; |
ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; |
} |
} |
if (fn->op_array.static_variables) { |
if (fn->op_array.static_variables) { |
ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS; |
ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS; |
} |
} |
fn_copy = *fn; |
|
function_add_ref(&fn_copy); |
|
|
|
if (zend_hash_quick_update(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &fn_copy, sizeof(zend_function), (void**)&fn_copy_p)==FAILURE) { |
|
zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occured during updating class method table", hash_key->arKey); |
|
} |
|
|
|
zend_add_magic_methods(ce, hash_key->arKey, hash_key->nKeyLength, fn_copy_p TSRMLS_CC); |
|
|
|
zend_function_dtor(fn); |
|
} else { |
|
zend_function_dtor(fn); |
|
} |
} |
| return ZEND_HASH_APPLY_KEEP; |
return ZEND_HASH_APPLY_REMOVE; | |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ |
{ |
{ |
HashTable* target; | zend_class_entry *ce; |
zend_trait_alias** aliases; | HashTable **overriden; |
HashTable* exclude_table; | zend_trait_alias *alias, **alias_ptr; |
char* lcname; | HashTable *exclude_table; |
unsigned int fnname_len; | char *lcname; |
zend_function fn_copy; | unsigned int fnname_len; |
void* dummy; | zend_function fn_copy; |
size_t i = 0; | void *dummy; |
|
|
target = va_arg(args, HashTable*); | ce = va_arg(args, zend_class_entry*); |
aliases = va_arg(args, zend_trait_alias**); | overriden = va_arg(args, HashTable**); |
exclude_table = va_arg(args, HashTable*); |
exclude_table = va_arg(args, HashTable*); |
|
|
|
fnname_len = hash_key->nKeyLength - 1; |
|
|
fnname_len = strlen(fn->common.function_name); |
|
|
|
/* apply aliases which are qualified with a class name, there should not be any ambiguity */ |
/* apply aliases which are qualified with a class name, there should not be any ambiguity */ |
if (aliases) { | if (ce->trait_aliases) { |
while (aliases[i]) { | alias_ptr = ce->trait_aliases; |
| alias = *alias_ptr; |
| while (alias) { |
/* Scope unset or equal to the function we compare to, and the alias applies to fn */ |
/* Scope unset or equal to the function we compare to, and the alias applies to fn */ |
if (aliases[i]->alias != NULL | if (alias->alias != NULL |
&& (!aliases[i]->trait_method->ce || fn->common.scope == aliases[i]->trait_method->ce) | && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce) |
&& aliases[i]->trait_method->mname_len == fnname_len | && alias->trait_method->mname_len == fnname_len |
&& (zend_binary_strcasecmp(aliases[i]->trait_method->method_name, aliases[i]->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) { | && (zend_binary_strcasecmp(alias->trait_method->method_name, alias->trait_method->mname_len, hash_key->arKey, fnname_len) == 0)) { |
fn_copy = *fn; |
fn_copy = *fn; |
function_add_ref(&fn_copy); |
|
/* this function_name is never destroyed, because its refcount |
|
greater than 1, classes are always destoyed in reverse order |
|
and trait is declared early than this class */ |
|
fn_copy.common.function_name = aliases[i]->alias; |
|
|
|
/* if it is 0, no modifieres has been changed */ |
/* if it is 0, no modifieres has been changed */ |
if (aliases[i]->modifiers) { | if (alias->modifiers) { |
fn_copy.common.fn_flags = aliases[i]->modifiers; | fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK)); |
if (!(aliases[i]->modifiers & ZEND_ACC_PPP_MASK)) { | |
fn_copy.common.fn_flags |= ZEND_ACC_PUBLIC; | |
} | |
fn_copy.common.fn_flags |= fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK); | |
} |
} |
|
|
lcname = zend_str_tolower_dup(aliases[i]->alias, aliases[i]->alias_len); | lcname = zend_str_tolower_dup(alias->alias, alias->alias_len); |
| zend_add_trait_method(ce, alias->alias, lcname, alias->alias_len+1, &fn_copy, overriden TSRMLS_CC); |
if (zend_hash_add(target, lcname, aliases[i]->alias_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) { | |
zend_error(E_COMPILE_ERROR, "Failed to add aliased trait method (%s) to the trait table. There is probably already a trait method with the same name", fn_copy.common.function_name); | |
} | |
efree(lcname); |
efree(lcname); |
|
|
/** Record the trait from which this alias was resolved. */ | /* Record the trait from which this alias was resolved. */ |
if (!aliases[i]->trait_method->ce) { | if (!alias->trait_method->ce) { |
aliases[i]->trait_method->ce = fn->common.scope; | alias->trait_method->ce = fn->common.scope; |
} |
} |
} |
} |
i++; | alias_ptr++; |
| alias = *alias_ptr; |
} |
} |
} |
} |
|
|
lcname = zend_str_tolower_dup(fn->common.function_name, fnname_len); | lcname = hash_key->arKey; |
|
|
if (exclude_table == NULL || zend_hash_find(exclude_table, lcname, fnname_len, &dummy) == FAILURE) { |
if (exclude_table == NULL || zend_hash_find(exclude_table, lcname, fnname_len, &dummy) == FAILURE) { |
/* is not in hashtable, thus, function is not to be excluded */ |
/* is not in hashtable, thus, function is not to be excluded */ |
fn_copy = *fn; |
fn_copy = *fn; |
function_add_ref(&fn_copy); |
|
|
|
/* apply aliases which are not qualified by a class name, or which have not | /* apply aliases which have not alias name, just setting visibility */ |
* alias name, just setting visibility */ | if (ce->trait_aliases) { |
if (aliases) { | alias_ptr = ce->trait_aliases; |
i = 0; | alias = *alias_ptr; |
while (aliases[i]) { | while (alias) { |
/* Scope unset or equal to the function we compare to, and the alias applies to fn */ |
/* Scope unset or equal to the function we compare to, and the alias applies to fn */ |
if (aliases[i]->alias == NULL && aliases[i]->modifiers != 0 | if (alias->alias == NULL && alias->modifiers != 0 |
&& (!aliases[i]->trait_method->ce || fn->common.scope == aliases[i]->trait_method->ce) | && (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce) |
&& (aliases[i]->trait_method->mname_len == fnname_len) | && (alias->trait_method->mname_len == fnname_len) |
&& (zend_binary_strcasecmp(aliases[i]->trait_method->method_name, aliases[i]->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) { | && (zend_binary_strcasecmp(alias->trait_method->method_name, alias->trait_method->mname_len, lcname, fnname_len) == 0)) { |
fn_copy.common.fn_flags = aliases[i]->modifiers; | |
|
|
if (!(aliases[i]->modifiers & ZEND_ACC_PPP_MASK)) { | fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK)); |
fn_copy.common.fn_flags |= ZEND_ACC_PUBLIC; | |
} | |
fn_copy.common.fn_flags |= fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK); | |
|
|
/** Record the trait from which this alias was resolved. */ |
/** Record the trait from which this alias was resolved. */ |
if (!aliases[i]->trait_method->ce) { | if (!alias->trait_method->ce) { |
aliases[i]->trait_method->ce = fn->common.scope; | alias->trait_method->ce = fn->common.scope; |
} |
} |
} |
} |
i++; | alias_ptr++; |
| alias = *alias_ptr; |
} |
} |
} |
} |
|
|
if (zend_hash_add(target, lcname, fnname_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) { | zend_add_trait_method(ce, fn->common.function_name, lcname, fnname_len+1, &fn_copy, overriden TSRMLS_CC); |
zend_error(E_COMPILE_ERROR, "Failed to add trait method (%s) to the trait table. There is probably already a trait method with the same name", fn_copy.common.function_name); | |
} | |
} |
} |
|
|
efree(lcname); |
|
|
|
return ZEND_HASH_APPLY_KEEP; |
return ZEND_HASH_APPLY_KEEP; |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
/* Copies function table entries to target function table with applied aliasing */ | static void zend_check_trait_usage(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */ |
static void zend_traits_copy_trait_function_table(HashTable *target, HashTable *source, zend_trait_alias** aliases, HashTable* exclude_table TSRMLS_DC) /* {{{ */ | |
{ |
{ |
zend_hash_apply_with_arguments(source TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, target, aliases, exclude_table); | zend_uint i; |
| |
| if ((trait->ce_flags & ZEND_ACC_TRAIT) != ZEND_ACC_TRAIT) { |
| zend_error(E_COMPILE_ERROR, "Class %s is not a trait, Only traits may be used in 'as' and 'insteadof' statements", trait->name); |
| } |
| |
| for (i = 0; i < ce->num_traits; i++) { |
| if (ce->traits[i] == trait) { |
| return; |
| } |
| } |
| zend_error(E_COMPILE_ERROR, "Required Trait %s wasn't added to %s", trait->name, ce->name); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 3965 static void zend_traits_init_trait_structures(zend_cla
|
Line 3888 static void zend_traits_init_trait_structures(zend_cla
|
/** Resolve classes for all precedence operations. */ |
/** Resolve classes for all precedence operations. */ |
if (cur_precedence->exclude_from_classes) { |
if (cur_precedence->exclude_from_classes) { |
cur_method_ref = cur_precedence->trait_method; |
cur_method_ref = cur_precedence->trait_method; |
cur_precedence->trait_method->ce = zend_fetch_class(cur_method_ref->class_name, | if (!(cur_precedence->trait_method->ce = zend_fetch_class(cur_method_ref->class_name, cur_method_ref->cname_len, |
cur_method_ref->cname_len, ZEND_FETCH_CLASS_TRAIT TSRMLS_CC); | ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { |
| zend_error(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name); |
| } |
| zend_check_trait_usage(ce, cur_precedence->trait_method->ce TSRMLS_CC); |
|
|
/** Ensure that the prefered method is actually available. */ |
/** Ensure that the prefered method is actually available. */ |
lcname = zend_str_tolower_dup(cur_method_ref->method_name, |
lcname = zend_str_tolower_dup(cur_method_ref->method_name, |
Line 3993 static void zend_traits_init_trait_structures(zend_cla
|
Line 3919 static void zend_traits_init_trait_structures(zend_cla
|
char* class_name = (char*)cur_precedence->exclude_from_classes[j]; |
char* class_name = (char*)cur_precedence->exclude_from_classes[j]; |
zend_uint name_length = strlen(class_name); |
zend_uint name_length = strlen(class_name); |
|
|
cur_precedence->exclude_from_classes[j] = zend_fetch_class(class_name, name_length, ZEND_FETCH_CLASS_TRAIT TSRMLS_CC); | if (!(cur_precedence->exclude_from_classes[j] = zend_fetch_class(class_name, name_length, ZEND_FETCH_CLASS_TRAIT |ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { |
| zend_error(E_COMPILE_ERROR, "Could not find trait %s", class_name); |
| } |
| zend_check_trait_usage(ce, cur_precedence->exclude_from_classes[j] TSRMLS_CC); |
| |
/* make sure that the trait method is not from a class mentioned in |
/* make sure that the trait method is not from a class mentioned in |
exclude_from_classes, for consistency */ |
exclude_from_classes, for consistency */ |
if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i]) { |
if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i]) { |
Line 4020 static void zend_traits_init_trait_structures(zend_cla
|
Line 3949 static void zend_traits_init_trait_structures(zend_cla
|
/** For all aliases with an explicit class name, resolve the class now. */ |
/** For all aliases with an explicit class name, resolve the class now. */ |
if (ce->trait_aliases[i]->trait_method->class_name) { |
if (ce->trait_aliases[i]->trait_method->class_name) { |
cur_method_ref = ce->trait_aliases[i]->trait_method; |
cur_method_ref = ce->trait_aliases[i]->trait_method; |
cur_method_ref->ce = zend_fetch_class(cur_method_ref->class_name, cur_method_ref->cname_len, ZEND_FETCH_CLASS_TRAIT TSRMLS_CC); | if (!(cur_method_ref->ce = zend_fetch_class(cur_method_ref->class_name, cur_method_ref->cname_len, ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) { |
| zend_error(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name); |
| } |
| zend_check_trait_usage(ce, cur_method_ref->ce TSRMLS_CC); |
|
|
/** And, ensure that the referenced method is resolvable, too. */ |
/** And, ensure that the referenced method is resolvable, too. */ |
lcname = zend_str_tolower_dup(cur_method_ref->method_name, |
lcname = zend_str_tolower_dup(cur_method_ref->method_name, |
cur_method_ref->mname_len); | cur_method_ref->mname_len); |
method_exists = zend_hash_exists(&cur_method_ref->ce->function_table, |
method_exists = zend_hash_exists(&cur_method_ref->ce->function_table, |
lcname, cur_method_ref->mname_len + 1); | lcname, cur_method_ref->mname_len + 1); |
efree(lcname); |
efree(lcname); |
|
|
if (!method_exists) { |
if (!method_exists) { |
Line 4070 static void zend_traits_compile_exclude_table(HashTabl
|
Line 4002 static void zend_traits_compile_exclude_table(HashTabl
|
|
|
static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */ |
static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */ |
{ |
{ |
HashTable** function_tables; | zend_uint i; |
HashTable* resulting_table; | HashTable *overriden = NULL; |
HashTable exclude_table; | |
size_t i; | |
|
|
/* prepare copies of trait function tables for combination */ |
|
function_tables = malloc(sizeof(HashTable*) * ce->num_traits); |
|
resulting_table = (HashTable *) malloc(sizeof(HashTable)); |
|
|
|
/* TODO: revisit this start size, may be its not optimal */ |
|
zend_hash_init_ex(resulting_table, 10, NULL, NULL, 1, 0); |
|
|
|
for (i = 0; i < ce->num_traits; i++) { |
for (i = 0; i < ce->num_traits; i++) { |
function_tables[i] = (HashTable *) malloc(sizeof(HashTable)); |
|
zend_hash_init_ex(function_tables[i], ce->traits[i]->function_table.nNumOfElements, NULL, NULL, 1, 0); |
|
|
|
if (ce->trait_precedences) { |
if (ce->trait_precedences) { |
|
HashTable exclude_table; |
|
|
/* TODO: revisit this start size, may be its not optimal */ |
/* TODO: revisit this start size, may be its not optimal */ |
zend_hash_init_ex(&exclude_table, 2, NULL, NULL, 0, 0); |
zend_hash_init_ex(&exclude_table, 2, NULL, NULL, 0, 0); |
|
|
zend_traits_compile_exclude_table(&exclude_table, ce->trait_precedences, ce->traits[i]); |
zend_traits_compile_exclude_table(&exclude_table, ce->trait_precedences, ce->traits[i]); |
|
|
/* copies functions, applies defined aliasing, and excludes unused trait methods */ |
/* copies functions, applies defined aliasing, and excludes unused trait methods */ |
zend_traits_copy_trait_function_table(function_tables[i], &ce->traits[i]->function_table, ce->trait_aliases, &exclude_table TSRMLS_CC); | zend_hash_apply_with_arguments(&ce->traits[i]->function_table TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, ce, &overriden, &exclude_table); |
| |
zend_hash_destroy(&exclude_table); |
zend_hash_destroy(&exclude_table); |
} else { |
} else { |
zend_traits_copy_trait_function_table(function_tables[i], &ce->traits[i]->function_table, ce->trait_aliases, NULL TSRMLS_CC); | zend_hash_apply_with_arguments(&ce->traits[i]->function_table TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, ce, &overriden, NULL); |
} |
} |
} |
} |
|
|
/* now merge trait methods */ | zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t)zend_fixup_trait_method, ce TSRMLS_CC); |
for (i = 0; i < ce->num_traits; i++) { | |
zend_hash_apply_with_arguments(function_tables[i] TSRMLS_CC, (apply_func_args_t)zend_traits_merge_functions, 5, i, ce->num_traits, resulting_table, function_tables, ce); | |
} | |
| |
/* Now the resulting_table contains all trait methods we would have to | |
* add to the class in the following step the methods are inserted into the method table | |
* if there is already a method with the same name it is replaced iff ce != fn.scope | |
* --> all inherited methods are overridden, methods defined in the class are left untouched | |
*/ | |
zend_hash_apply_with_arguments(resulting_table TSRMLS_CC, (apply_func_args_t)zend_traits_merge_functions_to_class, 1, ce); | |
| |
/* free temporary function tables */ | |
for (i = 0; i < ce->num_traits; i++) { | |
/* zend_hash_destroy(function_tables[i]); */ | |
zend_hash_graceful_destroy(function_tables[i]); | |
free(function_tables[i]); | |
} | |
free(function_tables); | |
|
|
/* free temporary resulting table */ | if (overriden) { |
/* zend_hash_destroy(resulting_table); */ | zend_hash_destroy(overriden); |
zend_hash_graceful_destroy(resulting_table); | FREE_HASHTABLE(overriden); |
free(resulting_table); | } |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
static zend_class_entry* find_first_definition(zend_class_entry *ce, size_t current_trait, const char* prop_name, int prop_name_length, ulong prop_hash, zend_class_entry *coliding_ce) /* {{{ */ |
static zend_class_entry* find_first_definition(zend_class_entry *ce, size_t current_trait, const char* prop_name, int prop_name_length, ulong prop_hash, zend_class_entry *coliding_ce) /* {{{ */ |
{ |
{ |
size_t i; |
size_t i; |
zend_property_info *coliding_prop; | |
for (i = 0; (i < current_trait) && (i < ce->num_traits); i++) { | if (coliding_ce == ce) { |
if (zend_hash_quick_find(&ce->traits[i]->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop) == SUCCESS) { | for (i = 0; i < current_trait; i++) { |
return ce->traits[i]; | if (zend_hash_quick_exists(&ce->traits[i]->properties_info, prop_name, prop_name_length+1, prop_hash)) { |
| return ce->traits[i]; |
| } |
} |
} |
} |
} |
|
|
Line 4141 static zend_class_entry* find_first_definition(zend_cl
|
Line 4048 static zend_class_entry* find_first_definition(zend_cl
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
static void zend_traits_register_private_property(zend_class_entry *ce, const char *name, int name_len, zend_property_info *old_info, zval *property TSRMLS_DC) /* {{{ */ |
|
{ |
|
char *priv_name; |
|
int priv_name_length; |
|
const char *interned_name; |
|
zend_property_info property_info; |
|
ulong h = zend_get_hash_value(name, name_len+1); |
|
property_info = *old_info; |
|
|
|
if (old_info->flags & ZEND_ACC_STATIC) { |
|
property_info.offset = ce->default_static_members_count++; |
|
ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval*) * ce->default_static_members_count, ce->type == ZEND_INTERNAL_CLASS); |
|
ce->default_static_members_table[property_info.offset] = property; |
|
if (ce->type == ZEND_USER_CLASS) { |
|
ce->static_members_table = ce->default_static_members_table; |
|
} |
|
} else { |
|
property_info.offset = ce->default_properties_count++; |
|
ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval*) * ce->default_properties_count, ce->type == ZEND_INTERNAL_CLASS); |
|
ce->default_properties_table[property_info.offset] = property; |
|
} |
|
|
|
zend_mangle_property_name(&priv_name, &priv_name_length, ce->name, ce->name_length, name, name_len, ce->type & ZEND_INTERNAL_CLASS); |
|
property_info.name = priv_name; |
|
property_info.name_length = priv_name_length; |
|
|
|
interned_name = zend_new_interned_string(property_info.name, property_info.name_length+1, 0 TSRMLS_CC); |
|
if (interned_name != property_info.name) { |
|
if (ce->type == ZEND_USER_CLASS) { |
|
efree((char*)property_info.name); |
|
} else { |
|
free((char*)property_info.name); |
|
} |
|
property_info.name = interned_name; |
|
} |
|
|
|
property_info.h = zend_get_hash_value(property_info.name, property_info.name_length+1); |
|
|
|
property_info.ce = ce; |
|
|
|
if (property_info.doc_comment) { |
|
property_info.doc_comment = estrndup(property_info.doc_comment, property_info.doc_comment_len); |
|
} |
|
|
|
zend_hash_quick_update(&ce->properties_info, name, name_len+1, h, &property_info, sizeof(zend_property_info), NULL); |
|
} |
|
/* }}} */ |
|
|
|
static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */ |
static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */ |
{ |
{ |
size_t i; |
size_t i; |
Line 4202 static void zend_do_traits_property_binding(zend_class
|
Line 4061 static void zend_do_traits_property_binding(zend_class
|
zend_bool not_compatible; |
zend_bool not_compatible; |
zval* prop_value; |
zval* prop_value; |
char* doc_comment; |
char* doc_comment; |
| zend_uint flags; |
| |
/* In the following steps the properties are inserted into the property table |
/* In the following steps the properties are inserted into the property table |
* for that, a very strict approach is applied: |
* for that, a very strict approach is applied: |
* - check for compatibility, if not compatible with any property in class -> fatal |
* - check for compatibility, if not compatible with any property in class -> fatal |
Line 4215 static void zend_do_traits_property_binding(zend_class
|
Line 4075 static void zend_do_traits_property_binding(zend_class
|
/* first get the unmangeld name if necessary, |
/* first get the unmangeld name if necessary, |
* then check whether the property is already there |
* then check whether the property is already there |
*/ |
*/ |
if ((property_info->flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) { | flags = property_info->flags; |
| if ((flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) { |
prop_hash = property_info->h; |
prop_hash = property_info->h; |
prop_name = property_info->name; |
prop_name = property_info->name; |
prop_name_length = property_info->name_length; |
prop_name_length = property_info->name_length; |
Line 4229 static void zend_do_traits_property_binding(zend_class
|
Line 4090 static void zend_do_traits_property_binding(zend_class
|
|
|
/* next: check for conflicts with current class */ |
/* next: check for conflicts with current class */ |
if (zend_hash_quick_find(&ce->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop) == SUCCESS) { |
if (zend_hash_quick_find(&ce->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop) == SUCCESS) { |
if (coliding_prop->flags & ZEND_ACC_SHADOW) { | if (coliding_prop->flags & ZEND_ACC_SHADOW) { |
/* this one is inherited, lets look it up in its own class */ | zend_hash_quick_del(&ce->properties_info, prop_name, prop_name_length+1, prop_hash); |
zend_hash_quick_find(&coliding_prop->ce->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop); | flags |= ZEND_ACC_CHANGED; |
if (coliding_prop->flags & ZEND_ACC_PRIVATE) { | } else { |
/* private property, make the property_info.offset indenpended */ | if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC)) |
if (property_info->flags & ZEND_ACC_STATIC) { | == (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) { |
prop_value = ce->traits[i]->default_static_members_table[property_info->offset]; | /* flags are identical, now the value needs to be checked */ |
| if (flags & ZEND_ACC_STATIC) { |
| not_compatible = (FAILURE == compare_function(&compare_result, |
| ce->default_static_members_table[coliding_prop->offset], |
| ce->traits[i]->default_static_members_table[property_info->offset] TSRMLS_CC)) |
| || (Z_LVAL(compare_result) != 0); |
} else { |
} else { |
prop_value = ce->traits[i]->default_properties_table[property_info->offset]; | not_compatible = (FAILURE == compare_function(&compare_result, |
| ce->default_properties_table[coliding_prop->offset], |
| ce->traits[i]->default_properties_table[property_info->offset] TSRMLS_CC)) |
| || (Z_LVAL(compare_result) != 0); |
} |
} |
Z_ADDREF_P(prop_value); |
|
|
|
zend_traits_register_private_property(ce, prop_name, prop_name_length, property_info, prop_value TSRMLS_CC); |
|
continue; |
|
} |
|
} |
|
|
|
if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC)) |
|
== (property_info->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) { |
|
/* flags are identical, now the value needs to be checked */ |
|
if (property_info->flags & ZEND_ACC_STATIC) { |
|
not_compatible = (FAILURE == compare_function(&compare_result, |
|
ce->default_static_members_table[coliding_prop->offset], |
|
ce->traits[i]->default_static_members_table[property_info->offset] TSRMLS_CC)) |
|
|| (Z_LVAL(compare_result) != 0); |
|
} else { |
} else { |
not_compatible = (FAILURE == compare_function(&compare_result, | /* the flags are not identical, thus, we assume properties are not compatible */ |
ce->default_properties_table[coliding_prop->offset], | not_compatible = 1; |
ce->traits[i]->default_properties_table[property_info->offset] TSRMLS_CC)) | |
|| (Z_LVAL(compare_result) != 0); | |
} |
} |
} else { |
|
/* the flags are not identical, thus, we assume properties are not compatible */ |
|
not_compatible = 1; |
|
} |
|
|
|
if (not_compatible) { | if (not_compatible) { |
zend_error(E_COMPILE_ERROR, | zend_error(E_COMPILE_ERROR, |
"%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed", |
"%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed", |
find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, |
find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, |
property_info->ce->name, |
property_info->ce->name, |
prop_name, |
prop_name, |
ce->name); |
ce->name); |
} else { | } else { |
zend_error(E_STRICT, | zend_error(E_STRICT, |
"%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed", |
"%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed", |
find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, |
find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name, |
property_info->ce->name, |
property_info->ce->name, |
prop_name, |
prop_name, |
ce->name); |
ce->name); |
|
continue; |
|
} |
} |
} |
} |
} |
|
|
/* property not found, so lets add it */ |
/* property not found, so lets add it */ |
if (property_info->flags & ZEND_ACC_STATIC) { | if (flags & ZEND_ACC_STATIC) { |
prop_value = ce->traits[i]->default_static_members_table[property_info->offset]; |
prop_value = ce->traits[i]->default_static_members_table[property_info->offset]; |
} else { |
} else { |
prop_value = ce->traits[i]->default_properties_table[property_info->offset]; |
prop_value = ce->traits[i]->default_properties_table[property_info->offset]; |
Line 4292 static void zend_do_traits_property_binding(zend_class
|
Line 4142 static void zend_do_traits_property_binding(zend_class
|
|
|
doc_comment = property_info->doc_comment ? estrndup(property_info->doc_comment, property_info->doc_comment_len) : NULL; |
doc_comment = property_info->doc_comment ? estrndup(property_info->doc_comment, property_info->doc_comment_len) : NULL; |
zend_declare_property_ex(ce, prop_name, prop_name_length, |
zend_declare_property_ex(ce, prop_name, prop_name_length, |
prop_value, property_info->flags, | prop_value, flags, |
doc_comment, property_info->doc_comment_len TSRMLS_CC); |
doc_comment, property_info->doc_comment_len TSRMLS_CC); |
} |
} |
} |
} |
Line 4416 ZEND_API int do_bind_function(const zend_op_array *op_
|
Line 4266 ZEND_API int do_bind_function(const zend_op_array *op_
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
void zend_add_trait_precedence(znode *precedence_znode TSRMLS_DC) /* {{{ */ |
|
{ |
|
zend_class_entry *ce = CG(active_class_entry); |
|
zend_add_to_list(&ce->trait_precedences, precedence_znode->u.op.ptr TSRMLS_CC); |
|
} |
|
/* }}} */ |
|
|
|
void zend_add_trait_alias(znode *alias_znode TSRMLS_DC) /* {{{ */ |
|
{ |
|
zend_class_entry *ce = CG(active_class_entry); |
|
zend_add_to_list(&ce->trait_aliases, alias_znode->u.op.ptr TSRMLS_CC); |
|
} |
|
/* }}} */ |
|
|
|
void zend_prepare_reference(znode *result, znode *class_name, znode *method_name TSRMLS_DC) /* {{{ */ |
void zend_prepare_reference(znode *result, znode *class_name, znode *method_name TSRMLS_DC) /* {{{ */ |
{ |
{ |
zend_trait_method_reference *method_ref = emalloc(sizeof(zend_trait_method_reference)); |
zend_trait_method_reference *method_ref = emalloc(sizeof(zend_trait_method_reference)); |
Line 4454 void zend_prepare_reference(znode *result, znode *clas
|
Line 4290 void zend_prepare_reference(znode *result, znode *clas
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
void zend_prepare_trait_alias(znode *result, znode *method_reference, znode *modifiers, znode *alias TSRMLS_DC) /* {{{ */ | void zend_add_trait_alias(znode *method_reference, znode *modifiers, znode *alias TSRMLS_DC) /* {{{ */ |
{ |
{ |
zend_trait_alias *trait_alias = emalloc(sizeof(zend_trait_alias)); | zend_class_entry *ce = CG(active_class_entry); |
| zend_trait_alias *trait_alias; |
|
|
trait_alias->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr; |
|
trait_alias->modifiers = Z_LVAL(modifiers->u.constant); |
|
|
|
if (Z_LVAL(modifiers->u.constant) == ZEND_ACC_STATIC) { |
if (Z_LVAL(modifiers->u.constant) == ZEND_ACC_STATIC) { |
zend_error(E_COMPILE_ERROR, "Cannot use 'static' as method modifier"); |
zend_error(E_COMPILE_ERROR, "Cannot use 'static' as method modifier"); |
return; |
return; |
|
} else if (Z_LVAL(modifiers->u.constant) == ZEND_ACC_ABSTRACT) { |
|
zend_error(E_COMPILE_ERROR, "Cannot use 'abstract' as method modifier"); |
|
return; |
|
} else if (Z_LVAL(modifiers->u.constant) == ZEND_ACC_FINAL) { |
|
zend_error(E_COMPILE_ERROR, "Cannot use 'final' as method modifier"); |
|
return; |
} |
} |
|
|
|
trait_alias = emalloc(sizeof(zend_trait_alias)); |
|
trait_alias->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr; |
|
trait_alias->modifiers = Z_LVAL(modifiers->u.constant); |
if (alias) { |
if (alias) { |
trait_alias->alias = Z_STRVAL(alias->u.constant); |
trait_alias->alias = Z_STRVAL(alias->u.constant); |
trait_alias->alias_len = Z_STRLEN(alias->u.constant); |
trait_alias->alias_len = Z_STRLEN(alias->u.constant); |
Line 4474 void zend_prepare_trait_alias(znode *result, znode *me
|
Line 4317 void zend_prepare_trait_alias(znode *result, znode *me
|
} |
} |
trait_alias->function = NULL; |
trait_alias->function = NULL; |
|
|
result->u.op.ptr = trait_alias; | zend_add_to_list(&ce->trait_aliases, trait_alias TSRMLS_CC); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
void zend_prepare_trait_precedence(znode *result, znode *method_reference, znode *trait_list TSRMLS_DC) /* {{{ */ | void zend_add_trait_precedence(znode *method_reference, znode *trait_list TSRMLS_DC) /* {{{ */ |
{ |
{ |
|
zend_class_entry *ce = CG(active_class_entry); |
zend_trait_precedence *trait_precedence = emalloc(sizeof(zend_trait_precedence)); |
zend_trait_precedence *trait_precedence = emalloc(sizeof(zend_trait_precedence)); |
|
|
trait_precedence->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr; |
trait_precedence->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr; |
Line 4487 void zend_prepare_trait_precedence(znode *result, znod
|
Line 4331 void zend_prepare_trait_precedence(znode *result, znod
|
|
|
trait_precedence->function = NULL; |
trait_precedence->function = NULL; |
|
|
result->u.op.ptr = trait_precedence; | zend_add_to_list(&ce->trait_precedences, trait_precedence TSRMLS_CC); |
} |
} |
/* }}} */ |
/* }}} */ |
|
|
Line 4522 ZEND_API zend_class_entry *do_bind_class(const zend_op
|
Line 4366 ZEND_API zend_class_entry *do_bind_class(const zend_op
|
} |
} |
return NULL; |
return NULL; |
} else { |
} else { |
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES))) { | if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) { |
zend_verify_abstract_class(ce TSRMLS_CC); |
zend_verify_abstract_class(ce TSRMLS_CC); |
} |
} |
return ce; |
return ce; |
Line 4954 void zend_do_begin_class_declaration(const znode *clas
|
Line 4798 void zend_do_begin_class_declaration(const znode *clas
|
/* Prefix class name with name of current namespace */ |
/* Prefix class name with name of current namespace */ |
znode tmp; |
znode tmp; |
|
|
|
tmp.op_type = IS_CONST; |
tmp.u.constant = *CG(current_namespace); |
tmp.u.constant = *CG(current_namespace); |
zval_copy_ctor(&tmp.u.constant); |
zval_copy_ctor(&tmp.u.constant); |
zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC); |
zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC); |
class_name = &tmp; | *class_name = tmp; |
efree(lcname); |
efree(lcname); |
lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant)); |
lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant)); |
} |
} |
Line 5008 void zend_do_begin_class_declaration(const znode *clas
|
Line 4853 void zend_do_begin_class_declaration(const znode *clas
|
opline->op2_type = IS_CONST; |
opline->op2_type = IS_CONST; |
|
|
if (doing_inheritance) { |
if (doing_inheritance) { |
/* Make sure a trait does not try to extend a class */ | /* Make sure a trait does not try to extend a class */ |
if ((new_class_entry->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { | if ((new_class_entry->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { |
zend_error(E_COMPILE_ERROR, "A trait (%s) cannot extend a class. Traits can only be composed from other traits with the 'use' keyword. Error", new_class_entry->name); | zend_error(E_COMPILE_ERROR, "A trait (%s) cannot extend a class. Traits can only be composed from other traits with the 'use' keyword. Error", new_class_entry->name); |
} | } |
| |
opline->extended_value = parent_class_name->u.op.var; |
opline->extended_value = parent_class_name->u.op.var; |
opline->opcode = ZEND_DECLARE_INHERITED_CLASS; |
opline->opcode = ZEND_DECLARE_INHERITED_CLASS; |
} else { |
} else { |
Line 5090 void zend_do_end_class_declaration(const znode *class_
|
Line 4935 void zend_do_end_class_declaration(const znode *class_
|
} |
} |
|
|
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) |
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) |
&& ((parent_token->op_type != IS_UNUSED) || (ce->num_interfaces > 0))) { | && (parent_token || (ce->num_interfaces > 0))) { |
zend_verify_abstract_class(ce TSRMLS_CC); |
zend_verify_abstract_class(ce TSRMLS_CC); |
if (ce->num_interfaces) { | if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { |
do_verify_abstract_class(TSRMLS_C); |
do_verify_abstract_class(TSRMLS_C); |
} |
} |
} |
} |
Line 5142 void zend_do_implements_interface(znode *interface_nam
|
Line 4987 void zend_do_implements_interface(znode *interface_nam
|
} |
} |
/* }}} */ |
/* }}} */ |
|
|
void zend_do_implements_trait(znode *trait_name TSRMLS_DC) /* {{{ */ | void zend_do_use_trait(znode *trait_name TSRMLS_DC) /* {{{ */ |
{ |
{ |
zend_op *opline; |
zend_op *opline; |
|
|
if ((CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE)) { |
if ((CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE)) { |
zend_error(E_COMPILE_ERROR, |
zend_error(E_COMPILE_ERROR, |
"Cannot use traits inside of interfaces. %s is used in %s", |
"Cannot use traits inside of interfaces. %s is used in %s", |
Line 5633 void zend_do_shell_exec(znode *result, const znode *cm
|
Line 5479 void zend_do_shell_exec(znode *result, const znode *cm
|
break; |
break; |
} |
} |
SET_NODE(opline->op1, cmd); |
SET_NODE(opline->op1, cmd); |
opline->op2.opline_num = 0; | opline->op2.opline_num = 1; |
opline->extended_value = ZEND_DO_FCALL; |
opline->extended_value = ZEND_DO_FCALL; |
SET_UNUSED(opline->op2); |
SET_UNUSED(opline->op2); |
|
|
Line 5874 void zend_add_to_list(void *result, void *item TSRMLS_
|
Line 5720 void zend_add_to_list(void *result, void *item TSRMLS_
|
void** list = *(void**)result; |
void** list = *(void**)result; |
size_t n = 0; |
size_t n = 0; |
|
|
while (list && list[n]) { n++; } | if (list) { |
| while (list[n]) { |
| n++; |
| } |
| } |
|
|
list = erealloc(list, sizeof(void*) * (n+2)); |
list = erealloc(list, sizeof(void*) * (n+2)); |
|
|
Line 6957 void zend_do_use(znode *ns_name, znode *new_name, int
|
Line 6807 void zend_do_use(znode *ns_name, znode *new_name, int
|
lcname = zend_str_tolower_dup(Z_STRVAL_P(name), Z_STRLEN_P(name)); |
lcname = zend_str_tolower_dup(Z_STRVAL_P(name), Z_STRLEN_P(name)); |
|
|
if (((Z_STRLEN_P(name) == sizeof("self")-1) && |
if (((Z_STRLEN_P(name) == sizeof("self")-1) && |
!memcmp(lcname, "self", sizeof("self")-1)) || | !memcmp(lcname, "self", sizeof("self")-1)) || |
((Z_STRLEN_P(name) == sizeof("parent")-1) && | ((Z_STRLEN_P(name) == sizeof("parent")-1) && |
!memcmp(lcname, "parent", sizeof("parent")-1))) { | !memcmp(lcname, "parent", sizeof("parent")-1))) { |
zend_error(E_COMPILE_ERROR, "Cannot use %s as %s because '%s' is a special class name", Z_STRVAL_P(ns), Z_STRVAL_P(name), Z_STRVAL_P(name)); |
zend_error(E_COMPILE_ERROR, "Cannot use %s as %s because '%s' is a special class name", Z_STRVAL_P(ns), Z_STRVAL_P(name), Z_STRVAL_P(name)); |
} |
} |
|
|
Line 7093 ZEND_API size_t zend_dirname(char *path, size_t len)
|
Line 6943 ZEND_API size_t zend_dirname(char *path, size_t len)
|
} |
} |
#elif defined(NETWARE) |
#elif defined(NETWARE) |
/* |
/* |
* Find the first occurence of : from the left | * Find the first occurrence of : from the left |
* move the path pointer to the position just after : |
* move the path pointer to the position just after : |
* increment the len_adjust to the length of path till colon character(inclusive) |
* increment the len_adjust to the length of path till colon character(inclusive) |
* If there is no character beyond : simple return len |
* If there is no character beyond : simple return len |