Is there any interest for object constructor shorthand *just for stdClass*

php.internals

tyson andre

5 years ago
Hi internals, e.g. `$x = object{key: object{'escaped-literal': $v2 }};` (equivalent to `$x = (object)['key' => (object)['escaped-literal' => $v2]];`) For example, in JS, non-string key literals are surrounded in `[]` to unambiguously reference them. Here, that could be `object{[$key]: $value, [MY_CONST]: $other}` - but `(MY_CONST)` would also work - https://externals.io/message/52990 was mentioned 9 years ago but would potentially conflict with future block expression RFCs - `object{key: $value}` is suggested here for similarity to named arguments Benefits: - Shorter and more readable code - casting an array to an object is an unintuitive and potentially hard to remember way to create an object literal - Make it easier for users to use objects for generic associative data instead of using arrays both for lists and associative data, in situations where it may result in more usable APIs For example, ``` $x = new stdClass{}; $x->prop = new stdClass{}; $x->prop->a = $a; $x->prop->b = $b; // Could be shortened to $x = object{prop: object{a: $a, b: $b}} // $x = (object)['prop' => (object)['a' => $a, 'b' => $b]] ``` This proposal is only for stdClass - there was not much interest in https://wiki.php.net/rfc/compact-object-property-assignment and the combination of named properties and constructor property promotion helps with the readability of other classes. Thanks, - Tyson

Larry Garfield

5 years ago
On Tue, Oct 20, 2020, at 5:53 PM, tyson andre wrote:
> Hi internals, > > e.g. `$x = object{key: object{'escaped-literal': $v2 }};` > (equivalent to `$x = (object)['key' => (object)['escaped-literal' => $v2]];`) > > For example, in JS, non-string key literals are surrounded in `[]` to > unambiguously reference them. > Here, that could be `object{[$key]: $value, [MY_CONST]: $other}` - but > `(MY_CONST)` would also work > > - https://externals.io/message/52990 was mentioned 9 years ago but > would potentially conflict with future block expression RFCs > - `object{key: $value}` is suggested here for similarity to named > arguments > > Benefits: > - Shorter and more readable code - casting an array to an object is an > unintuitive and potentially hard to remember > way to create an object literal > - Make it easier for users to use objects for generic associative data > instead of using arrays both for lists and associative data, > in situations where it may result in more usable APIs > > For example, > > ``` > $x = new stdClass{}; > $x->prop = new stdClass{}; > $x->prop->a = $a; > $x->prop->b = $b; > // Could be shortened to > $x = object{prop: object{a: $a, b: $b}} > // $x = (object)['prop' => (object)['a' => $a, 'b' => $b]] > ``` > > This proposal is only for stdClass - there was not much interest in > https://wiki.php.net/rfc/compact-object-property-assignment > and the combination of named properties and constructor property > promotion helps with the readability of other classes. > > Thanks, > - Tyson
The only data structure worse than an associative array -- from the point of view of performance, self-documentation, or usability -- is stdClass. :-) I can't recall using it in the last decade at least, and my code has been better for it. Especially now with constructor promotion and named properties, I'd prefer to just forget that stdClass exists and encourage others to do so as well. --Larry Garfield

Björn Larsson

