DEV Community

david duymelinck
david duymelinck

Posted on • Edited on

Php property hooks (now i understand)

Update: this post is heavy modified, because i didn't understand the goal of the property hooks. If you want to see the constructor property promotion validation examples that were in this post, i made a new post to let them shine on their own merits.

Yesterday morning I reacted on a post about the possible new php 8.4 feature, property hooks.


After a comment from the author, I saw my mistake and now this post is my take on what the property hooks feature is about.

My confusion

Based on examples like these

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
interface Named
{
    // Objects implementing this interface must have a readable
    // $fullName property.  That could be satisfied with a traditional
    // property or a property with a "get" hook.
    public string $fullName { get; }
}

// The "User" class above satisfies this interface, but so does:

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

I thought the feature was a step back from constructor property promotion.
The second example should have given me a clue, but I was too blind to see it.

What are property hooks then?

When you create a class in php you can add or get properties after the instantiation by calling them directly.

$a = new A();
$a->property = 'a';
Enter fullscreen mode Exit fullscreen mode

When calling a property directly php has a mechanism to make this work if the property is not explicitly set in the class. In the RFC they mention the term virtual property. The php term is dynamic property.

If you want to add a dynamic property you can do two things; create a setter method or use the __set magic method.

Property hooks provide a third method to do this with the set hook.
And the same goes for getting a dynamic property; use a getter method, use the __get magic method or use the set hook.

Should you use property hooks?

Since php 8.2 there is a deprecation notice when calling dynamic properties. There is an attribute to allow dynamic properties so if you want to use it you can.

If you are using the __set and __get methods, yes as soon as possible.

If you have a php 8.x code base there are going to be fewer cases where the feature makes sense.
One case where I can see a use is for data-object classes, most of the times the properties are public.

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

#[\AllowDynamicProperties]
class Person implements Named {
  public string $firstName = {
    set {
      if (strlen($value) === 0) {
         throw new ValueError("First name must be non-empty");
      }
      $this->firstName = $value;
    }
  } // dynamic property
  public string $lastName = ''; // actual property

  public string $fullName {
    get => "$this->firstName $this->lastName";
  }
}

$p = new Person();
$p->firstName = 'Me';
echo $p->fullName;
Enter fullscreen mode Exit fullscreen mode

It allows you to interact with the data-object class only using properties.

It is getting murkier if the properties are constructor arguments, because then you need the validation in two places.

#[\AllowDynamicProperties]
class Person {
  public string $firstName = {
    set {
      $this->validateFirstName($value);
    }
  }

  public function __construct(string $firstName) {
    $this->validateFirstName($firstName);
  }

  private function validateFirstName(string $n) {
     if (strlen($n) === 0) {
         throw new ValueError("First name must be non-empty");
      }
      $this->firstName = $n;
  }

$p = new Person('Me'); // not validated by the property hook
$p->firstName = 'You'; // validated by the property hook
Enter fullscreen mode Exit fullscreen mode

This is where I lost the distinction between dynamic and actual property.
Because they looks so alike.

In the RFC they only mention there is an error when a trait and a class have the same property. No mention was is going to happen when the same property is in two interfaces that are implemented in a class. I guess it also creates an error?

Conclusion

With the php 8.2 deprecation warning in mind, when will they decide to make it an error?
I guess they won't remove dynamic property feature all together because then the property hooks are going to stop working.

It looks like it is a spaceship operator niche feature that gets a lot of publicity because it of its novelty.

It will not be wide spread pattern in code bases.

Top comments (3)

Collapse
 
spo0q profile image
spO0q

I understand you're skeptical, but maybe you did not read this part :

A normal property has a stored ā€œbacking valueā€ that is part of the object, and part of the memory layout of the class. However, if a property has at least one hook, and none of them make use of $this->[propertyName], then no backing value will be created and there will be no data stored in the object automatically (just as if there were no property, just methods). Such properties are known as ā€œvirtual properties,ā€ as they have no materialized stored value.

Collapse
 
xwero profile image
david duymelinck • Edited

Thank you for showing the mistake in my opinion. The rewrite of the post is due to you comments.
If you find other things I am wrong on, let me know.

After understanding what the feature does, I'm more sure this is not something that is a game changer in the way we are going to write php code.

Collapse
 
spo0q profile image
spO0q • Edited

Hi, I don't think that's a mistake. RFCs can be pretty hard to understand, as there's a lot of abstraction.

While it's certainly not just a POC (Proof Of Concept), authors are creating the future of PHP, so it's experimental, by definition.

In addition, this particular RFC is huge, and I don't pretend to know everything. I'm just another developer.

You might want to check the Pull Request here