Re: [PHP-DEV] [RFC[ Property accessor hooks, take 2

On Fri, 8 Mar 2024, Larry Garfield wrote:

Hi folks. Based on earlier discussions, we've made a number of
changes to the RFC that should address some of the concerns people
raised. We also had some very fruitful discussions off-list with
several developers from the Foundation, which led to what we feel are
some solid improvements.

PHP: rfc:property-hooks

Some comments and questions:

  Be aware, the detection logic works on $this->[propertyName] directly at
  compile time, not on dynamic forms of it like $prop = 'beep';
  $this->$prop. That will not trigger a backing value.

How can that not cause issues?

   The set hook's return type is unspecified, and will silently be
  treated as void.

What happens if you *do* specify a return type? Will it Error?

  Implicit ''set'' parameter

  If the write-type of a property is the same as its defined type
  (this is the common case), then the argument may be omitted
  entirely.

  …

  If the parameter is not specified, it defaults to $value.

I am not a fan of this "magical" behaviour. Do we *need* this short cut,
and the following "Short-set"?

  With asymmetric visibility that was previously proposed, the
  example can be further simplified.

But it isn't here, so why is this example (and the next one) in the RFC?
:slight_smile:

  Interaction with constructor property promotion

  … In particular, the shorthand version of hook bodies and the
  ability to call out to private methods if they get complicated
  partially obviate the concern about syntactic complexity.

Although that is true, it *does* add more complexity in tools that needs
to parse PHP, as there is now another piece of new syntax that needs to
be added (and tested with).

  ReflectionProperty has several new methods to work with hooks.

  getHooks(): array returns an array of \ReflectionMethod objects
  keyed by the hook they are for.

What will the name for the &get hook be? And shouldn't there be an enum
case for that as well?

cheers,
Derick

On Tue, Mar 19, 2024, at 11:16 AM, Derick Rethans wrote:

On Fri, 8 Mar 2024, Larry Garfield wrote:

Hi folks. Based on earlier discussions, we've made a number of
changes to the RFC that should address some of the concerns people
raised. We also had some very fruitful discussions off-list with
several developers from the Foundation, which led to what we feel are
some solid improvements.

PHP: rfc:property-hooks

Some comments and questions:

  Be aware, the detection logic works on $this->[propertyName] directly at
  compile time, not on dynamic forms of it like $prop = 'beep';
  $this->$prop. That will not trigger a backing value.

How can that not cause issues?

For most uses that should be fine. But it's more that trying to do anything else would be vastly more complicated and error prone for the implementation. And most people hated the alternate $field variable from Kotlin.

   The set hook's return type is unspecified, and will silently be
  treated as void.

What happens if you *do* specify a return type? Will it Error?

Yes, it's a parse error.

  Implicit ''set'' parameter

  If the write-type of a property is the same as its defined type
  (this is the common case), then the argument may be omitted
  entirely.

  …

  If the parameter is not specified, it defaults to $value.

I am not a fan of this "magical" behaviour. Do we *need* this short cut,
and the following "Short-set"?

We believe we do. One of the goals of the RFC is to reduce and avoid verbose boilerplate. The short-hands should be the common case, in practice. The only reason one would have to specify a set parameter is if there was a good reason to change the variable name or widen the set type. Both of those are a-typical situations.

  With asymmetric visibility that was previously proposed, the
  example can be further simplified.

But it isn't here, so why is this example (and the next one) in the RFC?
:slight_smile:

Many/most languages with accessors also have asymmetric visibility. Nikita's original RFC also combined them all into one. The two RFCs are not mutually dependent, but are mutually-supportive, by design. So this comes under the "why didn't we include feature X" heading, with an answer "that's in a separate RFC."

  Interaction with constructor property promotion

  … In particular, the shorthand version of hook bodies and the
  ability to call out to private methods if they get complicated
  partially obviate the concern about syntactic complexity.

Although that is true, it *does* add more complexity in tools that needs
to parse PHP, as there is now another piece of new syntax that needs to
be added (and tested with).

That's true, but it's also inherently true of hooks themselves. And any other syntax improvement to the language.

  ReflectionProperty has several new methods to work with hooks.

  getHooks(): array returns an array of \ReflectionMethod objects
  keyed by the hook they are for.

What will the name for the &get hook be? And shouldn't there be an enum
case for that as well?

&get isn't its own hook. It's still a "get" hook, it just returns by reference. So it will be available with the "Get" enum value. If the return-by-ref status needs to be known for whatever reason, that is already available on the ReflectionMethod object that is returned.

--Larry Garfield

Hello,

I'm a bit confused on inheritance. In the following example of a
proxy, do I need to be aware of a parent's hook and handle it
specially?

class Loud
{
    public string $name {
        get {
            return strtoupper($this->name);
        }
    }
}

class LoudProxy extends Loud
{
   public string $name {
       get {
           // detected the parent has a hook? //
           $return = parent::$name::get();
           // do something with return //
           return $return;
       }
   }
}

what happens if the Loud class later removes its hook implementation
(ex: moves it to the set hook)? Will my proxy now cause an error?

Would simply calling $this->name call the parents hook?

Robert Landers
Software Engineer
Utrecht NL

On Thu, Mar 21, 2024 at 5:15 PM Larry Garfield <larry@garfieldtech.com> wrote:

On Thu, Mar 21, 2024, at 9:10 AM, Robert Landers wrote:
> Hello,
>
> I'm a bit confused on inheritance. In the following example of a
> proxy, do I need to be aware of a parent's hook and handle it
> specially?
>
> class Loud
> {
> public string $name {
> get {
> return strtoupper($this->name);
> }
> }
> }
>
> class LoudProxy extends Loud
> {
> public string $name {
> get {
> // detected the parent has a hook? //
> $return = parent::$name::get();
> // do something with return //
> return $return;
> }
> }
> }
>
> what happens if the Loud class later removes its hook implementation
> (ex: moves it to the set hook)? Will my proxy now cause an error?
>
> Would simply calling $this->name call the parents hook?

Per the RFC:

"If there is no hook on the parent property, its default get/set behavior will be used. "

so parent::$name::get() will "read the parent property", which will go through a hook if one is defined, and just read the raw value if not. So there is no detection logic needed, and the parent can add/remove a hook without affecting the child.

Calling $this->name in LoudProxy's get hook will access backing property on LoudProxy itself, ignoring the parent entirely.

--Larry Garfield

Awesome! Thanks! I just wasn't sure what "default" meant in this context.

Robert Landers
Software Engineer
Utrecht NL

On Thu, Mar 21, 2024, at 9:10 AM, Robert Landers wrote:

Hello,

I'm a bit confused on inheritance. In the following example of a
proxy, do I need to be aware of a parent's hook and handle it
specially?

class Loud
{
    public string $name {
        get {
            return strtoupper($this->name);
        }
    }
}

class LoudProxy extends Loud
{
   public string $name {
       get {
           // detected the parent has a hook? //
           $return = parent::$name::get();
           // do something with return //
           return $return;
       }
   }
}

what happens if the Loud class later removes its hook implementation
(ex: moves it to the set hook)? Will my proxy now cause an error?

Would simply calling $this->name call the parents hook?

Per the RFC:

"If there is no hook on the parent property, its default get/set behavior will be used. "

so parent::$name::get() will "read the parent property", which will go through a hook if one is defined, and just read the raw value if not. So there is no detection logic needed, and the parent can add/remove a hook without affecting the child.

Calling $this->name in LoudProxy's get hook will access backing property on LoudProxy itself, ignoring the parent entirely.

--Larry Garfield