5 years ago
Den 2020-10-21 kl. 01:30, skrev Larry Garfield:
> On Tue, Oct 20, 2020, at 5:53 PM, tyson andre wrote: >> Hi internals, >> >> e.g. `$x = object{key: object{'escaped-literal': $v2 }};` >> (equivalent to `$x = (object)['key' => (object)['escaped-literal' => $v2]];`) >> >> For example, in JS, non-string key literals are surrounded in `[]` to >> unambiguously reference them. >> Here, that could be `object{[$key]: $value, [MY_CONST]: $other}` - but >> `(MY_CONST)` would also work >> >> - https://externals.io/message/52990 was mentioned 9 years ago but >> would potentially conflict with future block expression RFCs >> - `object{key: $value}` is suggested here for similarity to named >> arguments >> >> Benefits: >> - Shorter and more readable code - casting an array to an object is an >> unintuitive and potentially hard to remember >> way to create an object literal >> - Make it easier for users to use objects for generic associative data >> instead of using arrays both for lists and associative data, >> in situations where it may result in more usable APIs >> >> For example, >> >> ``` >> $x = new stdClass{}; >> $x->prop = new stdClass{}; >> $x->prop->a = $a; >> $x->prop->b = $b; >> // Could be shortened to >> $x = object{prop: object{a: $a, b: $b}} >> // $x = (object)['prop' => (object)['a' => $a, 'b' => $b]] >> ``` >> >> This proposal is only for stdClass - there was not much interest in >> https://wiki.php.net/rfc/compact-object-property-assignment >> and the combination of named properties and constructor property >> promotion helps with the readability of other classes. >> >> Thanks, >> - Tyson > The only data structure worse than an associative array -- from the point of view of performance, self-documentation, or usability -- is stdClass. :-) I can't recall using it in the last decade at least, and my code has been better for it. > > Especially now with constructor promotion and named properties, I'd prefer to just forget that stdClass exists and encourage others to do so as well. > > --Larry Garfield
Well, but for us with a legacy code base where this is heavily used it would definitely be an improvement! It would enable a so called stepwise refinement :-) One typical usecase we have is populating an object with the result from a DB query or using an object to write to a DB. I'm also wondering how prevalent this feature is used in different Open Source libraries. r//Björn Larsson

Pierre

5 years ago
Le 21/10/2020 à 11:38, Björn Larsson a écrit :
> > Well, but for us with a legacy code base where this is heavily used > it would definitely be an improvement! It would enable a so called > stepwise refinement :-) > > One typical usecase we have is populating an object with the result > from a DB query or using an object to write to a DB.
Drupal prior to 8.x used to do this, I definitely hated it for it. I'd recommend that if your data is arbitrary, you can use arrays, if you it's not, you probably always should write typed value objects, it's much more comprehensive and stable (yeah IDE auto-completion and static analysis), and you'll benefit from typing your properties if you rely upon a decent PHP version.
> > I'm also wondering how prevalent this feature is used in different > Open Source libraries. > > r//Björn Larsson >
I'm not in favor of a shorthand syntax if it's only for \stdClass, but I'm in favor of having a shorthand syntax for all types and classes: both constructor property promotion and named parameters altogether already solve this particular feature quite nicely. \stdClass should probably retire at some point, it'll be honored to contribute some retirement gift and celebrate with a party if that happened :D
-- Pierre

Rowan Collins

5 years ago
On 21/10/2020 10:47, Pierre wrote:
> I'd recommend that if your data is arbitrary, you can use arrays, if you > it's not, you probably always should write typed value objects
This is pretty much what I was going to say. If you have truly dynamic keys, then having the full set of array functions available - e.g. array_map, array_filter, all the variations of sort - is almost definitely a win. If you know the property names in advance, then defining them on a class is going to help you catch a lot of mistakes. On 21/10/2020 10:38, Björn Larsson wrote:
> One typical usecase we have is populating an object with the result > from a DB query or using an object to write to a DB.
This is certainly common, and even has a built-in mode in PDO. However, I suspect that if we didn't have stdClass, we would no longer see the need to add it just for these use cases, because they could use an anonymous class instead. Which brings me back to the feature request: what if rather than stdClass, we look for a better syntax for creating and populating anonymous class instances? Then you could start with a literal and add real object behaviour to it - add methods, implement an interface, constrain the type of properties, etc. I think there are two problems we might want to solve there: 1. The only way to initialise the object with variables from the outer scope is to pass them to a constructor, which is a bit messy (although constructor property promotion improves things a bit, see example below). 2. There's no way to create a truly dynamic class, i.e. one with some or all properties determined at run-time, without extensive string manipulation and eval(). (Although you can simply write "$object = new class{}; $object->foo=42;" to add a public, untyped property). The proposed syntax would help with (1), but not really with (2), since you need to know at least the _number_ of properties in order to write out the literal. Examples, and some probably bad ideas, in the hope they inspire someone to better ideas: // 7.4 $object = new class($foo, $foo * 2, $nextBaz) {     public int $foo;     public int $bar;     public int $baz;     public function __construct($foo, $bar, $baz) {           $this->foo=$foo;           $this->bar=$bar;           $this->baz=$baz;     } }; // 8.0 $object = new class($foo, $foo * 2, $nextBaz) {     public function __construct(public int $foo, public int $bar, public int $baz) {} }; // Promote properties into named parameters, implicitly defining a constructor and passing them to it? $object = new class(public int foo: $foo, public int bar: $foo * 2, public int baz: $nextBaz) {}; // Extend this to accept an array spread? Means all properties have to have the same visibility and type, or default to "public mixed"... $data = ['foo' => 42, 'bar' => 84, 'baz' => 69]; // dynamic data, e.g. a DB result set row $object = new class(public int ...$data) {}; Regards,
-- Rowan Tommins (né Collins) [IMSoP]

