Thinking about it... in , why are interfaces and traits even different things?

Everything you can do with an interface:

  1. declare functions
  2. extend another interface
  3. "implement" it in a class
  4. use it as a parameter type

Item 1 (functions)

This is functionally the same as defining an abstract public function in a trait.

Item 2 (extend)

This is functionally the same as a trait use-ing another trait.

Item 3 (implementation)

This is functionally the same as use-ing a trait that consists entirely of abstract public functions.

Item 4 (parameter type)

This is the one thing a trait cannot do -- but I don't understand why it was done that way.

Note: this seems related to my trait implements wishlist item from earlier. At first I thought that they might be in conflict, but on looking at the syntax in more depth, I don't see how. A trait that uses/extends another trait is also declaring abstract functions in that trait as an implementation requirement.

Putting both of these suggestions together would both simplify the language and make it more powerful, I'd think.

experts: is there any non-destructive way to tell if a variable contains a reference rather than a literal value?

very mild PHP gripe 

array_key_exists() is just backwards, that's all. The array should come first, and then the index. Why, .

I keep forgetting to include the keyword function when declaring functions in . Does this mean my brain is pining for C++ or something? 🧠

7 was giving me the ever-popular blank-screen look, and it turned out to be entirely due to a break; which had somehow ended up in the wrong place.

That shouldn't be a showstopper, guize. :kestraglow:

There needs to be a companion to the final keyword in .

Background: The "final" keyword indicates that a method cannot be overridden in a child class.

Wish: There needs to be a first keyword to prevent accidentally creating a method ancestor which might get completely bypassed because a descendant doesn't call parent::[method] because it doesn't expect one to exist.

Either that, or a way to allow calls to parent::[method]() even when there's no method ancestor.

...or else a way to define a method such that each generation gets called automatically -- except that takes away sequencing control, which would cause problems.

:

You can get a list of an object's methods with get_class_methods(), but it would be really really nice to get additional info along with each listing -- mainly the filename and line where the method is defined.

You can make throw an error if a function is called without expected arguments, but not if a function is called with more arguments than expected.

I think maybe fixes this by allowing "void" as an argument type? I don't yet have a system with PHP8 on it, though, so I don't actually know.

If it doesn't, then this is a .

I think I've figured out one of the problems I'm running into with traits in .

Background

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[1] , 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[2] 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.)

Minor Obstacle

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 Problem

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 traits B,C, and D, each of which has its own OnCreate() function that needs calling.

I guess it can be done by aliasing OnEvent() in each one (OnEventB(), OnEventC(), 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 parent::OnEvent() from A::OnEvent() to get D::OnEvent(), which would then call parent::OnEvent() to get C::OnEvent()... and so on, up to B::OnEvent() calling parent::OnEvent() to get A's parent class's OnEvent()[3] .

A way to kluge that desired behavior, I suppose, would be to have a series of intermediate classes:

class AB hosts trait B
class AC hosts trait C and extends AB
class AD hosts trait D and extends AC
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 , or something else?)

Notes

  1. When I say "host class", I mean a class that has 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.
  2. This is one of the reasons I wish traits could "require" their host-class to be of a certain class-family, so a trait can prevent itself from being used by a class-family that it's not designed to work with.
  3. ...or it could work the other way: functions in hosted traits could take precedence over the host-class's functions -- so if class A hosts trait B, and class C extends A, and they all declare OnEvent(), then C calling parent::OnEvent() would call B::OnEvent() and B calling parent::OnEvent() would call A::OnEvent() -- that would be fine too.

Also, if trait A and trait B both have a function X(), and I use both of them in the same class, why not just use the X() from whichever trait is named last?

Show thread

Fatal error: Trait method FieldCollective has not been applied, because there are collisions with other trait methods on greenmine\workferret\cCommonTable in /home/htnet/site/git/greenmine/dropins/WorkFerret/lib/base.php on line 25

Ya know, , it would be reeeally helpful if you could tell me what methods are colliding, rather than just mentioning one that isn't.

KDevelop does a decent job of mapping out classes in a project, but does nothing at all for traits or interfaces.

Single-step debugging (SSD) with

a brief review

✅ SSD support in PHP (Xdebug module)
✅ SSD support in KDevelop (native)
🚫 instructions on how to activate SSD in KDevelop when working with an interpreted language
🚫 any information at all about how to use KDevelop with PHP, other than "there's a plug-in!" (which I have installed)

meh.

Mood: trying to use traits to self-impose coding conventions

Not sure if it's a good mood.

It's annoying that in I can't declare an interface with very general terms (e.g. a class that handles some kind of variable, type unspecified, in various ways) and then implement it with more specific terms (e.g. this class handles strings in the same ways, this other class handles objects of a certain class family...).

PHP issue 

Apparently you can do

declare(ticks=1) {
[ ...code... ]
}

instead of

declare(ticks=1);
[ ...code... ]

but you can't do

declare(strict_types=1) {
[ ...code... ]
}

instead of

declare(strict_types=1);
[ ...code... ]

...or at least not in PHP 7.4.

This isn't mentioned or explained in the documentation, as far as I can tell.

It also leaves open the question of how to turn on strict typing globally.

It's annoying the way inheriting traits in doesn't confer parentage.

Like... if trait B uses trait A which has function X() which is re-implemented in B, B can't call parent::X(); you have to alias it in the use statement and call the alias... or else split off the functionality you want them both to have in common into a separate function, and call that from each version of X().

(The latter technique is kind of good, in a way, since it forces you to be more descriptive and compartmental in your functions... but then why not make class inheritance work the same way?)

(albeit minor)

Apparently this is only actually supported for return values? I'm getting a warning trying to override an argument type with a class that descends from the original type...

I wish had an equivalent to dumping the memory address of an object -- some kind of unique identifier. It does have the === operator which lets you determine if two variables point to the same data-space, but sometimes that's not practical to use when debugging.

Show more

Woozle Hypertwin's choices:

Toot.Cat

The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!