gron                                     Sun, 09 Oct 2011 11:13:27 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=317935

Log:
Fixed Bug #55554 (Legacy constructors not handled properly) [TRAITS] [DOC]
# The handling of legacy constructors defined by traits was corrected.
# They are now properly registered and used on instantiation.
# The situation for conflicting legacy and __construct constructors is
# mostly identical. If they are defined in the class, they override conflicts
# and do not collide. However, in case different styles are mixed, between
# class and trait definition, we assume a programmer's mistake and report
# a collision.
#
# BTW: +1 for all the fixed tests! `make test` is fun again.

Bug: https://bugs.php.net/55554 (Assigned) Trait methods overriding legacy 
constructors
      
Changed paths:
    A   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554a.phpt
    A   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554b.phpt
    A   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554c.phpt
    A   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554d.phpt
    A   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554e.phpt
    A   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554f.phpt
    A   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554g.phpt
    U   php/php-src/branches/PHP_5_4/Zend/tests/traits/language009.phpt
    U   php/php-src/branches/PHP_5_4/Zend/zend_compile.c
    A   php/php-src/trunk/Zend/tests/traits/bug55554a.phpt
    A   php/php-src/trunk/Zend/tests/traits/bug55554b.phpt
    A   php/php-src/trunk/Zend/tests/traits/bug55554c.phpt
    A   php/php-src/trunk/Zend/tests/traits/bug55554d.phpt
    A   php/php-src/trunk/Zend/tests/traits/bug55554e.phpt
    A   php/php-src/trunk/Zend/tests/traits/bug55554f.phpt
    A   php/php-src/trunk/Zend/tests/traits/bug55554g.phpt
    U   php/php-src/trunk/Zend/tests/traits/language009.phpt
    U   php/php-src/trunk/Zend/zend_compile.c

Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554a.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554a.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554a.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,34 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// All constructors should be registered as such
+
+trait TConstructor {
+    public function constructor() {
+        echo "ctor executed\n";
+    }
+}
+
+class NewConstructor {
+	use TConstructor {
+	    constructor as __construct;
+	}
+}
+
+class LegacyConstructor {
+    use TConstructor {
+        constructor as LegacyConstructor;
+    }
+}
+
+echo "New constructor: ";
+$o = new NewConstructor;
+
+echo "Legacy constructor: ";
+$o = new LegacyConstructor;
+
+--EXPECT--
+New constructor: ctor executed
+Legacy constructor: ctor executed

Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554b.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554b.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554b.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,56 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+trait TConstructor {
+    public function foo() {
+        echo "foo executed\n";
+    }
+    public function bar() {
+        echo "bar executed\n";
+    }
+}
+
+class OverridingIsSilent1 {
+    use TConstructor {
+	    foo as __construct;
+	}
+
+	public function __construct() {
+	    echo "OverridingIsSilent1 __construct\n";
+	}
+}
+
+$o = new OverridingIsSilent1;
+
+class OverridingIsSilent2 {
+    use TConstructor {
+	    foo as OverridingIsSilent2;
+	}
+
+	public function OverridingIsSilent2() {
+	    echo "OverridingIsSilent2 OverridingIsSilent2\n";
+	}
+}
+
+$o = new OverridingIsSilent2;
+
+class ReportCollision {
+	use TConstructor {
+	    bar as ReportCollision;
+	    foo as __construct;
+	}
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+OverridingIsSilent1 __construct
+OverridingIsSilent2 OverridingIsSilent2
+
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
+

Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554c.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554c.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554c.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,46 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Test that the behavior is consistent with the existing handling of new
+// and legacy constructors.
+// Here, the traits conflicts are overridden by local definitions,
+// and the two constructor definitions do not directly collide in that case.
+
+trait TC1 {
+    public function __construct() {
+        echo "TC1 executed\n";
+    }
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+trait TC2 {
+    public function __construct() {
+        echo "TC2 executed\n";
+    }
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+class ReportCollision {
+	use TC1, TC2;
+
+	public function __construct() {
+        echo "New constructor executed\n";
+    }
+    public function ReportCollision() {
+        echo "Legacy constructor executed\n";
+    }
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+ReportCollision: New constructor executed

Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554d.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554d.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554d.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,32 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Test mixed constructors from different traits, we are more strict about
+// these cases, since that can lead to un-expected behavior.
+// It is not consistent with the normal constructor handling, but
+// here we have a chance to be more strict for the new traits.
+
+trait TNew {
+    public function __construct() {
+        echo "TNew executed\n";
+    }
+}
+
+trait TLegacy {
+    public function ReportCollision() {
+        echo "ReportCollision executed\n";
+    }
+}
+
+class ReportCollision {
+    use TNew, TLegacy;
+}
+
+$o = new ReportCollision;
+
+--EXPECTF--
+
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
+

Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554e.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554e.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554e.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,30 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Ensuring that the collision still occurs as expected.
+
+trait TC1 {
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+trait TC2 {
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+class ReportCollision {
+	use TC1, TC2;
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+Fatal error: Trait method ReportCollision has not been applied, because there are collisions with other trait methods on ReportCollision in %s on line %d
\ No newline at end of file

Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554f.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554f.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554f.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,29 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Ensuring that inconsistent constructor use results in an error to avoid
+// problems creeping in.
+
+trait TNew {
+    public function __construct() {
+        echo "TNew executed\n";
+    }
+}
+
+class ReportCollision {
+    use TNew;
+
+	public function ReportCollision() {
+	    echo "ReportCollision executed\n";
+	}
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
\ No newline at end of file

Added: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554g.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554g.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55554g.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,29 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Ensuring that inconsistent constructor use results in an error to avoid
+// problems creeping in.
+
+trait TLegacy {
+    public function ReportCollision() {
+        echo "TLegacy executed\n";
+    }
+}
+
+class ReportCollision {
+    use TLegacy;
+
+	public function __construct() {
+	    echo "ReportCollision executed\n";
+	}
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
\ No newline at end of file

Modified: php/php-src/branches/PHP_5_4/Zend/tests/traits/language009.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/language009.phpt	2011-10-09 09:22:36 UTC (rev 317934)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/language009.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -22,13 +22,13 @@
    }
 }

-class Foo {
+class MyClass {
     use C, A, B {
 		B::foo insteadof A, C;
 	}
 }

-$t = new Foo;
+$t = new MyClass;
 $t->foo();

 ?>

Modified: php/php-src/branches/PHP_5_4/Zend/zend_compile.c
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_compile.c	2011-10-09 09:22:36 UTC (rev 317934)
+++ php/php-src/branches/PHP_5_4/Zend/zend_compile.c	2011-10-09 11:13:27 UTC (rev 317935)
@@ -3654,22 +3654,7 @@
 }
 /* }}} */

-#define IS_EQUAL(mname, mname_len, str) \
-      strncmp(mname, str, mname_len)

-#define _ADD_MAGIC_METHOD(ce, mname, mname_len, fe) { \
-	if (     !IS_EQUAL(mname, mname_len, ZEND_CLONE_FUNC_NAME))       { (ce)->clone       = (fe); (fe)->common.fn_flags |= ZEND_ACC_CLONE; } \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_CONSTRUCTOR_FUNC_NAME)) { (ce)->constructor = (fe); (fe)->common.fn_flags |= ZEND_ACC_CTOR; } \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_DESTRUCTOR_FUNC_NAME))  { (ce)->destructor  = (fe); (fe)->common.fn_flags |= ZEND_ACC_DTOR; } \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_GET_FUNC_NAME))        (ce)->__get          = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_SET_FUNC_NAME))        (ce)->__set          = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_CALL_FUNC_NAME))       (ce)->__call         = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_UNSET_FUNC_NAME))      (ce)->__unset        = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_ISSET_FUNC_NAME))      (ce)->__isset        = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_CALLSTATIC_FUNC_NAME)) (ce)->__callstatic   = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_TOSTRING_FUNC_NAME))   (ce)->__tostring     = (fe); \
-}
-
 /* {{{ Originates from php_runkit_function_copy_ctor
 	Duplicate structures in an op_array where necessary to make an outright duplicate */
 static void zend_traits_duplicate_function(zend_function *fe, zend_class_entry *target_ce, char *newname TSRMLS_DC)