Björn Larsson

5 years ago
Den 2020-10-21 kl. 22:45, skrev Rowan Tommins:
> On 21/10/2020 10:47, Pierre wrote: >> I'd recommend that if your data is arbitrary, you can use arrays, if you >> it's not, you probably always should write typed value objects > > > This is pretty much what I was going to say. If you have truly dynamic > keys, then having the full set of array functions available - e.g. > array_map, array_filter, all the variations of sort - is almost > definitely a win. If you know the property names in advance, then > defining them on a class is going to help you catch a lot of mistakes. >
We considered arrays once when migrating our PHP 5 codebase to PHP 7, but the cost benefit analyse didn't show any worth while business aspects. The only thing we needed to do was to add one line with new stdClass for the PHP 7 migration to succeed.
> > On 21/10/2020 10:38, Björn Larsson wrote: >> One typical usecase we have is populating an object with the result >> from a DB query or using an object to write to a DB. > > > This is certainly common, and even has a built-in mode in PDO. > However, I suspect that if we didn't have stdClass, we would no longer > see the need to add it just for these use cases, because they could > use an anonymous class instead.
True, we looked into anonymous classes as another option but it didn't improve readability, rather the opposite.
> > Which brings me back to the feature request: what if rather than > stdClass, we look for a better syntax for creating and populating > anonymous class instances? Then you could start with a literal and add > real object behaviour to it - add methods, implement an interface, > constrain the type of properties, etc.
That's a very good point! We have code today that uses stdClass, so it would probably only come into play for new code since the existing code runs perfectly fine :-)
> > I think there are two problems we might want to solve there: > > 1. The only way to initialise the object with variables from the outer > scope is to pass them to a constructor, which is a bit messy (although > constructor property promotion improves things a bit, see example below). > 2. There's no way to create a truly dynamic class, i.e. one with some > or all properties determined at run-time, without extensive string > manipulation and eval(). (Although you can simply write "$object = new > class{}; $object->foo=42;" to add a public, untyped property). > > The proposed syntax would help with (1), but not really with (2), > since you need to know at least the _number_ of properties in order to > write out the literal. > > > Examples, and some probably bad ideas, in the hope they inspire > someone to better ideas: > > // 7.4 > $object = new class($foo, $foo * 2, $nextBaz) { >     public int $foo; >     public int $bar; >     public int $baz; >     public function __construct($foo, $bar, $baz) { >           $this->foo=$foo; >           $this->bar=$bar; >           $this->baz=$baz; >     } > }; > > // 8.0 > $object = new class($foo, $foo * 2, $nextBaz) { >     public function __construct(public int $foo, public int $bar, > public int $baz) {} > }; > > // Promote properties into named parameters, implicitly defining a > constructor and passing them to it? > $object = new class(public int foo: $foo, public int bar: $foo * 2, > public int baz: $nextBaz) {}; > > // Extend this to accept an array spread? Means all properties have to > have the same visibility and type, or default to "public mixed"... > $data = ['foo' => 42, 'bar' => 84, 'baz' => 69]; // dynamic data, e.g. > a DB result set row > $object = new class(public int ...$data) {}; >
Interesting alternatives!
> > Regards, >
r//Björn L

