Good Morning Internals,
I am aware that traits in general are a topic where people can have
strong opinions, so I wanted to get an idea of initial thoughts on
this before deciding if it is worth looking into any further.
At the moment, there are three functions in SPL that allow access to
what a given class is using in some way:
-
class_implements -
class_parents -
class_uses(returns the traits the current class uses)
class_uses is unique out of the three in that it only returns traits
declared on the current class as opposed to the parent class (and
higher in inheritance). While this is documented in the manual,
looking at the original bug ticket that introduced class_uses
(https://bugs.php.net/bug.php?id=55266) then it isn't documented why
it only returns traits on the current class. As far as I can tell from
the bug ticket and the code, class_implements was copied for
class_uses and while _zend_class_entry->interaces contains all
interfaces (including from the parent classes),
_zend_class_entry->interaces->trait_names does not.
So, in terms of my question; what would be the general thoughts on a
new optional argument to class_uses that iterates to each parent
class, adding the traits from the parent as well?
Thanks,
Robert
Le 25 mars 2026 à 08:37, Robert Humphries contact@developer-rob.co.uk a écrit :
it isn't documented why it only returns traits on the current class
The reason is given here: https://bugs.php.net/bug.php?id=61554#1333085728 :
Traits are outside the inheritance system: the
parent class is effectively defined by the composition of its own
methods/properties and any traits it uses, so trait usage is not actually
inherited. Furthermore, it doesn't really make sense to reflect trait usage down
the class hierarchy because the same trait may be used more than once within a
class hierarchy.
—Claude
it isn't documented why it only returns traits on the current class
The reason is given here: https://bugs.php.net/bug.php?id=61554#1333085728 :
Traits are outside the inheritance system: the
parent class is effectively defined by the composition of its own
methods/properties and any traits it uses, so trait usage is not actually
inherited. Furthermore, it doesn't really make sense to reflect trait usage down
the class hierarchy because the same trait may be used more than once within a
class hierarchy.
Thanks Claude, that is useful to note, when I initially looked, I was
looking for class_uses in particular as opposed to getTraits from
Reflection.
I am not sure I agree with the reasoning though. If you are using
class_uses in the first place to check for a trait's presence, then
I can't find any difference in use cases for if a trait is defined at
the current level or parent level. Although the trait is not
inherited, the effect of the trait is.
(While you might get differences in behaviour if using multiple traits
and specifying insteadof to resolve conflicts, what functions from
the trait that were used/not used are not provided by class_uses, so
the same trait being used more than once doesn't affect any changes to
the function other than needing to dedupe the list)
~ Robert
Good Morning Internals,
I am aware that traits in general are a topic where people can have
strong opinions, so I wanted to get an idea of initial thoughts on
this before deciding if it is worth looking into any further.At the moment, there are three functions in SPL that allow access to
what a given class is using in some way:
class_implementsclass_parentsclass_uses(returns the traits the current class uses)
class_usesis unique out of the three in that it only returns traits
declared on the current class as opposed to the parent class (and
higher in inheritance). While this is documented in the manual,
looking at the original bug ticket that introducedclass_uses
(https://bugs.php.net/bug.php?id=55266) then it isn't documented why
it only returns traits on the current class. As far as I can tell from
the bug ticket and the code,class_implementswas copied for
class_usesand while_zend_class_entry->interacescontains all
interfaces (including from the parent classes),
_zend_class_entry->interaces->trait_namesdoes not.So, in terms of my question; what would be the general thoughts on a
new optional argument toclass_usesthat iterates to each parent
class, adding the traits from the parent as well?
Frankly I don't like the existence of these 3 functions in SPL.
This is very much in Reflection territory, and it should only ever be in Reflection.
SPL is already a mess of badly designed "features" trying to cohabitate.
The Reflection extension is designed to give you access to do introspection with a well-defined API.
But more over I don't even think you addition makes that much sense.
Traits are not part of the inheritance hierarchy, that's because the "composition" of traits is effectively just copy and paste.
Interfaces are fully inherited onto any child class, so there is no need to iterate through the parents to gather all interfaces.
Traits do not work this way, any "prior" use information is not transmitted due to the copy and paste nature of them.
The current API doesn't even tell you if used traits also use other traits.
I don't even know how it handles trait aliases, or if it even cares about them.
And that's not even going through overridden trait methods/props/constants at any stage of the inheritance chain.
So I'm very much not convinced about this, nor do I see really the utility of wanting this information?
But anyway here is a small snippet that will fetch all used traits: https://3v4l.org/kvQGR
Best regards,
Gina P. Banyard
Sorry for the delay here, been quite busy with other work related tasks.
Frankly I don't like the existence of these 3 functions in SPL.
This is very much in Reflection territory, and it should only ever be in Reflection.
SPL is already a mess of badly designed "features" trying to cohabitate.
The Reflection extension is designed to give you access to do introspection with a well-defined API.
I don't necessarily disagree with this, especially about SPL in
general; however we are at a point where we have them currently (and
the functions are not deprecated/warned against use/etc). That said, I
certainly wouldn't be suggesting adding the function if it didn't
already exist.
But more over I don't even think you addition makes that much sense.
Traits are not part of the inheritance hierarchy, that's because the "composition" of traits is effectively just copy and paste.
Interfaces are fully inherited onto any child class, so there is no need to iterate through the parents to gather all interfaces.
Traits do not work this way, any "prior" use information is not transmitted due to the copy and paste nature of them.
Note with this, the same constraints apply to parent classes in terms
of the information stored in C - and ReflectionClass will return
only the parent as opposed to grandparents/etc; but class_parents
returns the parent class / grandparent class / etc. I think this is
the strongest argument for making such a change to class_uses -
currently it is inconsistent with the other SPL functions (and I
wouldn't say that modifying the function is going to cause millions of
developers to start using the function, it is more about making the
behaviour a little more consistent).
The current API doesn't even tell you if used traits also use other traits.
As per your example,ReflectionClass::getTraitsalso does not tell
you about any traits used by other traits, you need to query the
ReflectionClass for the trait returned by 'getTraits'.class_uses
will also accept a trait for$object_or_class, returning the traits
that trait uses. I would suggest that if a change was made to
class_uses to support optionally returning inherited traits then it
returning traits used on traits or the parent class would make sense.
I don't even know how it handles trait aliases, or if it even cares about them.
And that's not even going through overridden trait methods/props/constants at any stage of the inheritance chain.
class_usesseems to just return the names of the traits (mirroring
getTraitNamesonReflectionClassas far as I can tell).
So I'm very much not convinced about this, nor do I see really the utility of wanting this information?
But anyway here is a small snippet that will fetch all used traits: https://3v4l.org/kvQGR
In a private / tightly controlled codebase we are using a set of
traits to implement certain functionality at a higher level in the
inheritance chain; and then where needed checking for the existence of
the trait to tell if the functionality is supported or not (think
along the lines of if a trait could implement an interface). There are
various reasons why it isn't easily possible to use interfaces
directly on the relevant classes themselves in the particular codebase
(mainly related to refactoring work required as opposed to technical
reasons) - currently we have gone with a reduced version of the
example code (as we only need to know the names of traits implemented
by the classes as opposed to from traits using traits for our exact
use-case).
(As a quick side note, what I am really looking for proposal 2 from
https://wiki.php.net/rfc/traits-with-interfaces (or a form of trait
that does this) and then using traits with interfaces for composition,
however from what I can see on activity such as GH-11435 technical
reasons mean this isn't likely to proceed).
I guess the important questions is that regardless of the people's
thoughts on the functions themselves, what are the
benefits/disadvantages of making the change to the pre-existing
function (and if there are more benefits compared to disadvantages, do
those benefits justify introducing the change)? The example code
proves that the information can be gained from userland code; but the
code isn't a straight forward example - and I suspect it is a lot
slower (however you are not likely to do a large number of calls to
the userland or internals code). I still think personally is the
biggest benefit is it brings the behaviour of the three class_XXXX
functions in SPL fully inline with each other (that they all return
the parents/interfaces/traits that the class or any related
classes/traits use); and I am not seeing any disadvantages except that
the change not necessary (but I don't think any implementation would
be complex or hard to maintain - my thoughts here would it would
basically be the while in php_spl.c lines 125-128 (in
class_parents) plus if it was decided to include traits included by
traits then a similar check or recursive call to get traits used by
each trait in the array).
Le 19 avr. 2026 à 21:01, Robert Humphries contact@developer-rob.co.uk a écrit :
I think this is
the strongest argument for making such a change toclass_uses-
currently it is inconsistent with the other SPL functions (and I
wouldn't say that modifying the function is going to cause millions of
developers to start using the function, it is more about making the
behaviour a little more consistent).
I think that class_uses() should never have been implemented, because it gives a false sense of symmetry with class_implements() and class_parents(). Implemented interfaces and parent classes can be used as types on the corresponding object (and types are inherited), while used traits cannot. Those are fundamentally different concepts.
If, for some reason, you want to “cheat” and use traits as if they were inherited types, you are free to do that, but I don’t think that PHP should provide a built-in function that goes beyond what traits are intended for.
(As a quick side note, what I am really looking for proposal 2 from
https://wiki.php.net/rfc/traits-with-interfaces (or a form oftrait
that does this) and then using traits with interfaces for composition,
however from what I can see on activity such as GH-11435 technical
reasons mean this isn't likely to proceed).
See also https://wiki.php.net/rfc/interface-default-methods, which is, I think, what is really needed.
—Claude
I think that
class_uses()should never have been implemented, because
it gives a false sense of symmetry withclass_implements()and
class_parents(). Implemented interfaces and parent classes can be used
as types on the corresponding object (and types are inherited), while
used traits cannot. Those are fundamentally different concepts.
I agree
If, for some reason, you want to “cheat” and use traits as if they were
inherited types, you are free to do that, but I don’t think that PHP
should provide a built-in function that goes beyond what traits are
intended for.
That won't even work reliably. You can rename trait methods and override
them with methods with incompatible signatures and do other similar
stuff to make this use case a nightmare.
Therefore this:
and then where needed checking for the existence of
the trait to tell if the functionality is supported or not (think
along the lines of if a trait could implement an interface).
is totally impossible to do. A trait with a method does not guarantee
you that the method is available.
Anton