@@ -3790,8 +3775,40 @@
 	fe->op_array.brk_cont_array = (zend_brk_cont_element*)estrndup((char*)fe->op_array.brk_cont_array, sizeof(zend_brk_cont_element) * fe->op_array.last_brk_cont);

 }
-/* }}}} */
+/* }}} */

+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)) {	ce->clone = fe; fe->common.fn_flags |= ZEND_ACC_CLONE; }
+	else if (!strncmp(mname, ZEND_CONSTRUCTOR_FUNC_NAME, mname_len)) {
+		if (ce->constructor) {
+			zend_error(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name);
+		}
+		ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR;
+	}
+	else if (!strncmp(mname, ZEND_DESTRUCTOR_FUNC_NAME,  mname_len)) {	ce->destructor  = fe; fe->common.fn_flags |= ZEND_ACC_DTOR; }
+	else if (!strncmp(mname, ZEND_GET_FUNC_NAME,         mname_len)) ce->__get       = fe;
+	else if (!strncmp(mname, ZEND_SET_FUNC_NAME,         mname_len)) ce->__set       = fe;
+	else if (!strncmp(mname, ZEND_CALL_FUNC_NAME,        mname_len)) ce->__call      = fe;
+	else if (!strncmp(mname, ZEND_UNSET_FUNC_NAME,       mname_len)) ce->__unset     = fe;
+	else if (!strncmp(mname, ZEND_ISSET_FUNC_NAME,       mname_len)) ce->__isset     = fe;
+	else if (!strncmp(mname, ZEND_CALLSTATIC_FUNC_NAME,  mname_len)) ce->__callstatic= fe;
+	else if (!strncmp(mname, ZEND_TOSTRING_FUNC_NAME,    mname_len)) ce->__tostring	 = fe;
+	else if (ce->name_length + 1 == mname_len) {
+		char *lowercase_name = emalloc(ce->name_length + 1);
+		zend_str_tolower_copy(lowercase_name, ce->name, ce->name_length);
+		lowercase_name = (char*)zend_new_interned_string(lowercase_name, ce->name_length + 1, 1 TSRMLS_CC);
+		if (!memcmp(mname, lowercase_name, mname_len)) {
+			if (ce->constructor) {
+				zend_error(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name);
+			}
+			ce->constructor = fe;
+			fe->common.fn_flags |= ZEND_ACC_CTOR;
+		}
+		str_efree(lowercase_name);
+	}
+}
+
+
 static int zend_traits_merge_functions_to_class(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
 {
 	zend_class_entry *ce = va_arg(args, zend_class_entry*);
@@ -3858,9 +3875,7 @@
 			zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occured during updating class method table", hash_key->arKey);
 		}

-		_ADD_MAGIC_METHOD(ce, hash_key->arKey, hash_key->nKeyLength, fn_copy_p);
-		/* it could be necessary to update child classes as well */
-		/* zend_hash_apply_with_arguments(EG(class_table) TSRMLS_CC, (apply_func_args_t)php_runkit_update_children_methods, 5, dce, dce, &dfe, dfunc, dfunc_len); */
+		zend_add_magic_methods(ce, hash_key->arKey, hash_key->nKeyLength, fn_copy_p TSRMLS_CC);

 		zend_function_dtor(fn);
 	} else {

Added: php/php-src/trunk/Zend/tests/traits/bug55554a.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55554a.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/traits/bug55554a.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,34 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// All constructors should be registered as such
+
+trait TConstructor {
+    public function constructor() {
+        echo "ctor executed\n";
+    }
+}
+
+class NewConstructor {
+	use TConstructor {
+	    constructor as __construct;
+	}
+}
+
+class LegacyConstructor {
+    use TConstructor {
+        constructor as LegacyConstructor;
+    }
+}
+
+echo "New constructor: ";
+$o = new NewConstructor;
+
+echo "Legacy constructor: ";
+$o = new LegacyConstructor;
+
+--EXPECT--
+New constructor: ctor executed
+Legacy constructor: ctor executed

Added: php/php-src/trunk/Zend/tests/traits/bug55554b.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55554b.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/traits/bug55554b.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,56 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+trait TConstructor {
+    public function foo() {
+        echo "foo executed\n";
+    }
+    public function bar() {
+        echo "bar executed\n";
+    }
+}
+
+class OverridingIsSilent1 {
+    use TConstructor {
+	    foo as __construct;
+	}
+
+	public function __construct() {
+	    echo "OverridingIsSilent1 __construct\n";
+	}
+}
+
+$o = new OverridingIsSilent1;
+
+class OverridingIsSilent2 {
+    use TConstructor {
+	    foo as OverridingIsSilent2;
+	}
+
+	public function OverridingIsSilent2() {
+	    echo "OverridingIsSilent2 OverridingIsSilent2\n";
+	}
+}
+
+$o = new OverridingIsSilent2;
+
+class ReportCollision {
+	use TConstructor {
+	    bar as ReportCollision;
+	    foo as __construct;
+	}
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+OverridingIsSilent1 __construct
+OverridingIsSilent2 OverridingIsSilent2
+
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
+

Added: php/php-src/trunk/Zend/tests/traits/bug55554c.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55554c.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/traits/bug55554c.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,46 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Test that the behavior is consistent with the existing handling of new
+// and legacy constructors.
+// Here, the traits conflicts are overridden by local definitions,
+// and the two constructor definitions do not directly collide in that case.
+
+trait TC1 {
+    public function __construct() {
+        echo "TC1 executed\n";
+    }
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+trait TC2 {
+    public function __construct() {
+        echo "TC2 executed\n";
+    }
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+class ReportCollision {
+	use TC1, TC2;
+
+	public function __construct() {
+        echo "New constructor executed\n";
+    }
+    public function ReportCollision() {
+        echo "Legacy constructor executed\n";
+    }
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+ReportCollision: New constructor executed

Added: php/php-src/trunk/Zend/tests/traits/bug55554d.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55554d.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/traits/bug55554d.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,32 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Test mixed constructors from different traits, we are more strict about
+// these cases, since that can lead to un-expected behavior.
+// It is not consistent with the normal constructor handling, but
+// here we have a chance to be more strict for the new traits.
+
+trait TNew {
+    public function __construct() {
+        echo "TNew executed\n";
+    }
+}
+
+trait TLegacy {
+    public function ReportCollision() {
+        echo "ReportCollision executed\n";
+    }
+}
+
+class ReportCollision {
+    use TNew, TLegacy;
+}
+
+$o = new ReportCollision;
+
+--EXPECTF--
+
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
+

Added: php/php-src/trunk/Zend/tests/traits/bug55554e.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55554e.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/traits/bug55554e.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,30 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Ensuring that the collision still occurs as expected.
+
+trait TC1 {
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+trait TC2 {
+    public function ReportCollision() {
+        echo "TC1 executed\n";
+    }
+}
+
+class ReportCollision {
+	use TC1, TC2;
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+Fatal error: Trait method ReportCollision has not been applied, because there are collisions with other trait methods on ReportCollision in %s on line %d
\ No newline at end of file

Added: php/php-src/trunk/Zend/tests/traits/bug55554f.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55554f.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/traits/bug55554f.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,29 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Ensuring that inconsistent constructor use results in an error to avoid
+// problems creeping in.
+
+trait TNew {
+    public function __construct() {
+        echo "TNew executed\n";
+    }
+}
+
+class ReportCollision {
+    use TNew;
+
+	public function ReportCollision() {
+	    echo "ReportCollision executed\n";
+	}
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
\ No newline at end of file

Added: php/php-src/trunk/Zend/tests/traits/bug55554g.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55554g.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/traits/bug55554g.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -0,0 +1,29 @@
+--TEST--
+Bug #55137 (Legacy constructor not registered for class)
+--FILE--
+<?php
+
+// Ensuring that inconsistent constructor use results in an error to avoid
+// problems creeping in.
+
+trait TLegacy {
+    public function ReportCollision() {
+        echo "TLegacy executed\n";
+    }
+}
+
+class ReportCollision {
+    use TLegacy;
+
+	public function __construct() {
+	    echo "ReportCollision executed\n";
+	}
+}
+
+
+echo "ReportCollision: ";
+$o = new ReportCollision;
+
+
+--EXPECTF--
+Fatal error: ReportCollision has colliding constructor definitions coming from traits in %s on line %d
\ No newline at end of file

Modified: php/php-src/trunk/Zend/tests/traits/language009.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/language009.phpt	2011-10-09 09:22:36 UTC (rev 317934)
+++ php/php-src/trunk/Zend/tests/traits/language009.phpt	2011-10-09 11:13:27 UTC (rev 317935)
@@ -22,13 +22,13 @@
    }
 }

-class Foo {
+class MyClass {
     use C, A, B {
 		B::foo insteadof A, C;
 	}
 }

-$t = new Foo;
+$t = new MyClass;
 $t->foo();

 ?>

Modified: php/php-src/trunk/Zend/zend_compile.c
===================================================================
--- php/php-src/trunk/Zend/zend_compile.c	2011-10-09 09:22:36 UTC (rev 317934)
+++ php/php-src/trunk/Zend/zend_compile.c	2011-10-09 11:13:27 UTC (rev 317935)
@@ -3654,22 +3654,7 @@
 }
 /* }}} */

-#define IS_EQUAL(mname, mname_len, str) \
-      strncmp(mname, str, mname_len)

-#define _ADD_MAGIC_METHOD(ce, mname, mname_len, fe) { \
-	if (     !IS_EQUAL(mname, mname_len, ZEND_CLONE_FUNC_NAME))       { (ce)->clone       = (fe); (fe)->common.fn_flags |= ZEND_ACC_CLONE; } \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_CONSTRUCTOR_FUNC_NAME)) { (ce)->constructor = (fe); (fe)->common.fn_flags |= ZEND_ACC_CTOR; } \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_DESTRUCTOR_FUNC_NAME))  { (ce)->destructor  = (fe); (fe)->common.fn_flags |= ZEND_ACC_DTOR; } \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_GET_FUNC_NAME))        (ce)->__get          = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_SET_FUNC_NAME))        (ce)->__set          = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_CALL_FUNC_NAME))       (ce)->__call         = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_UNSET_FUNC_NAME))      (ce)->__unset        = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_ISSET_FUNC_NAME))      (ce)->__isset        = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_CALLSTATIC_FUNC_NAME)) (ce)->__callstatic   = (fe); \
-	else if (!IS_EQUAL(mname, mname_len, ZEND_TOSTRING_FUNC_NAME))   (ce)->__tostring     = (fe); \
-}
-
 /* {{{ Originates from php_runkit_function_copy_ctor
 	Duplicate structures in an op_array where necessary to make an outright duplicate */
 static void zend_traits_duplicate_function(zend_function *fe, zend_class_entry *target_ce, char *newname TSRMLS_DC)
