DEV Community

spO0q
spO0q

Posted on • Edited on

PHP 8.4: Property Hooks

PHP 8.4 is expected for this fall. Let's review the RFC "Property Hooks."

Disclaimer (08/06)

After reading a comment on this post, I think it should be indicated that the idea with this RFC is not to use it for anything and everything.

It's not meant to replace all cases, but can be very beneficial in case you need it (e.g., data object).

What are PHP RFCs?

RFC means "Request For Comments." It's a pretty old concept (probably older than Internet itself) many core teams and their community use to discuss and implement new features, deprecate obsolete code or enhance existing structures.

The process for PHP is pretty well-documented, so do not hesitate to read this page if you want more details.

Here we'll focus on a specific RFC that looks promising: Property Hooks.

Other notable RFCs

While we'll focus on Property Hooks, there are other RFCs you might want to read:

Where to find all accepted RFCs?

You can check this page.

Property hooks in short

This RFC aims to remove the hassle of using boilerplate (e.g., getters/setters) for common interaction with object's properties.

PHP 8.0 already allows promoting properties in the constructor, so it's far less verbose than it used to be:

class User 
{
    public function __construct(public string $name) {}
}
Enter fullscreen mode Exit fullscreen mode

However, the RFC underlines the fact there's no built-in way to add custom behaviors or validation to these properties, which ultimately brings developers back to clumsy and verbose solutions (boilerplate or magic getters/setters).

With property hooks, this could be built-in the language:

interface Named
{
    public string $fullName { get; } // make the hook required
}

class User implements Named
{
    public function __construct(private string $firstName, private string $lastName) {}

    public string $fullName {
        get => strtoupper($this->firstName) . " " . strtoupper($this->lastName);
    }
}
Enter fullscreen mode Exit fullscreen mode

What's the problem with getters and setters?

You may read this [old] introduction:

👉🏻 When used blindly, setters and getters can break encapsulation, as the idea is to prevent anybody from modifying the object from the outside, and you probably want to keep the implementation private.

Wrap up

PHP contributors seem more and more inspired by other languages (e.g. Kotlin).

The RFC only includes two hooks: set and get, but there could be more hooks in the future.

Top comments (5)

Collapse
 
xwero profile image
david duymelinck • Edited

I'm still on the fence if this is a useful feature for most code.

One of the only examples that make the feature viable for me is defining properties in interfaces. At the moment you will get a "Interfaces may not include properties" error.
But i wonder if it is a good thing to define properties on the interface level.
And how is php going to deal with name collisions?

interface Named
{
    public string $fullName { get; }
}

interface Address
{
    public string $fullName { get; }
}

class SimpleUser implements Named, Address
{
    public function __construct(public readonly string $fullName) {}
}
Enter fullscreen mode Exit fullscreen mode

If you look at the validation examples, they can be written a lot shorter.
From the RFC

class User 
{
    private string $_name;

    public function __construct(string $name) {
        $this->_name = $name;
    }

    public function __set(string $propName, $value): void {
        switch ($propName) {
            case 'name':
                if (!is_string($value)) {
                    throw new TypeError("Name must be a string");
                }
                if (strlen($value) === 0) {
                    throw new ValueError("Name must be non-empty");
                }
                $this->_name = $value;
                break;
            default:
                throw new Error("Attempt to write undefined property $propName");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The property hook example

class User 
{
    public string $name {
        set {
            if (strlen($value) === 0) {
                throw new ValueError("Name must be non-empty");
            }
            $this->name = $value;
        }
    }

    public function __construct(string $name) {
        $this->name = $name;
    }
}
Enter fullscreen mode Exit fullscreen mode

You could write it almost as the property hook example

class User {
    public function __constructor(public string $name) {
         $this->validateName($name):
   }

  private function validateName(string $name) {
     if (strlen($value) === 0) {
         throw new ValueError("Name must be non-empty");
     }
  }
}
Enter fullscreen mode Exit fullscreen mode

The thing that i first noticed about the property hook example, is that it reintroduces a lot of boilerplate. You need to set the argument value in the constructor and in the set hook.

I will wait for the implementation to make a decision, but for now I am leaning more to keep working the way i'm doing now than to start using the feature.

Collapse
 
spo0q profile image
spO0q • Edited

Hi, thanks for your comment, but I don' t quite understand your examples, especially the name collision. How would it be different from the current way to handle that (without interfaces) ? Besides, I would probably not use the same field for two different data, and an object for the address.

The idea is to have a more standardized implementation, so I don't see why it would reintroduce boilerplate.

EDIT: maybe read the part "virtual properties vs. real properties."

Collapse
 
xwero profile image
david duymelinck

Thank you for pointing out i understood the RFC wrong.

I based my opinion on the examples like

class User 
{
    public string $name {
        set {
            if (strlen($value) === 0) {
                throw new ValueError("Name must be non-empty");
            }
            $this->name = $value;
        }
    }

    public function __construct(string $name) {
        $this->name = $name;
    }
}
Enter fullscreen mode Exit fullscreen mode

It seems like the set hook is undoing the constructor property promotion.

But after reading it the property hooks are a way to overwrite the way php handles direct property calls. They are the better version of the __get and __set methods.

Thank you for making me aware of my bad opinion

Thread Thread
 
spo0q profile image
spO0q

No problem. This RFC is not that simple to understand, IMHO, and aims to override existing mechanisms.

I've updated my post because your comment made me realize readers may think that's the new way to handle things, while it's another layer to standardize common implementations in a specific context.

I'm really excited by this new feature, though, and can't wait to see in action in real cases, but, as you guessed, we won't use it all the time.

Collapse
 
icolomina profile image
Nacho Colomina Torregrosa

I don't quite see the usefulness of hooks. I usually do this kind of logic, generating a fullname by using a firstname and lastname or validating a property or properties values, outside of an object with getter & setters. This way the data manipulation or validation logic is decoupled from the object that simply has to act as a data carrier.
I think there will be situations where hooks will be really useful. Looking forward to see hooks in action :)