Description
As noted in the "Future Scope" section of the property hooks RFC, reusable package hooks are not part of the first RFC, but the authors @iluuu1994 and @Crell envision it being added later. Here are some ideas of how it could be implemented.
Swift
The concept here is called property wrappers. Here's an example from their documentation:
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
rectangle.height = 10 // Width is set to 10
rectangle.height = 24 // Width is set to 12
Kotlin
The concept here is called property delegates. Here's an example of how these are implemented:
// Syntax
var name: String by NameDelegate()
// Compiled to
val name$delegate = NameDelegate()
var name: String
get() = name$delegate.getValue(this, this::name)
set(value) { name$delegate.setValue(this, this::name, value) }
See https://blog.kotlin-academy.com/kotlin-programmer-dictionary-field-vs-property-30ab7ef70531
Idea for PHP
Going down the same path as Kotlin (which the PHP RFC is inspired quite a bit from) but without introducing any new keywords, we could come up with the following:
class Environment {
// Syntax with "as" (doesn't conflict with syntax highlighting like "use" would)
public string $home as new ByLazy(fn() => getenv('HOME'));
// Compiled to the following which already works with PR #166 merged
private $__home_delegate= new ByLazy(fn() => getenv('HOME'));
public string $home {
get => $this->__home_delegate->get($this, (object)['value' => &$field]);
set => $this->__home_delegate->set($this, (object)['value' => &$field], $value);
}
}
The ByLazy
implementation is as follows:
class ByLazy {
public function __construct(private callable $init) { }
public function get($self, $property) {
return ($property->value??= [($this->init)()])[0];
}
public function set($self, $property, $value) {
$property->value= [$value];
}
}
Note: Using an array for a value will allow initializing the property to NULL.