scope). (Nikita)
. Fixed bug #77325 (ReflectionClassConstant::$class returns wrong class when
extending). (Nikita)
+ . Fixed bug #69180 (Reflection does not honor trait conflict resolution /
+ method aliasing). (Nikita)
+ . Fixed bug #74939 (Nested traits' aliased methods are lowercased). (Nikita)
- Session:
. Fixed bug #78624 (session_gc return value for user defined session
[0]=>
string(10) "testMethod"
[1]=>
- string(25) "testmethodfromparenttrait"
+ string(25) "testMethodFromParentTrait"
}
}
/* }}} */
-ZEND_API zend_string* zend_find_alias_name(zend_class_entry *ce, zend_string *name) /* {{{ */
-{
- zend_trait_alias *alias, **alias_ptr;
-
- if ((alias_ptr = ce->trait_aliases)) {
- alias = *alias_ptr;
- while (alias) {
- if (alias->alias && zend_string_equals_ci(alias->alias, name)) {
- return alias->alias;
- }
- alias_ptr++;
- alias = *alias_ptr;
- }
- }
-
- return name;
-}
-/* }}} */
-
-ZEND_API zend_string *zend_resolve_method_name(zend_class_entry *ce, zend_function *f) /* {{{ */
-{
- zend_function *func;
- HashTable *function_table;
- zend_string *name;
-
- if (f->common.type != ZEND_USER_FUNCTION ||
- (f->op_array.refcount && *(f->op_array.refcount) < 2) ||
- !f->common.scope ||
- !f->common.scope->trait_aliases) {
- return f->common.function_name;
- }
-
- function_table = &ce->function_table;
- ZEND_HASH_FOREACH_STR_KEY_PTR(function_table, name, func) {
- if (func == f) {
- if (!name) {
- return f->common.function_name;
- }
- if (ZSTR_LEN(name) == ZSTR_LEN(f->common.function_name) &&
- !strncasecmp(ZSTR_VAL(name), ZSTR_VAL(f->common.function_name), ZSTR_LEN(f->common.function_name))) {
- return f->common.function_name;
- }
- return zend_find_alias_name(f->common.scope, name);
- }
- } ZEND_HASH_FOREACH_END();
- return f->common.function_name;
-}
-/* }}} */
-
ZEND_API ZEND_COLD const char *zend_get_object_type(const zend_class_entry *ce) /* {{{ */
{
if(ce->ce_flags & ZEND_ACC_TRAIT) {
return SUCCESS;
}
-ZEND_API zend_string *zend_find_alias_name(zend_class_entry *ce, zend_string *name);
-ZEND_API zend_string *zend_resolve_method_name(zend_class_entry *ce, zend_function *f);
-
ZEND_API ZEND_COLD const char *zend_get_object_type(const zend_class_entry *ce);
ZEND_API zend_bool zend_is_iterable(zval *iterable);
zend_class_entry *ce = NULL;
zend_class_entry *scope;
zend_function *mptr;
- zend_string *key;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &klass) == FAILURE) {
RETURN_THROWS();
array_init(return_value);
scope = zend_get_executed_scope();
- ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, key, mptr) {
-
+ ZEND_HASH_FOREACH_PTR(&ce->function_table, mptr) {
if ((mptr->common.fn_flags & ZEND_ACC_PUBLIC)
|| (scope &&
(((mptr->common.fn_flags & ZEND_ACC_PROTECTED) &&
|| ((mptr->common.fn_flags & ZEND_ACC_PRIVATE) &&
scope == mptr->common.scope)))
) {
- if (mptr->type == ZEND_USER_FUNCTION &&
- (!mptr->op_array.refcount || *mptr->op_array.refcount > 1) &&
- key && !same_name(key, mptr->common.function_name)) {
- ZVAL_STR_COPY(&method_name, zend_find_alias_name(mptr->common.scope, key));
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
- } else {
- ZVAL_STR_COPY(&method_name, mptr->common.function_name);
- zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
- }
+ ZVAL_STR_COPY(&method_name, mptr->common.function_name);
+ zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &method_name);
}
} ZEND_HASH_FOREACH_END();
}
object = (Z_TYPE(call->This) == IS_OBJECT) ? Z_OBJ(call->This) : NULL;
if (call->func) {
- zend_string *zend_function_name;
-
func = call->func;
- if (func->common.scope && func->common.scope->trait_aliases) {
- zend_function_name = zend_resolve_method_name(object ? object->ce : func->common.scope, func);
- } else {
- zend_function_name = func->common.function_name;
- }
- if (zend_function_name != NULL) {
- function_name = ZSTR_VAL(zend_function_name);
+ if (func->common.function_name) {
+ function_name = ZSTR_VAL(func->common.function_name);
} else {
function_name = NULL;
}
if (call && call->func) {
func = call->func;
- function_name = (func->common.scope &&
- func->common.scope->trait_aliases) ?
- zend_resolve_method_name(
- (object ? object->ce : func->common.scope), func) :
- func->common.function_name;
+ function_name = func->common.function_name;
} else {
func = NULL;
function_name = NULL;
}
memset(ptr, 0, func->op_array.cache_size);
}
+ zend_string_addref(closure->func.op_array.function_name);
if (closure->func.op_array.refcount) {
(*closure->func.op_array.refcount)++;
}
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);
}
- } else if (function->type == ZEND_INTERNAL_FUNCTION) {
- if (function->common.function_name) {
- zend_string_addref(function->common.function_name);
- }
+ }
+
+ if (function->common.function_name) {
+ zend_string_addref(function->common.function_name);
}
}
/* }}} */
if (func->op_array.refcount) {
(*func->op_array.refcount)++;
}
+ if (EXPECTED(func->op_array.function_name)) {
+ zend_string_addref(func->op_array.function_name);
+ }
if (is_interface
|| EXPECTED(!func->op_array.static_variables)) {
/* reuse the same op_array structure */
}
/* }}} */
-static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_string *key, zend_function *fn, HashTable **overridden) /* {{{ */
+static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_string *key, zend_function *fn, HashTable **overridden) /* {{{ */
{
zend_function *existing_fn = NULL;
zend_function *new_fn;
/* two traits can't define the same non-abstract method */
#if 1
zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s",
- name, ZSTR_VAL(ce->name));
+ ZSTR_VAL(name), ZSTR_VAL(ce->name));
#else /* TODO: better error message */
zend_error_noreturn(E_COMPILE_ERROR, "Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s",
ZSTR_VAL(fn->common.scope->name), ZSTR_VAL(fn->common.function_name),
- ZSTR_VAL(ce->name), name,
+ ZSTR_VAL(ce->name), ZSTR_VAL(name),
ZSTR_VAL(existing_fn->common.scope->name), ZSTR_VAL(existing_fn->common.function_name));
#endif
} else {
new_fn->op_array.fn_flags |= ZEND_ACC_TRAIT_CLONE;
new_fn->op_array.fn_flags &= ~ZEND_ACC_IMMUTABLE;
}
+
+ /* Reassign method name, in case it is an alias. */
+ new_fn->common.function_name = name;
function_add_ref(new_fn);
fn = zend_hash_update_ptr(&ce->function_table, key, new_fn);
zend_add_magic_methods(ce, key, fn);
}
lcname = zend_string_tolower(alias->alias);
- zend_add_trait_method(ce, ZSTR_VAL(alias->alias), lcname, &fn_copy, overridden);
+ zend_add_trait_method(ce, alias->alias, lcname, &fn_copy, overridden);
zend_string_release_ex(lcname, 0);
/* Record the trait from which this alias was resolved. */
}
}
- zend_add_trait_method(ce, ZSTR_VAL(fn->common.function_name), fnname, &fn_copy, overridden);
+ zend_add_trait_method(ce, fn->common.function_name, fnname, &fn_copy, overridden);
}
}
/* }}} */
efree(ZEND_MAP_PTR(op_array->run_time_cache));
}
+ if (op_array->function_name) {
+ zend_string_release_ex(op_array->function_name, 0);
+ }
+
if (!op_array->refcount || --(*op_array->refcount) > 0) {
return;
}
}
efree(op_array->opcodes);
- if (op_array->function_name) {
- zend_string_release_ex(op_array->function_name, 0);
- }
if (op_array->doc_comment) {
zend_string_release_ex(op_array->doc_comment, 0);
}
EG(current_execute_data) = orig_execute_data;
}
+ if (op_array->function_name) {
+ zend_string *old_name = op_array->function_name;
+ zend_accel_store_interned_string(op_array->function_name);
+ /* Remember old function name, so it can be released multiple times if shared. */
+ if (op_array->function_name != old_name
+ && !zend_shared_alloc_get_xlat_entry(&op_array->function_name)) {
+ zend_shared_alloc_register_xlat_entry(&op_array->function_name, old_name);
+ }
+ }
+
if (op_array->scope) {
zend_class_entry *scope = zend_shared_alloc_get_xlat_entry(op_array->scope);
op_array->literals = zend_shared_alloc_get_xlat_entry(op_array->literals);
ZEND_ASSERT(op_array->literals != NULL);
}
- if (op_array->function_name && !IS_ACCEL_INTERNED(op_array->function_name)) {
- op_array->function_name = zend_shared_alloc_get_xlat_entry(op_array->function_name);
- ZEND_ASSERT(op_array->function_name != NULL);
- }
if (op_array->filename) {
op_array->filename = zend_shared_alloc_get_xlat_entry(op_array->filename);
ZEND_ASSERT(op_array->filename != NULL);
ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
}
- if (op_array->function_name && !IS_ACCEL_INTERNED(op_array->function_name)) {
- zend_accel_store_interned_string(op_array->function_name);
- }
-
if (op_array->filename) {
/* do not free! PHP has centralized filename storage, compiler will free it */
zend_accel_memdup_string(op_array->filename);
if (op_array->refcount && --(*op_array->refcount) == 0) {
efree(op_array->refcount);
}
+
+ /* If op_array is shared, the function name refcount is still incremented for each use,
+ * so we need to release it here. We remembered the original function name in xlat. */
+ zend_string *old_function_name =
+ zend_shared_alloc_get_xlat_entry(&old_op_array->function_name);
+ if (old_function_name) {
+ zend_string_release_ex(old_function_name, 0);
+ }
return;
}
if (ZCG(is_immutable_class)) {
static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
{
+ if (op_array->function_name) {
+ zend_string *old_name = op_array->function_name;
+ ADD_INTERNED_STRING(op_array->function_name);
+ /* Remember old function name, so it can be released multiple times if shared. */
+ if (op_array->function_name != old_name
+ && !zend_shared_alloc_get_xlat_entry(&op_array->function_name)) {
+ zend_shared_alloc_register_xlat_entry(&op_array->function_name, old_name);
+ }
+ }
+
if (op_array->scope && zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
/* already stored */
- if (op_array->function_name) {
- zend_string *new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name);
- if (new_name) {
- op_array->function_name = new_name;
- }
- }
ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array)));
return;
}
zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes);
ADD_SIZE(sizeof(zend_op) * op_array->last);
- if (op_array->function_name) {
- zend_string *old_name = op_array->function_name;
- if (!zend_shared_alloc_get_xlat_entry(old_name)) {
- ADD_INTERNED_STRING(op_array->function_name);
- if (!zend_shared_alloc_get_xlat_entry(op_array->function_name)) {
- zend_shared_alloc_register_xlat_entry(old_name, op_array->function_name);
- }
- }
- }
-
if (op_array->filename) {
ADD_STRING(op_array->filename);
}
if (!ZCG(is_immutable_class)) {
ADD_ARENA_SIZE(sizeof(void*));
}
+ } else {
+ /* If op_array is shared, the function name refcount is still incremented for each use,
+ * so we need to release it here. We remembered the original function name in xlat. */
+ zend_string *old_function_name =
+ zend_shared_alloc_get_xlat_entry(&old_op_array->function_name);
+ if (old_function_name) {
+ zend_string_release_ex(old_function_name, 0);
+ }
}
}
ZVAL_OBJ(&intern->obj, Z_OBJ_P(closure_object));
}
- ZVAL_STR_COPY(reflection_prop_name(object),
- (method->common.scope && method->common.scope->trait_aliases)
- ? zend_resolve_method_name(ce, method) : method->common.function_name);
+ ZVAL_STR_COPY(reflection_prop_name(object), method->common.function_name);
ZVAL_STR_COPY(reflection_prop_class(object), method->common.scope->name);
}
/* }}} */
--- /dev/null
+--TEST--
+Bug #69180: Reflection does not honor trait conflict resolution / method aliasing
+--FILE--
+<?php
+
+trait T1
+{
+ public function foo()
+ {
+ }
+}
+
+trait T2
+{
+ use T1 { foo as bar; }
+
+ public function foo()
+ {
+ }
+}
+
+
+class C
+{
+ use T2;
+}
+
+$class = new ReflectionClass('C');
+
+foreach ($class->getMethods() as $method) {
+ var_dump($method->getName());
+}
+
+?>
+--EXPECT--
+string(3) "foo"
+string(3) "bar"