Bug #67385 [Nab]: Static property value on trait copied to object if set before class definition.

From: Date: Fri, 06 Jun 2014 09:30:41 +0000
Subject: Bug #67385 [Nab]: Static property value on trait copied to object if set before class definition.
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-186081@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=67385&edit=1

 ID:                 67385
 Updated by:         requinix@php.net
 Reported by:        arjen at react dot com
 Summary:            Static property value on trait copied to object if
                     set before class definition.
 Status:             Not a bug
 Type:               Bug
 Package:            Scripting Engine problem
 Operating System:   Linux
 PHP Version:        5.5.13
 Block user comment: N
 Private report:     N

 New Comment:

tl;dr: trait "inheritance" includes data in the trait, and since classes using traits get
compiled at runtime, it's possible to modify the trait's state in such a way that
different classes get different data.

> C should not inherit the static properties of trait A, just like B doesn't.
Ah, that may be the cause of the confusion.
B *is* getting the values of the static properties - you just don't notice because they're
identical to the values originally defined.
Try changing it back to null: http://3v4l.org/5uN3n
Or modifying $_a before the first class definition: http://3v4l.org/02nEs

> One would expect traits to be 'mixed' into a class at compile time
Yes. But it's not compile time of the file. They're mixed in when the class definition is
evaluated at runtime, just like as happens with definitions inside if blocks or loops.
And that link talks about flattening traits into other traits, not traits into classes.

> Consider this test script...
Okay, but even though the static variable is in a different place it's still the same concept
being illustrated in the original code: the first method being called is the trait's static
method, which modifies the state of the trait after it has been used by one class but not yet used
by another.

It's like putting stuff into a file somewhere, referencing that file once, modifying the file,
and then referencing the file a second time. Those two times will get different results even though
it's the same file.

> This clashes with...
The methods truly are independent of each other. There is no interaction between B and C, nor
between C1 and C2. The deciding factor is the state of A/Counter at the time the various classes are
being "compiled".

> Looks like this is related...
Yup. It is the same thing as bug #63454: the classes are not compiled during parsing (because they
use traits) and thus it's possible to modify the original class definition at runtime. When D
extends C it get the static $var whose value at that moment is "C", not the NULL as was
written in the code.

And yes, I said at the time it seemed like a bug, but since then I've understood why it acts
that way. So I do feel differently now.


Previous Comments:
------------------------------------------------------------------------
[2014-06-06 07:46:58] arjen at react dot com

Description:
Static property value on trait copied to object if set before class definition.

Test script:
http://3v4l.org/nP4Pj

or

<?php

trait A {
    protected static $_a;

    public static function setA($a)
    {
		static::$_a = $a;
	}

    public static function getA()
    {
        return static::$_a;
    }
}

class B {
    use A;


    public function __construct()
    {
        var_dump(static::$_a);
	}
}

A::setA('AAAA');

class C {
    use A;


    public function __construct()
    {
        var_dump(static::$_a);
	}
}

class D extends B {}

new B;
new C;
new D;

Expected result:
NULL
NULL
NULL

Actual result:
NULL
string(4) "AAAA"
NULL

Exactly, traits are not inheritance, so C should not inherit the static properties of trait A, just
like B doesn't.

One would expect traits to be 'mixed' into a class at compile time, not runtime so the
order of definition shouldn't matter (from https://wiki.php.net/rfc/horizontalreuse#traits_composed_from_traits)

Consider this test script http://3v4l.org/Oqrgt (slightly
modified from https://wiki.php.net/rfc/horizontalreuse#static_variables)

Counter::inc is now a static method, called once before C1 and C2 are defined.
Both C1 and C2 'inherit' the incremented value from the Counter trait.

This clashes with "The flattening property implies that all methods are independent from each
other at their usage point, even so, they might origin from the same method/trait." from the
trait RFC.

Looks like this is related to https://bugs.php.net/bug.php?id=63454 where you
commented "Seems like a bug: the order of class declarations and var_dumps matters."

------------------------------------------------------------------------
[2014-06-05 17:21:56] requinix@php.net

Traits are not inheritance. B was composed of trait A before $_a was set. C came after it was set.

Next time please follow the instructions for creating bug reports. Posting a link for the
description and test script is not enough.
http://bugs.php.net/how-to-report.php

------------------------------------------------------------------------
[2014-06-05 13:31:53] arjen at react dot com

Description:
------------
See http://3v4l.org/nP4Pj

Test script:
---------------
See http://3v4l.org/nP4Pj



------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=67385&edit=1


Thread (4 messages)

« previous php.bugs (#186081) next »