Member since
It's never made sense to me that new
doesn't automatically begin the construction of the object, instead we've had to encapsulate it into an order of operations. It's unnecessary noise in the code.
For those saying "No," ask yourselves if it's the feature itself you're opposed to, or the level of knowledge you will need in order to use it properly. Then ask whether you'll need this language feature. If you don't need it, don't use it. If you don't want to learn it, don't. But I feel it's good for the language to regain traction in the development space if it's capable of the same patterns as others.
Aside from competition, I like this feature for the following:
The syntax in the RFC adds complexity, yes. But we've all gotten used to other degrees of complexity as the language has grown and matured. Remember when visibility wasn't even a thing yet? Remember when constructing an object required a method matching the class's name? Growing pains hurt sometimes, but that doesn't mean they're not worthwhile.
Asymmetric visibility allows for properties that are publicly readable but privately writable. This means that while the value can be accessed from anywhere, it can only be changed in controlled, intentional ways. By making a property writable only within a private scope, you can ensure that only specific methods or functions can modify it. This reduces the risk of unintended side effects from external code, which can potentially corrupt the state of the object.
It adds complexity, it can be confusing, but i think it's an important step forward for php to implement such feature.
For the readonly vs. asymmetric visibility discussion: you can mix them up since they aim a different goals (write-once vs writing from).
Readonly properties only allow you to set a property in the constructor. Some times, you want to make your property public while only ensuring that it's only changeable within the confines of your class. This feature will make that possible
i like this just for cutting down the abuse of magic methods. Likewise, if you opt for concrete get/set methods, you add irritating boilerplate, and clutter up functionality. The interface has to be concerned with implementation (i.e. knowing what properties are expected), or API documentation has to be coupled with specific implementation.
i see the constructive points being made against this (more than just "ew it looks like C#"), but i think this feature can coexist with other patterns. There's no magic bullet design pattern, so it's naive to say pattern A always is better than B so B shouldn't exist.
i'm on board with those who dislike the magic variables. Replacing magic __get()
/__set()
with magic variables is nauseating.
i think this RFC needs to require arrow-functions v2 before it gets reconsidered. Then the hooks can use that syntax with more concrete code:
class Foo { public string $bar { get: ($field) => { // do multi-line stuff }, set: (&$field, $value) => { // do multi-line stuff } }; }
$field
is defined and passed with the concrete property's value. For get
it's a pass-by-value to avoid magic writes back to the property, and in set
it's a reference to confer the new value back to the property. The syntax is required, but you can name the variables whatever makes sense to you (same as you can do with magic method parameters).
i really like the intention of this RFC, but as i see it the method resolution is backward. If you have two Interfaces that define the same method with incompatible signatures, the first-used Interface is blamed for being incompatible with the second:
interface I1 { public function foo(); } interface I2 { public function foo(string $bar): int; } // The latter interface method's signature wins the compatibility requirement: class C implements I1, I2 { } // Fatal error: Declaration of I1::foo() must be compatible with I2::foo(string $bar): int class C implements I2, I1 { } // Fatal error: Declaration of I2::foo(string $bar): int must be compatible with I1::foo()
The RFC would reverse this, keeping the method from the first-used. This also departs from replacement patterns found elsewhere, like associative arrays (the last definition of a key wins) and inheritance (an instance's members visible to the parent chain override the parents).
A lot of posts point out that JavaScript's syntax matches what's proposed here. If PHP remains a hold-out by not adding this syntax, it feeds the reputation that PHP is always behind the curve.
Marketability aside, i get sick of writing anonymous functions with use ()
statements where i need to make changes to an imported variable, and thus have to import by-reference. It's clunky, especially when references are so frowned-upon. To me it's much cleaner to have a short-closure where i know all data in scope remains in the same scope of that function.
My chief complaint about the syntax is it blocks functions with multiple parameters. This kneecaps the piping capability, and you're forced back into inside-out calls, or stopping a pipe to call a prohibited function, then start a new pipe with that result.
The operator itself |>
is fine, and to me it reads easily as one expression sending a value forward. What i dislike is the representation of the right-side callable: " Hello world " |> 'trim'
or "Hello world" |> [$object, 'method']
... In my opinion, the language already has too much of this workaround syntax.
First-class callable syntax " Hello world " |> trim(...)
is helpful in that regard, but still falls apart when multiple arguments are needed.
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') ) ) );
this syntax is unclear, and functions who gets multiple arguments will not be properly supported.