A private field, a getter, a setter with one line of normalization. Three members for one idea, repeated through every entity folder I have ever opened. PHP 8.4 is two months old, the conference demos are done, and the honest question is which features earn a place in a working codebase.

My filter is simple. A feature is good when it deletes code.

Property hooks pass:

class User
{
    public string $email {
        set => strtolower(trim($value));
    }
}

The class is shorter and the normalization cannot be bypassed. But keep hooks boring. A hook that talks to the database is a magic getter from 2008 in new syntax.

Asymmetric visibility also passes. public private(set) string $id says what half of my getters were saying anyway: read freely, write only inside. Before it was a comment and discipline. Now the engine checks it.

Lazy objects I do not touch in application code. They exist for ORMs and DI containers. Doctrine and the containers will use them for proxies, my project will simply have fewer generated classes in the cache dir. Good. Not my layer.

One warning from real adoption. Check your tooling before you put hooks on entities. Some serializers and older static analysis versions still get confused by a property that is not a plain property. We hit one such case, rolled the hook back, waited a week for the library update.

The guideline I wrote for the team fits in two lines. Use a new feature when the diff is red. If the new syntax makes the file longer or cleverer, wait a year and look again.

I wanted hooks on the entities from day one. The rolled-back one was mine.