Skip to content

Reusable property hooks #167

@thekid

Description

@thekid

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

See https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/#Property-Wrappers

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.

See also

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions