I think I've figured out one of the problems I'm running into with traits in #PHP.
The canonical usage of a trait is to define a set of behaviors that work together -- a feature that classes can use, basically.
That often requires initializing some stuff within the trait.
If it's just a matter of setting initial values for member-variables, you can do that when those members are declared... but if it involves picking up information from the host-object , then some code needs to be executed before trying to access any of the trait's functionality.
There are a couple of ways to handle this.
A. The trait can declare a constructor, if it knows enough about the potential host classes to do that gracefully (i.e. without interfering with their construction processes) to execute the initialization code.
B. The base class can define an initialization event (I usually define
function OnCreate() : void) for the trait to implement.
C. The trait can check itself for initialization every time any of its methods are called. (This probably works, but hurts my optimizing-squirrel's soul.)
It starts to get tricky if the host-class also needs to do some initialization, because if
class A uses
trait B and they both define function X(), A::X() can't call parent::X() to access B::X()... but there's a syntax for hosting traits which allows their functions to be aliased to another name when hosted, so you could rename B::X() to B::Trait_X(), and then call that.
...which means that whenever you host the trait, you also have to remember to do the aliasing.
The real problem is when I have a set of traits that all get hosted together.
...and they all need initialization.
So say you have
class A which needs
D, each of which has its own
OnCreate() function that needs calling.
I guess it can be done by aliasing
OnEvent() in each one (
OnEventD()), then explicitly calling those each individually from
A::OnEvent()... but that seems messy and easy to get wrong. There's too much stuff that needs to be done manually.
I guess it can be slightly tidied by declaring
OnEvent() under a different name in each trait -- basically pre-aliasing them -- so the only messy part is that you have to explicitly call the aliased OnEvent()s from
A::OnEvent()... but it still seems like this kind of breaks how class-inheritance is supposed to work.
You should be able to just use a trait, and not worry about initialization.
I should be able to just call
A::OnEvent() to get
D::OnEvent(), which would then call
parent::OnEvent() to get
C::OnEvent()... and so on, up to
parent::OnEvent() to get A's parent class's
A way to kluge that desired behavior, I suppose, would be to have a series of intermediate classes:
class AB hosts
class AC hosts
trait C and
class AD hosts
trait D and
class A extends AD
...in which case calling up the parent chain would work as needed.... but again, that's kind of messy: more code to manually include.
I'm not sure which of those last two solutions creates the least technical debt, and I keep feeling like there's got to be a better solution, even without changing how PHP handles traits.
I guess I'll try some things and see how deep a pit I end up digging myself into...
(is this a #softwareGripe, or something else?)
used a trait. If class A has
use B;, then A is hosting B. Similarly, a "host object" is an instantiation of a class that is hosting a trait.
The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!