0

As of version 8.0, PHP has gained the ability to add attributes to various entities.

With PHP 8.2, creating dynamic properties has been deprecated: attempting to set a property on a class that does not have that property explicitly declared now yields a deprecation notice, and from PHP 9.0 will throw a fatal error.

In order to avoid this, the documentation says that classes should either use __get() and __set() to store ‘dynamic’ properties in a pre-declared array in the class (which is a workaround that has nothing to do with dynamic properties and solves nothing), or apply the AllowDynamicProperties attribute to the class.

The documentation says that attributes can be applied to classes, methods, functions, parameters, properties and class constants – notably omitting traits and interfaces. The original RFC, conversely, includes traits and interfaces.

In practice, it seems that userland attributes can be declared on traits and interfaces (i.e., they are not invalid code), but they are not actually applied when using/implementing the trait/interface in an instantiating class (i.e., the instance does not see the attributes). Attributes declared on properties inside a trait, conversely, do get applied and are available in the class instance.

With native attributes, things work differently: at least some of them – such as AllowDynamicProperties – are actively prohibited from being even declared in traits/interfaces (cf. this GitHub comment). This throws a fatal error:

#[\AllowDynamicProperties]
trait MyTrait {}

I can see why an attribute like this – or perhaps attributes in general – would not be desirable for an interface which, after all, provides no independent functionality and is exclusively a contract. So let’s leave interfaces on the side.

But to the extent that dynamic properties have value (and I think they do), I cannot see any reason why they should have less value in a trait than in a class. Traits can provide functionality that requires dynamic properties just as well as classes can.

Similarly, I don’t see any logical reason why attributes declared on traits should not be applied when instantiating a class that uses that trait, particularly when attributes on trait properties are applied.

So my question is two-fold: what is the logic behind (and/or the benefits of)

  • not applying attributes declared on traits to instantiating classes?
  • not even allowing certain native properties, like AllowDynamicProperties, to be declared on traits?
Janus Bahs Jacquet
  • 859
  • 1
  • 11
  • 27

1 Answers1

0

Traits cannot have instance properties, so applying this attribute is meaningless. Perhaps you expect that all the classes using the trait can automatically obtain this attribute, but using trait isn't equivalent to inheritance. To do so you need to write addtional code. Since this is already a deprecated feature and will be removed in the next version, it doesn't seem necessary to do this.

shingo
  • 18,436
  • 5
  • 23
  • 42
  • I would indeed expect instances of classes using the trait to then have the attribute, just like attributes added to a property in the trait _do_ carry over to the corresponding property in the class instance. Testing it with a userland attribute on the trait itself, I do see that it doesn’t work there either: I can add the attribute to the trait, but instantiating a class that uses the trait does not carry over those attributes. So I guess my question is _why_ this is so. Traits are not inheritance, but it’s close enough that I don’t see any reason why attributes in particular should be → – Janus Bahs Jacquet Jun 04 '23 at 10:06
  • → excluded from the list of things that are pulled into the using class. Also, if by “this” being a deprecated feature, you’re referring to the `AllowDynamicAttributes`, that is incorrect. It’s a brand-new feature in PHP 8.2. What’s deprecated is dynamically creating properties **without** applying this new attribute (or using `__set()` and `__get()` to make pseudo-dynamic properties by saving them in a declared class property) . – Janus Bahs Jacquet Jun 04 '23 at 10:09
  • FYI, I have updated the question to include a more general approach about trait attributes in general, in addition to asking about `AllowDynamicProperties` specifically. – Janus Bahs Jacquet Jun 04 '23 at 10:30
  • You know, generally child classes do not carry over parent's attributes too, these built-in attributes are deliberately processed. Although using trait may appear to be close to inheritance, they are different implementions in the zend engine. I know that the `AllowDynamicProperties` attribute is new, but it's just for compatibility with previous versions and is not recommended to use. – shingo Jun 04 '23 at 11:05
  • There’s no indication that the ability to create dynamic attributes will be removed entirely in PHP 9. Attempting to create them _without_ applying `AllowDynamicProperties` will be upgraded from a deprecation warning to a fatal error, but I’ve seen nothing to indicate that the ability to explicitly opt in to allowing dynamic properties will be removed. But you’re right that child classes don’t seem to inherit parent class attributes either, only property attributes. That would explain it for traits as well (though still, why?). – Janus Bahs Jacquet Jun 04 '23 at 11:10
  • Perhaps the answer to your question 1 is: It is possible, but no plans to implement it. The introduction of trait is to solve the problem of function reuse. As for question 2, as I mentioned at the beginning of the answer, it is mainly a semantic issue, you can see that [readonly classes](https://www.php.net/manual/en/language.oop5.basic.php) cannot use the `AllowDynamicProperties` attribute for the same reason. – shingo Jun 04 '23 at 11:31