php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #70437 Changes to static properties in trait methods are not reflected in the class
Submitted: 2015-09-06 14:32 UTC Modified: 2015-09-07 11:10 UTC
Votes:3
Avg. Score:3.7 ± 0.9
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: davey@php.net Assigned: bwoebi (profile)
Status: Assigned Package: Scripting Engine problem
PHP Version: 7.0.0RC2 OS: OSX/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: davey@php.net
New email:
PHP Version: OS:

 

 [2015-09-06 14:32 UTC] davey@php.net
Description:
------------
If you have a trait that defines a static property, and you modify said property from within a trait inherited method, the modifications are done to a trait instance of the property, as opposed to the inherited-from-trait static property within the class.

The behavior differs slightly in PHP 5.4.7 - 5.5.29 in that if the methods are instance methods, then they will correctly modify the inherited-from-trait static property, but are broken for static methods. 5.6.0+ has the same behavior for static and instance methods.

Test script:
---------------
Static methods: https://3v4l.org/UZ6gq
Instance methods: https://3v4l.org/piUFm

Expected result:
----------------
Should modify the inherited-from-trait static property within the class

Actual result:
--------------
Modifies a trait-specific copy of the static property

Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-09-07 03:53 UTC] nikic@php.net
Imho the bug here is that we allow calls to static methods on traits. I don't think that's supposed to work and we should add this forgotten check in PHP 7.
 [2015-09-07 09:15 UTC] bwoebi@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: bwoebi
 [2015-09-07 09:15 UTC] bwoebi@php.net
Totally agreeing with Nikita here; I've just attached a small PR: https://github.com/php/php-src/pull/1506
 [2015-09-07 10:39 UTC] davey@php.net
The issue isn't when calling the method directly on the trait but also when calling it on the implementing class. Any changes to static properties within trait methods called on the implementing class are only reflected within the traits copy of it; rather than the implementing classes copy as it should be.
 [2015-09-07 10:45 UTC] bwoebi@php.net
Did you checkout the patch? It's always accessing the class' static property now as far as I see?
 [2015-09-07 11:02 UTC] gron@php.net
This is by design, changing that is a BC issue.
The design is chosen based on the desired 'copy-down' semantics of traits.
Static properties of traits are independent of the ones of classes using the trait.
Changing it means you break the copy-down semantics by adding another corner case where it is not consistent.
 [2015-09-07 11:10 UTC] bwoebi@php.net
Is it by design?
We don't know, it's neither documented nor mentioned in the original RFC.

Heck, the RFC itself proposed even a singleton trait.
With current semantics that trait could be used only exactly once, because the trait shares the static property. I guess that's not the purpose of traits, it's about horizontal *reuse*. Hence each class should have its own static properties and never use the static property of the trait.

Also, by the way, if they were supposed to be independent, then the classes shouldn't be copied the traits static properties (and methods too) into them.

We should have one or the other behavior, but having both behaviors is very confusing.
 [2016-03-08 20:53 UTC] eric at ericstern dot com
I'm experiencing this (or at least something very similar) as well, and have some additional information which may or may not be relevant:

If you call a static method that sets a property on the trait before a class uses it, that property is copied across. If the class is what triggers the trait to load and that same setter is called after the class using the trait is loaded (but still before access to that property), the value is null.  Basically, the autoload order matters.

While the examples below may not quite follow the original RFC's intent, I think it's fairly obvious what the code's intent is (conceivably, setting the same static property on all classes that use a given trait)

Interestingly, trying to use the trait's name instead of self errors out with invalid property access, even though the intent is *even* more obviously to get the value out of the trait. This case has completely unambiguous intent, but fails the worst.

Null property: https://3v4l.org/4pXor
Works as expected: https://3v4l.org/428lo
Access not allowed: https://3v4l.org/Lk3ll

tl;dr: `self` doesn't really work as an end-user would expect in traits. Maybe it should have been prevented outright in the initial design, but that ship has sailed. As someone just writing code (and not PHP internals), I expect static properties defined in traits to be shared across everything using that trait, even though that's not semantically in line with "compiler-assisted copy and paste"

For context, the main use case (for me) is to avoid injecting the same configuration change in thirty places, which is totally in line with the actual intent of using traits to write DRY code.
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Sun Nov 19 01:31:42 2017 UTC