-
Notifications
You must be signed in to change notification settings - Fork 0
Implement property hooks via virtual properties #166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This way, stack traces / warnings will point to the correct location, we have a way to invoke parent hooks, and interface and abstract hooks will be subject to implementation checks
The RFC states: "Using $this->propertyName directly is supported, but not recommended"
On the ambiguity of
|
Kind | This member | Parent member |
---|---|---|
Constant | self::CONSTANT |
parent::CONSTANT |
Static method | self::method() |
parent::method() |
Static property | self::$property |
parent::$property |
Instance method | $this->method() |
parent::method() |
Constructor | never invoked directly | parent::__construct() |
Instance property | $this->property |
not possible or useful |
The most consistent way would be to use parent::$property
here. Determining whether to invoke get
or set
could be done by looking for assignment expression, and only invoking set
in those situations.
Possible alternative: $this->propertyName
Inside hooks, we should use $field
to reference the property rather than $this->propertyName
, which the RFC states is "supported by discouraged". If we drop this translation, we could use it as follows:
class Base {
public $test { get => 'Test'; }
}
class Child extends Base {
public $test { get => $this->test.'!'; }
}
$test= (new Child())->test; // "Test!"
See https://gist.github.com/thekid/d83bac22f30e3d3e41425bb703ab1d0b
Possible alternative: parent->propertyName
Same as above, a bit more specific and in line with #164, but would be ambiguous with accessing the property propertyName of a constant named parent:
define('parent', new class() { public $propertyName= 'Test'; });
$name= parent->propertyName; // "Test"
This would be inconsistent with parent::<method>()
syntax to invoke parent methods and the parent constructor.
Discussion in the original "Property accessors" RFC
An open question is how parent accessors may be invoked. A possible syntax is to use parent::$prop for the parent getter and parent::$prop = $value for the parent setter. As the “static-ness” of properties cannot change, this reference should be unambiguous.
This syntax can't extend to other accessor types though, so those would either have no way to invoke the parent accessor, or invoke it implicitly. For possible guard accessors, it would make sense to automatically chain the accessors (though the order is not entirely obvious). For lazy accessors this wouldn't be possible though.
Other syntax choices like parent::$prop::get() are somewhat ambiguous, for example this syntax looks like a static property access followed by a static method call.
In any case, adding support for parent accessors will be technically non-trivial, because we need to perform a modified-scope property access through a separate syntax.
|
See https://externals.io/message/122445#122478: > However, since it seems no one likes $field, we have removed it from the RFC
More changes, see https://externals.io/message/122445#122583, especially: ShorthandsThe $foo => expression shorthand has been removed. The legal shorthands are now: public string $foo {
get => /* evaluates to a value; */
set => /* assigns this value; */
} The set shorthand (with Type and nameOn a set hook, the user may specify both a type and name, or neither. (That is, ParentClarified that the parent::$foo::get() syntax works on a parent property regardless of whether it has hooks The rest of the changes seem to not have any effect on this implementation |
This cannot work in this implementation due to us relying on class B {
public mixed $x;
}
class C extends B {
public mixed $x {
set {
$f = parent::$x::set(...);
$f($value);
}
}
}
$c = new C();
$c->x = 0; |
Status: Waiting for PHP RFC to successfully be voted on |
The vote for the Property Hooks RFC is now open, see https://externals.io/message/123139 |
…iler into feature/property-hooks
…e with property type"
} | ||
} | ||
|
||
// TODO move this to lang.ast.emit.PHP once https://github.com/php/php-src/pull/13455 is merged |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Now works with both current PHP 8.4 and PHP 8.4 with php/php-src#13455 in place |
AST syntax is released: https://github.com/xp-framework/ast/releases/tag/v11.1.0 |
This pull request adds support for property hooks to all PHP versions
Example
This example calculates and caches the full name from first and last names
Implementation
The above results in the following being emitted:
__set
and__get
, exceptions raised would print incorrect line numbers in their stack trace!debug_backtrace()
to verify the calling scope.Shortcomings
Currently, property modifiers are unchecked, theaddressed in ed5003f__set
and__get
functions effectively make them public, we would need to usedebug_backtrace()
for this!The syntax for invoking parent selectorsaddressed in f21a615, see comment belowparent::$x::get()
is not implemented yet to be rewritten toparent::__get_x()
as the former is ambiguous, see RFC__set
/__get
, see Implement property hooks via virtual properties #166 (comment)See also