php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #67385 Static property value on trait copied to object if set before class definition.
Submitted: 2014-06-05 13:31 UTC Modified: 2014-06-06 09:30 UTC
From: arjen at react dot com Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 5.5.13 OS: Linux
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: arjen at react dot com
New email:
PHP Version: OS:

 

 [2014-06-05 13:31 UTC] arjen at react dot com
Description:
------------
See http://3v4l.org/nP4Pj

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


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-06-05 17:21 UTC] requinix@php.net
-Status: Open +Status: Not a bug
 [2014-06-05 17:21 UTC] 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-06 07:46 UTC] 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-06 09:30 UTC] requinix@php.net
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.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 21:01:27 2024 UTC