@@ -3790,8 +3775,40 @@
 	fe->op_array.brk_cont_array = (zend_brk_cont_element*)estrndup((char*)fe->op_array.brk_cont_array, sizeof(zend_brk_cont_element) * fe->op_array.last_brk_cont);

 }
-/* }}}} */
+/* }}} */

+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)) {	ce->clone = fe; fe->common.fn_flags |= ZEND_ACC_CLONE; }
+	else if (!strncmp(mname, ZEND_CONSTRUCTOR_FUNC_NAME, mname_len)) {
+		if (ce->constructor) {
+			zend_error(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name);
+		}
+		ce->constructor = fe; fe->common.fn_flags |= ZEND_ACC_CTOR;
+	}
+	else if (!strncmp(mname, ZEND_DESTRUCTOR_FUNC_NAME,  mname_len)) {	ce->destructor  = fe; fe->common.fn_flags |= ZEND_ACC_DTOR; }
+	else if (!strncmp(mname, ZEND_GET_FUNC_NAME,         mname_len)) ce->__get       = fe;
+	else if (!strncmp(mname, ZEND_SET_FUNC_NAME,         mname_len)) ce->__set       = fe;
+	else if (!strncmp(mname, ZEND_CALL_FUNC_NAME,        mname_len)) ce->__call      = fe;
+	else if (!strncmp(mname, ZEND_UNSET_FUNC_NAME,       mname_len)) ce->__unset     = fe;
+	else if (!strncmp(mname, ZEND_ISSET_FUNC_NAME,       mname_len)) ce->__isset     = fe;
+	else if (!strncmp(mname, ZEND_CALLSTATIC_FUNC_NAME,  mname_len)) ce->__callstatic= fe;
+	else if (!strncmp(mname, ZEND_TOSTRING_FUNC_NAME,    mname_len)) ce->__tostring	 = fe;
+	else if (ce->name_length + 1 == mname_len) {
+		char *lowercase_name = emalloc(ce->name_length + 1);
+		zend_str_tolower_copy(lowercase_name, ce->name, ce->name_length);
+		lowercase_name = (char*)zend_new_interned_string(lowercase_name, ce->name_length + 1, 1 TSRMLS_CC);
+		if (!memcmp(mname, lowercase_name, mname_len)) {
+			if (ce->constructor) {
+				zend_error(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ce->name);
+			}
+			ce->constructor = fe;
+			fe->common.fn_flags |= ZEND_ACC_CTOR;
+		}
+		str_efree(lowercase_name);
+	}
+}
+
+
 static int zend_traits_merge_functions_to_class(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
 {
 	zend_class_entry *ce = va_arg(args, zend_class_entry*);
@@ -3858,9 +3875,7 @@
 			zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occured during updating class method table", hash_key->arKey);
 		}

-		_ADD_MAGIC_METHOD(ce, hash_key->arKey, hash_key->nKeyLength, fn_copy_p);
-		/* it could be necessary to update child classes as well */
-		/* zend_hash_apply_with_arguments(EG(class_table) TSRMLS_CC, (apply_func_args_t)php_runkit_update_children_methods, 5, dce, dce, &dfe, dfunc, dfunc_len); */
+		zend_add_magic_methods(ce, hash_key->arKey, hash_key->nKeyLength, fn_copy_p TSRMLS_CC);

 		zend_function_dtor(fn);
 	} else {
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to