There are some arguments against the implementation and I agree to those. But in addition I don't agree to the concept itself, personally, because I am convinced that immutability is key when enforcing domain logic. E.g. the example above could be replaced by:
readonly class User { public function __construct(public string $name) { if (strlen($name) === 0) { throw new ValueError('Name must be non-empty'); } } public function withName(string $newName): self { return new self($newName); } }
Or, even better IMO, with value objects that validate themselves:
readonly class User { public function __construct(public Name $name) {} public function withName(Name $newName): self { return new self($newName); } } readonly class Name { public function __construct(public string $value) { if (strlen($value) === 0) { throw new ValueError('Name must be non-empty'); } } }
Granted, this is only one example and immutabilty is not a silver-bullet. But I haven't come across a usecase for property hooks that was really convincing yet
I think complaints about the syntax being messy are really about the array manipulation functions being inherently hard to format nicely.
To be clear I agree that the proposed example isn't great, nested closures are never going to win any readabillity prizes. If however we look at any non-array based manipulation I think the readabillity is objectively better:
$name = 'my_user_name' |> fn (string $string): string => str_replace('_', ' ', $string) |> strtolower(...) |> ucwords(...) |> trim(...);
Or without first class callables:
$name = 'my_user_name' |> fn (string $string): string => str_replace('_', ' ', $string) |> fn (string $string): string => strtolower($string) |> fn (string $string): string => ucwords($string) |> fn (string $string): string => trim($string);
Bonus: this also adds runtime type checks to each step. Eg strreplace
returns string|array
I can't imagine anyone would think this is better:
$name = trim( ucwords( strtolower( str_replace('_', ' ', 'my_user_name') ) ) );
..a controversial one indeed. Personally I would try to avoid this feature in my own code (just like I avoid traits and abstract classes because I feel that they are a sign of bad abstractions).
To me the fact that convinced me is the last section of the RFC:
This feature may be used to enhance existing interfaces in PHP. Countable could add function isEmpty(): bool { return $this->count() == 0; }. Iterator could add methods like map, filter, and reduce which behave similarly to array_map, array_filter, and array_reduce.
At least once a week, I throw away an array_map because it ended up looking too bloated and go with a classic foreach instead. Short Closures 2.0 without the use(...) block would've solved this problem, just 2 votes...