gron Sat, 05 Nov 2011 01:46:40 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=318793
Log: Fixed Bug #60217 (Requiring the same method from different traits) - also added test to check for inconsistent abstract method definitions, they need to be compatible Bug: https://bugs.php.net/60217 (Assigned) imposing requirements in traits Changed paths: A php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217a.phpt A php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217b.phpt A php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217c.phpt U php/php-src/branches/PHP_5_4/Zend/zend_compile.c A php/php-src/trunk/Zend/tests/traits/bug60217a.phpt A php/php-src/trunk/Zend/tests/traits/bug60217b.phpt A php/php-src/trunk/Zend/tests/traits/bug60217c.phpt U php/php-src/trunk/Zend/zend_compile.c
Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217a.phpt =================================================================== --- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217a.phpt (rev 0) +++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217a.phpt 2011-11-05 01:46:40 UTC (rev 318793) @@ -0,0 +1,26 @@ +--TEST-- +Bug #60217 (Requiring the same method from different traits.) +--FILE-- +<?php + +trait T1 { + public abstract function foo(); +} + +trait T2 { + public abstract function foo(); +} + +class C { + use T1, T2; + + public function foo() { + echo "C::foo() works.\n"; + } +} + +$o = new C; +$o->foo(); + +--EXPECTF-- +C::foo() works. Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217b.phpt =================================================================== --- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217b.phpt (rev 0) +++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217b.phpt 2011-11-05 01:46:40 UTC (rev 318793) @@ -0,0 +1,26 @@ +--TEST-- +Bug #60217 (Requiring the same method from different traits and abstract methods have to be compatible) +--FILE-- +<?php + +trait TBroken1 { + public abstract function foo($a); +} + +trait TBroken2 { + public abstract function foo($a, $b = 0); +} + +class CBroken { + use TBroken1, TBroken2; + + public function foo($a) { + echo 'FOO'; + } +} + +$o = new CBroken; +$o->foo(1); + +--EXPECTF-- +Fatal error: Declaration of TBroken1::foo($a) must be compatible with TBroken2::foo($a, $b = 0) in %s on line %d Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217c.phpt =================================================================== --- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217c.phpt (rev 0) +++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug60217c.phpt 2011-11-05 01:46:40 UTC (rev 318793) @@ -0,0 +1,26 @@ +--TEST-- +Bug #60217 (Requiring the same method from different traits and abstract methods have to be compatible, in both directions.) +--FILE-- +<?php + +trait TBroken1 { + public abstract function foo($a, $b = 0); +} + +trait TBroken2 { + public abstract function foo($a); +} + +class CBroken { + use TBroken1, TBroken2; + + public function foo($a) { + echo 'FOO'; + } +} + +$o = new CBroken; +$o->foo(1); + +--EXPECTF-- +Fatal error: Declaration of TBroken1::foo($a, $b = 0) must be compatible with TBroken2::foo($a) in %s on line %d Modified: php/php-src/branches/PHP_5_4/Zend/zend_compile.c =================================================================== --- php/php-src/branches/PHP_5_4/Zend/zend_compile.c 2011-11-04 22:58:44 UTC (rev 318792) +++ php/php-src/branches/PHP_5_4/Zend/zend_compile.c 2011-11-05 01:46:40 UTC (rev 318793) @@ -3617,7 +3617,19 @@ /* 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 */ - do_inheritance_check_on_method(fn, other_trait_fn TSRMLS_CC); + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + /* In case both are abstract, just check prototype, but need to do that in both directions */ + if ( !zend_do_perform_implementation_check(fn, other_trait_fn TSRMLS_CC) + || !zend_do_perform_implementation_check(other_trait_fn, fn TSRMLS_CC)) { + zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", //ZEND_FN_SCOPE_NAME(fn), fn->common.function_name, //::%s() + zend_get_function_declaration(fn TSRMLS_CC), + zend_get_function_declaration(other_trait_fn TSRMLS_CC)); + } + } + else { + /* otherwise, do the full check */ + do_inheritance_check_on_method(fn, other_trait_fn TSRMLS_CC); + } /* we can savely free and remove it from other table */ zend_function_dtor(other_trait_fn); @@ -3626,7 +3638,8 @@ /* 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 */ + /* Make sure they are compatible. + Here, we already know other_trait_fn cannot be abstract, full check ok. */ do_inheritance_check_on_method(other_trait_fn, fn TSRMLS_CC); /* just mark as solved, will be added if its own trait is processed */ @@ -3843,39 +3856,39 @@ zend_function* existing_fn = NULL; 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) { add = 1; /* not found */ } else if (existing_fn->common.scope != ce) { add = 1; /* or inherited from other class or interface */ } - + if (add) { zend_function* parent_function; if (ce->parent && zend_hash_quick_find(&ce->parent->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &parent_function) != FAILURE) { prototype = parent_function; /* ->common.fn_flags |= ZEND_ACC_ABSTRACT; */ /* we got that method in the parent class, and are going to override it, - except, if the trait is just asking to have an abstract method implemented. */ + except, if the trait is just asking to have an abstract method implemented. */ if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { /* then we clean up an skip this method */ zend_function_dtor(fn); return ZEND_HASH_APPLY_REMOVE; } } - + fn->common.scope = ce; fn->common.prototype = prototype; - + if (prototype - && (prototype->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT - || prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { - fn->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; - } else if (fn->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT) { - /* remove ZEND_ACC_IMPLEMENTED_ABSTRACT flag, think it shouldn't be copied to class */ - fn->common.fn_flags = fn->common.fn_flags - ZEND_ACC_IMPLEMENTED_ABSTRACT; - } - + && (prototype->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT + || prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { + fn->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; + } else if (fn->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT) { + /* remove ZEND_ACC_IMPLEMENTED_ABSTRACT flag, think it shouldn't be copied to class */ + fn->common.fn_flags = fn->common.fn_flags - ZEND_ACC_IMPLEMENTED_ABSTRACT; + } + /* check whether the trait method fullfills the inheritance requirements */ if (prototype) { do_inheritance_check_on_method(fn, prototype TSRMLS_CC); @@ -3906,18 +3919,18 @@ } fn_copy = *fn; zend_traits_duplicate_function(&fn_copy, ce, estrdup(fn->common.function_name) TSRMLS_CC); - + 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_REMOVE; } /* }}} */ Added: php/php-src/trunk/Zend/tests/traits/bug60217a.phpt =================================================================== --- php/php-src/trunk/Zend/tests/traits/bug60217a.phpt (rev 0) +++ php/php-src/trunk/Zend/tests/traits/bug60217a.phpt 2011-11-05 01:46:40 UTC (rev 318793) @@ -0,0 +1,26 @@ +--TEST-- +Bug #60217 (Requiring the same method from different traits.) +--FILE-- +<?php + +trait T1 { + public abstract function foo(); +} + +trait T2 { + public abstract function foo(); +} + +class C { + use T1, T2; + + public function foo() { + echo "C::foo() works.\n"; + } +} + +$o = new C; +$o->foo(); + +--EXPECTF-- +C::foo() works. Added: php/php-src/trunk/Zend/tests/traits/bug60217b.phpt =================================================================== --- php/php-src/trunk/Zend/tests/traits/bug60217b.phpt (rev 0) +++ php/php-src/trunk/Zend/tests/traits/bug60217b.phpt 2011-11-05 01:46:40 UTC (rev 318793) @@ -0,0 +1,26 @@ +--TEST-- +Bug #60217 (Requiring the same method from different traits and abstract methods have to be compatible) +--FILE-- +<?php + +trait TBroken1 { + public abstract function foo($a); +} + +trait TBroken2 { + public abstract function foo($a, $b = 0); +} + +class CBroken { + use TBroken1, TBroken2; + + public function foo($a) { + echo 'FOO'; + } +} + +$o = new CBroken; +$o->foo(1); + +--EXPECTF-- +Fatal error: Declaration of TBroken1::foo($a) must be compatible with TBroken2::foo($a, $b = 0) in %s on line %d Added: php/php-src/trunk/Zend/tests/traits/bug60217c.phpt =================================================================== --- php/php-src/trunk/Zend/tests/traits/bug60217c.phpt (rev 0) +++ php/php-src/trunk/Zend/tests/traits/bug60217c.phpt 2011-11-05 01:46:40 UTC (rev 318793) @@ -0,0 +1,26 @@ +--TEST-- +Bug #60217 (Requiring the same method from different traits and abstract methods have to be compatible, in both directions.) +--FILE-- +<?php + +trait TBroken1 { + public abstract function foo($a, $b = 0); +} + +trait TBroken2 { + public abstract function foo($a); +} + +class CBroken { + use TBroken1, TBroken2; + + public function foo($a) { + echo 'FOO'; + } +} + +$o = new CBroken; +$o->foo(1); + +--EXPECTF-- +Fatal error: Declaration of TBroken1::foo($a, $b = 0) must be compatible with TBroken2::foo($a) in %s on line %d Modified: php/php-src/trunk/Zend/zend_compile.c =================================================================== --- php/php-src/trunk/Zend/zend_compile.c 2011-11-04 22:58:44 UTC (rev 318792) +++ php/php-src/trunk/Zend/zend_compile.c 2011-11-05 01:46:40 UTC (rev 318793) @@ -3617,7 +3617,19 @@ /* 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 */ - do_inheritance_check_on_method(fn, other_trait_fn TSRMLS_CC); + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + /* In case both are abstract, just check prototype, but need to do that in both directions */ + if ( !zend_do_perform_implementation_check(fn, other_trait_fn TSRMLS_CC) + || !zend_do_perform_implementation_check(other_trait_fn, fn TSRMLS_CC)) { + zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", //ZEND_FN_SCOPE_NAME(fn), fn->common.function_name, //::%s() + zend_get_function_declaration(fn TSRMLS_CC), + zend_get_function_declaration(other_trait_fn TSRMLS_CC)); + } + } + else { + /* otherwise, do the full check */ + do_inheritance_check_on_method(fn, other_trait_fn TSRMLS_CC); + } /* we can savely free and remove it from other table */ zend_function_dtor(other_trait_fn); @@ -3626,7 +3638,8 @@ /* 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 */ + /* Make sure they are compatible. + Here, we already know other_trait_fn cannot be abstract, full check ok. */ do_inheritance_check_on_method(other_trait_fn, fn TSRMLS_CC); /* just mark as solved, will be added if its own trait is processed */ @@ -3843,39 +3856,39 @@ zend_function* existing_fn = NULL; 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) { add = 1; /* not found */ } else if (existing_fn->common.scope != ce) { add = 1; /* or inherited from other class or interface */ } - + if (add) { zend_function* parent_function; if (ce->parent && zend_hash_quick_find(&ce->parent->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &parent_function) != FAILURE) { prototype = parent_function; /* ->common.fn_flags |= ZEND_ACC_ABSTRACT; */ /* we got that method in the parent class, and are going to override it, - except, if the trait is just asking to have an abstract method implemented. */ + except, if the trait is just asking to have an abstract method implemented. */ if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { /* then we clean up an skip this method */ zend_function_dtor(fn); return ZEND_HASH_APPLY_REMOVE; } } - + fn->common.scope = ce; fn->common.prototype = prototype; - + if (prototype - && (prototype->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT - || prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { - fn->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; - } else if (fn->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT) { - /* remove ZEND_ACC_IMPLEMENTED_ABSTRACT flag, think it shouldn't be copied to class */ - fn->common.fn_flags = fn->common.fn_flags - ZEND_ACC_IMPLEMENTED_ABSTRACT; - } - + && (prototype->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT + || prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) { + fn->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; + } else if (fn->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT) { + /* remove ZEND_ACC_IMPLEMENTED_ABSTRACT flag, think it shouldn't be copied to class */ + fn->common.fn_flags = fn->common.fn_flags - ZEND_ACC_IMPLEMENTED_ABSTRACT; + } + /* check whether the trait method fullfills the inheritance requirements */ if (prototype) { do_inheritance_check_on_method(fn, prototype TSRMLS_CC); @@ -3906,18 +3919,18 @@ } fn_copy = *fn; zend_traits_duplicate_function(&fn_copy, ce, estrdup(fn->common.function_name) TSRMLS_CC); - + 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_REMOVE; } /* }}} */
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php