Hans Henrik Bergan

5 years ago
@Rowan Tommins not saying this is a good idea or anything, but i just want to point out that there's another way to do it in 7.4 $object = new class($foo, $foo * 2, $nextBaz) { public int $foo; public int $bar; public int $baz; public function __construct($foo, $bar, $baz) { for($i=0;$i<func_num_args();++$i){ $name = ((new ReflectionParameter([$this,__FUNCTION__],$i))->getName()); $this->$name = $$name; } } }; haha finally found a use-case for variable-variables On Thu, 22 Oct 2020 at 14:40, Björn Larsson <bjorn.x.larsson@telia.com> wrote:

Andreas Bittner

5 years ago
> e.g. `$x = object{key: object{'escaped-literal': $v2 }};` > (equivalent to `$x = (object)['key' => (object)['escaped-literal' => $v2]];`) > > For example, in JS, non-string key literals are surrounded in `[]` to unambiguously reference them. > Here, that could be `object{[$key]: $value, [MY_CONST]: $other}` - but `(MY_CONST)` would also work > > - https://externals.io/message/52990 was mentioned 9 years ago but would potentially conflict with future block expression RFCs > - `object{key: $value}` is suggested here for similarity to named arguments > > Benefits: > - Shorter and more readable code - casting an array to an object is an unintuitive and potentially hard to remember > way to create an object literal > - Make it easier for users to use objects for generic associative data instead of using arrays both for lists and associative data, > in situations where it may result in more usable APIs > > For example, > > ``` > $x = new stdClass{}; > $x->prop = new stdClass{}; > $x->prop->a = $a; > $x->prop->b = $b; > // Could be shortened to > $x = object{prop: object{a: $a, b: $b}} > // $x = (object)['prop' => (object)['a' => $a, 'b' => $b]] > ```
Creating a new syntax, that is only used in context of \stdClass, feels like overkill. Unfortunately, I cannot come up with a use case for this syntax, outside of the \stdClass context. I would also debate the point of the new syntax being more readable compared to the `(object)` cast, but that very much depends on what the reader is used to. I would however like to point out that shorter isn't always better and sometimes more verbose code is easier to understand. Imho a language shouldn't look like code-golf by recommendation. But I don't want to and hope I didn't sidetrack the discussion. With regards to the matter at hand I can see where you are coming from. `(object)['prop' => 1, ...]` feels very indirect, needlessly creating an array instance just to get an object out of it. Wouldn't it be opportune to just use named arguments? ``` $x = new \stdClass( prop: new \stdClass( a: $a, b: $b ) ); ``` While this isn't as short as the object{} suggestion, it doesn't add any additional language constructs. The signature of \stdClass constructor would change to `\stdClass(...$defaultProperties)`. Maybe this is the kickoff for variadic property promotion `__constructor(public mixed ...$properties)`. The following would be true: `new \stdClass(1, 2, 3, prop: 1.2) == (object)[1, 2, 3, 'prop' => 1.2];` On the subject of dynamic/variable property names: I would suggest that a RFC for the syntax `functionA($parameterName: 'value')` should be submitted at some point. This would replace the already working `functionA(...[$parameterName => 'value'])`. Maybe we even find a way of using constants as well, which would could be something like your suggested `functionA([SOME_CONST]: 'value')`. This would allow everyone to benefit from the change, not only \stdClass. Closing I would like to make a short point with regards to the `(object)` cast: Both a cast of \stdClass to `(array)` and a cast of arrays to `(object)` are common use cases in (JSON-)API development. It is not unheard of for API signatures to contain certain properties of the JSON response object only if some conditions are met. Currently the best way to achieve this is either by dynamically adding properties to an existing \stdClass object, or by populating an array and `(object)` casting it later. - Andreas