Alias stdClass to DynamicObject?

  115965
September 6, 2021 15:28 nikita.ppv@gmail.com (Nikita Popov)
Hi internals,

In the thread for deprecation of dynamic properties, Rowan suggested that
we alias "stdClass" to "DynamicObject" in
https://externals.io/message/115800#115802. I wanted to split this
discussion off into a separate thread, as this can be decided independently.

The rationale for this is that "stdClass" is something of a misnomer: The
name makes it sound like this is a common base class, as used in a number
of other languages. However, PHP does not have a common base class. The
only way in which "stdClass" is "standard" is that it is the return type of
casting an array to (object).

The actual role of stdClass is to serve as a container for dynamic
properties, thus the suggested DynamicObject name.

What do people think about adding such an alias? Is this worthwhile?

Regards,
Nikita
  115967
September 6, 2021 16:08 benjamin.morel@gmail.com (Benjamin Morel)
Hi Nikita, Rowan,

In the thread for deprecation of dynamic properties, Rowan suggested that
> we alias "stdClass" to "DynamicObject" in > https://externals.io/message/115800#115802. I wanted to split this > discussion off into a separate thread, as this can be decided > independently. > > The rationale for this is that "stdClass" is something of a misnomer: The > name makes it sound like this is a common base class, as used in a number > of other languages. However, PHP does not have a common base class. The > only way in which "stdClass" is "standard" is that it is the return type of > casting an array to (object). > > The actual role of stdClass is to serve as a container for dynamic > properties, thus the suggested DynamicObject name. > > What do people think about adding such an alias? Is this worthwhile?
Yes, please! A future where dynamic properties are only allowed on DynamicObject is bright. I would even deprecate the stdClass alias straight away, so that the whole thing can be gone in PHP 9. Kind regards, Benjamin
>
  115968
September 6, 2021 16:22 cschneid@cschneid.com (Christian Schneider)
Am 06.09.2021 um 18:08 schrieb Benjamin Morel morel@gmail.com>:
> Yes, please! A future where dynamic properties are only allowed on > DynamicObject is bright.
I have nothing against a DynamicObject alias for people who like to be more explicit in their code...
> I would even deprecate the stdClass alias straight away, so that the whole > thing can be gone in PHP 9.
.... but I'd prefer retaining a backward compatible version for library code, at least for a while and preferably without warnings :-) - Chris
  115970
September 6, 2021 16:49 tekiela246@gmail.com (Kamil Tekiela)
Hi Nikita,

I think this might be a good idea, but I would like to propose yet another
variant.
Replace stdClass with DynamicObject and keep stdClass as an alias. It can
be deprecated in 8.3.
If we only add an alias, I am afraid that it will not catch on quickly
enough. What I am proposing is that the cast to object will create
DynamicObject by default.

$arr = [1,2];
var_dump((object) $arr);
Output:
object(DynamicObject)#1 (2) {
  ["0"]=>
  int(1)
  ["1"]=>
  int(2)
}

It will break unit tests and it might break some code (e.g. `if ('stdClass'
=== $class)`), but it will help people understand what is the preferred
name going forward without deprecating it right now.

Regards,
Kamil
  115984
September 7, 2021 09:22 nikita.ppv@gmail.com (Nikita Popov)
On Mon, Sep 6, 2021 at 6:50 PM Kamil Tekiela <tekiela246@gmail.com> wrote:

> Hi Nikita, > > I think this might be a good idea, but I would like to propose yet another > variant. > Replace stdClass with DynamicObject and keep stdClass as an alias. It can > be deprecated in 8.3. > If we only add an alias, I am afraid that it will not catch on quickly > enough. What I am proposing is that the cast to object will create > DynamicObject by default. > > $arr = [1,2]; > var_dump((object) $arr); > Output: > object(DynamicObject)#1 (2) { > ["0"]=> > int(1) > ["1"]=> > int(2) > } > > It will break unit tests and it might break some code (e.g. `if ('stdClass' > === $class)`), but it will help people understand what is the preferred > name going forward without deprecating it right now. >
My only apprehension with making stdClass rather than DynamicObject the alias is the widespread impact this will have on extension testing code. https://github.com/php/php-src/pull/7475 shows the approximate impact on php-src. We need to update references to stdClass in var_dump() output in more than 300 tests. We can do this easily, but it will be an inconvenience for 3rd party extension tests that need to deal with more than one PHP version. Apart from that I agree that making DynamicObject the actual name and stdClass the alias would be better. Regards, Nikita
  115995
September 7, 2021 14:18 larry@garfieldtech.com ("Larry Garfield")
On Tue, Sep 7, 2021, at 4:22 AM, Nikita Popov wrote:
> On Mon, Sep 6, 2021 at 6:50 PM Kamil Tekiela <tekiela246@gmail.com> wrote: > > > Hi Nikita, > > > > I think this might be a good idea, but I would like to propose yet another > > variant. > > Replace stdClass with DynamicObject and keep stdClass as an alias. It can > > be deprecated in 8.3. > > If we only add an alias, I am afraid that it will not catch on quickly > > enough. What I am proposing is that the cast to object will create > > DynamicObject by default. > > > > $arr = [1,2]; > > var_dump((object) $arr); > > Output: > > object(DynamicObject)#1 (2) { > > ["0"]=> > > int(1) > > ["1"]=> > > int(2) > > } > > > > It will break unit tests and it might break some code (e.g. `if ('stdClass' > > === $class)`), but it will help people understand what is the preferred > > name going forward without deprecating it right now. > > > > My only apprehension with making stdClass rather than DynamicObject the > alias is the widespread impact this will have on extension testing code. > https://github.com/php/php-src/pull/7475 shows the approximate impact on > php-src. We need to update references to stdClass in var_dump() output in > more than 300 tests. We can do this easily, but it will be an inconvenience > for 3rd party extension tests that need to deal with more than one PHP > version. > > Apart from that I agree that making DynamicObject the actual name and > stdClass the alias would be better. > > Regards, > Nikita
Adding the alias, I'm fully on board with. Removing stdClass any time before PHP 10, I'm not on board with. The user space breakage potential is too large to even be thinking about that at this point. Flipping the alias around... again, I'm very concerned about the BC breakage. Apparently `readonly` caused problems for Wordpress, which is doubleplusungood. I would assume that we can't safely change which is the real name until proven otherwise, and based on Nikita's comments about PHP's own tests, I'd say it's definitely not proven otherwise. --Larry Garfield
  115974
September 6, 2021 17:50 internals@lists.php.net ("Björn Larsson via internals")
Den 2021-09-06 kl. 18:22, skrev Christian Schneider:
> Am 06.09.2021 um 18:08 schrieb Benjamin Morel morel@gmail.com>: >> Yes, please! A future where dynamic properties are only allowed on >> DynamicObject is bright. > > I have nothing against a DynamicObject alias for people who like to be more explicit in their code... >
Totally agree here!
>> I would even deprecate the stdClass alias straight away, so that the whole >> thing can be gone in PHP 9. > > ... but I'd prefer retaining a backward compatible version for library code, at least for a while and preferably without warnings :-) > > - Chris > Couldn't agree more on the statement "without warnings"!
I mean if one have code that is in production, is running perfect and one wants to upgrade to the newest and fanciest PHP version and the logs are flooded with warnings for a deprecation with zero impact on functionality. What do one do? Maybe just turn of the warnings for that code section / library ;-) r//Björn L
  115977
September 6, 2021 20:42 cdtreeks@gmail.com (Aran Reeks)
Creating a new DynamicObject class which is the preferred alias for
stdClass sounds logical but I too share the consern around making it the
default anytime soon. Too much code depends on an expected type of stdClass
presently which would likely make future upgrades more difficult for no
functional gain, beyond some added clarify.

With functions such as json_decode() still returning stdClass by default,
this is the main bug bare I can forsee where we commonly strict type
passing the decoded value throuh to another method or type. Would be nice
if in the future you could specify a class to populate instead of stdClass
- just a thought.

I too share the same view on deprecation notices being enfored and although
this would probably warrent its own thread, perhaps there benefit in
resolving this in itself under another RFC where a new php.ini directive,
or method call could be used to silence certain specified deprecation
warnings for those that are aware and understand the changes required in
the future so can knowingly make a decision to mute them.

On Mon, 6 Sep 2021, 18:50 Björn Larsson via internals, <
internals@lists.php.net> wrote:

> Den 2021-09-06 kl. 18:22, skrev Christian Schneider: > > Am 06.09.2021 um 18:08 schrieb Benjamin Morel morel@gmail.com > >: > >> Yes, please! A future where dynamic properties are only allowed on > >> DynamicObject is bright. > > > > I have nothing against a DynamicObject alias for people who like to be > more explicit in their code... > > > > Totally agree here! > > >> I would even deprecate the stdClass alias straight away, so that the > whole > >> thing can be gone in PHP 9. > > > > ... but I'd prefer retaining a backward compatible version for library > code, at least for a while and preferably without warnings :-) > > > > - Chris > > > Couldn't agree more on the statement "without warnings"! > > I mean if one have code that is in production, is running perfect and > one wants to upgrade to the newest and fanciest PHP version and the > logs are flooded with warnings for a deprecation with zero impact on > functionality. What do one do? Maybe just turn of the warnings for > that code section / library ;-) > > r//Björn L > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
  115979
September 7, 2021 01:05 hossein.baghayi@gmail.com (Hossein Baghayi)
On Tue, 7 Sept 2021 at 01:12, Aran Reeks <cdtreeks@gmail.com> wrote:

> ... Too much code depends on an expected type of stdClass > presently which would likely make future upgrades more difficult for no > functional gain, beyond some added clarify.
What if we had the DynamicObject as a subclass of stdClass? It would reduce the impact. I don't know if aliasing works the same as with subclassing (regarding type checking).
  115972
September 6, 2021 17:30 matthewmatthew@gmail.com (Matthew Brown)
> On Sep 6, 2021, at 11:29 AM, Nikita Popov ppv@gmail.com> wrote: >
I think this would be a massive benefit to first-time PHP users one or two years from now. I remember being confused by this terminology — I am sure bugs have been caused by people who assumed stdClass was a base class for all objects. Best wishes, Matt
  115973
September 6, 2021 17:36 internals@lists.php.net ("Björn Larsson via internals")
Den 2021-09-06 kl. 17:28, skrev Nikita Popov:
> Hi internals, > > In the thread for deprecation of dynamic properties, Rowan suggested that > we alias "stdClass" to "DynamicObject" in > https://externals.io/message/115800#115802. I wanted to split this > discussion off into a separate thread, as this can be decided independently. > > The rationale for this is that "stdClass" is something of a misnomer: The > name makes it sound like this is a common base class, as used in a number > of other languages. However, PHP does not have a common base class. The > only way in which "stdClass" is "standard" is that it is the return type of > casting an array to (object). > > The actual role of stdClass is to serve as a container for dynamic > properties, thus the suggested DynamicObject name. > > What do people think about adding such an alias? Is this worthwhile? > > Regards, > Nikita >
Well, we have legacy code where stdClass is quite prevalent and the code works just fine! In case of deprecation of stdClass I would like to point out that the benefit of replacing "new stdClass" with "new DynamicObject" in our existing code is absolutely zero. However, using DynamicObject in new code has a benefit since the intent is clearer. I think one also should take into account how much is stdClass described in documentation and books. Reason is that deprecation is not just about how it affects code. I'm a bit of a book worm so I'll check up on how the usage of stdClass is described in some PHP books. If deprecation of stdClass is on the table I think it should target the last 8.x or 9.0 zero release. r//Björn L
  115978
September 6, 2021 21:42 rowan.collins@gmail.com (Rowan Tommins)
On 06/09/2021 18:36, Björn Larsson wrote:
> In case of deprecation of stdClass I would like to point out that the > benefit of replacing "new stdClass" with "new DynamicObject" in our > existing code is absolutely zero. However, using DynamicObject in new > code has a benefit since the intent is clearer.
Just to mildly challenge this: Why is the intent of old code any less important? Do you never hire junior developers, or those cross-training from other languages, who have to maintain that code? Do you never have days where your brain is just a bit fuzzy, and every extra meaning you have to think about is one less line of code you'll get written? But yes, for experienced PHP developers, the name is not that big a deal. Indeed, a lot of PHP developers probably never even see it. It's one of those little "paper cuts", where if we can make the fix not too painful (including deprecation messages not being too annoying), it will pay off in the long run. Regards, -- Rowan Tommins [IMSoP]
  115996
September 7, 2021 16:15 marc@mabe.berlin (Marc Bennewitz)
On 06.09.21 17:28, Nikita Popov wrote:
> Hi internals, > > In the thread for deprecation of dynamic properties, Rowan suggested that > we alias "stdClass" to "DynamicObject" in > https://externals.io/message/115800#115802. I wanted to split this > discussion off into a separate thread, as this can be decided independently. > > The rationale for this is that "stdClass" is something of a misnomer: The > name makes it sound like this is a common base class, as used in a number > of other languages. However, PHP does not have a common base class. The > only way in which "stdClass" is "standard" is that it is the return type of > casting an array to (object). > > The actual role of stdClass is to serve as a container for dynamic > properties, thus the suggested DynamicObject name. > > What do people think about adding such an alias? Is this worthwhile?
I do like the idea very much as "stdClass" is most confusion and also wrong. The name "DynamicObject" is much better but I'm not sure if this is a good name either. * It's a class -> Why do we suffix it with "Object"? * Yes it's about dynamic properties - but is this the user goal to have dynamic properties or is it an implementation detail to get something else (map/dict)? Wouldn't it be better to name it for what purpose it's being used (map/dict/ordered dict/...) instead of how this is done? And if we want to go that route we could also add common functionalities to it like getting list of keys/values converting to array iterating etc. it's already possible right know by casting to array but it would be more logically. Yes we have assoc arrays in PHP to serve the purpose but this also has it's downsides as there is no type for it and the issue of converting an empty array from/to JSON (is it a list or a map?). The big difference, of course, is the by-ref vs. by-value. Hope that's not stupid questions? Greetings, Marc
  115997
September 7, 2021 17:28 tekiela246@gmail.com (Kamil Tekiela)
Hi Marc,

The name seems to be ok IMHO. Even though it is a class, the main use case
is to denote an object that was created dynamically rather than through an
instantiation of a class. And I know that a lot of usage comes from people
directly creating new instances of stdClass but if you do that then you can
use proper classes also (with Nikita's proposal to deprecate dynamic
properties one would have to use a class with defined properties to do the
same).
It's not unheard that a class has "object" in the name. e.g.
https://docs.oracle.com/javaee/7/api/javax/json/JsonObject.html or
https://docs.microsoft.com/en-us/dotnet/api/system.json.jsonobject?view=dotnet-plat-ext-5.0
The name DynamicObject is also used in other languages.
https://docs.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject?view=net-5.0
The word "Dynamic" semantically explains the purpose of the instance of
such class: it has no fixed structure.

We could call it Map or Dictionary, which would also be fitting, but in
doing so we are opening up the scope of this RFC. How much more
functionality does PHP need? What would be the goal of it? PHP has tons of
functions for working with arrays. If we introduce another type like Map
then it would be expected that a set of methods be added to it. This sounds
similar to scalar objects. And while it would be nice to have such kind of
functionality in PHP, it would also be a lot of work to implement it. If we
already have associative arrays, do we really need another structure to do
the same thing? I think it would be prudent to first understand why people
are using stdClass in the first place. From my experience, most of the time
people who work with stdClass don't expect it to have any methods. It's
just a container for data just like an array, but with a different syntax.
It's not much more different than an anonymous class.

The goal of Nikita's proposal is to retain the functionality of dynamically
assigning properties to an object after deprecating dynamic properties on
all other objects. The new class DynamicObject would be a special type of
class that would retain this functionality. Thus, the name fits perfectly.
A DynamicObject is a class whose properties can be created dynamically.

Regards,
Kamil
  116004
September 8, 2021 08:33 rowan.collins@gmail.com (Rowan Tommins)
On 07/09/2021 17:15, Marc Bennewitz wrote:
> > * It's a class -> Why do we suffix it with "Object"?
Classes are generally named to make sense when you have an instance: a class called "HttpRequest" is not saying that the *class* is a request, but that every *instance* is one, as in "$myRequest = new HttpRequest;" Similarly, "DynamicObject" is not saying that the *class* is an object (or dynamic, for that matter), but that every instance is: "$myObject = new DynamicObject;"
> * Yes it's about dynamic properties - but is this the user goal to > have dynamic properties or is it an implementation detail to get > something else (map/dict)?
I think a lot of uses of stdClass are precisely where people feel a map or dictionary *isn't* appropriate, and that's why they don't want to use an array: for instance, decoding a JSON string, or a database result row. The values aren't all of the same type, and you would never want to apply the same operation to all of them; you will probably initialise them once, and then access them by name, but don't have the facility to declare them up front. Regards, -- Rowan Tommins [IMSoP]
  115999
September 8, 2021 03:02 internals@lists.php.net ("Levi Morrison via internals")
On Mon, Sep 6, 2021 at 9:29 AM Nikita Popov ppv@gmail.com> wrote:
> > Hi internals, > > In the thread for deprecation of dynamic properties, Rowan suggested that > we alias "stdClass" to "DynamicObject" in > https://externals.io/message/115800#115802. I wanted to split this > discussion off into a separate thread, as this can be decided independently. > > The rationale for this is that "stdClass" is something of a misnomer: The > name makes it sound like this is a common base class, as used in a number > of other languages. However, PHP does not have a common base class. The > only way in which "stdClass" is "standard" is that it is the return type of > casting an array to (object). > > The actual role of stdClass is to serve as a container for dynamic > properties, thus the suggested DynamicObject name. > > What do people think about adding such an alias? Is this worthwhile? > > Regards, > Nikita
If the alias goes to vote as the name DynamicObject, then I will likely abstain. I don't care. If it goes to vote under some other name, perhaps "map", or "dict", then I would likely oppose it. I would like to keep those names available for more useful features. There are multiple feature that would like these names: 1. Hacklang-style dict, which is a value-based dictionary type. Importantly, this is copy-on-write like an array; it does not have object semantics. 2. In the event we ever add generics, it would be nice to have these names available for interfaces. I don't think an alias and replacement name for stdClass is important enough to use these names.
  116005
September 8, 2021 08:45 mike@newclarity.net (Mike Schinkel)
> On Sep 6, 2021, at 11:28 AM, Nikita Popov ppv@gmail.com> wrote: > > Hi internals, > > In the thread for deprecation of dynamic properties, Rowan suggested that > we alias "stdClass" to "DynamicObject" in > https://externals.io/message/115800#115802. I wanted to split this > discussion off into a separate thread, as this can be decided independently. > > The rationale for this is that "stdClass" is something of a misnomer: The > name makes it sound like this is a common base class, as used in a number > of other languages. However, PHP does not have a common base class. The > only way in which "stdClass" is "standard" is that it is the return type of > casting an array to (object). > > The actual role of stdClass is to serve as a container for dynamic > properties, thus the suggested DynamicObject name. > > What do people think about adding such an alias? Is this worthwhile?
So I am trying to get around what this would actually mean in practice, and what the ramifications would be. Given this proposal would `(object)array()` return an object of class 'DynamicObject' instead of 'stdClass'? Will I be able to do a `new DynamicObject` just like I can currently do `new stdClass`? I assume that instance would have a class of 'DynamicObject'? And what of `new stdClass`? Would `get_class(new stdClass)==='DynamicObject'` or would it continue to be `get_class(new stdClass)==='stdClass'` Or would this be just like any class we can define today but that would still support dynamic properties whereas all other classes would disallow dynamic properties per the other proposal? What about standard library functions that return a `stdClass` object? (I assume there has to be at least one but I am not certain there is.). Assuming there are standard functions that return objects, will they now return 'DynamicObject' or still 'stdClass'? =========== So while trying to get my head around the ramifications I prepared this bit of introspection, which is still missing some applicable tests I am sure: https://3v4l.org/MDlOn#v8.0.10 <https://3v4l.org/MDlOn#v8.0.10> =========== What I am struggling with here is to understand the vision for this change. Not that I find the change a bad idea — I do think it intimates a good direction — but at least as presented if feels like the vision for this may not have been fleshed out, or at least not fully communicated. Maybe what I am looking for is a vision for what this change would mean for future PHP. Is it just a special-cases that will cause as much confusion for developers trying to understand why there are too names for the same thing as it will help those who will better understand the new name, or is it more fully addressing the use-case for why people use dynamic objects? Maybe that's where we should start. I know there are some people who (I think) strongly argue that we should never use non-typed objects ever again but why do people use dynamic objects, still? This is a legitimate question, not a rhetorical, snide or sarcastic one. =========== So what are the use-cases where dynamic objects still make sense, and just as importantly what about those use-cases is still suboptimal besides just the class name? (I can think of a few, but want to hear from others before I explain mine.) -Mike
  116006
September 8, 2021 09:44 rowan.collins@gmail.com (Rowan Tommins)
On 08/09/2021 09:45, Mike Schinkel wrote:
> So I am trying to get around what this would actually mean in practice, and what the ramifications would be.
The proposal is that the new name is an "alias" - the same class, but with two names. So the following would all create exactly the same object: $obj = new stdClass; $obj = new DynamicObject; $obj = (object)[]; The following would be true for all of them: assert( $obj instanceOf stdClass ); assert( $obj instanceOf DynamicObject ); There are some places that have to return some "real" name for the class, like get_class($obj). The current proposal is that "stdClass" be kept as the real name, at least initially, so you would see this: $obj = new DynamicObject; echo get_class($obj); // stdClass
> So while trying to get my head around the ramifications I prepared > this bit of introspection, which is still missing some applicable > tests I am sure: > > https://3v4l.org/MDlOn#v8.0.10
You can get a better idea of what's being proposed using the class_alias() function, which lets you create an alias for a user-defined class: https://3v4l.org/a4EI0#v8.0.10
> Is it just a special-cases that will cause as much confusion for developers trying to understand why there are too names for the same thing as it will help those who will better understand the new name
That is certainly a risk, and a factor in favour of having some plan to retire the old name, even if only after a long overlap.
> or is it more fully addressing the use-case for why people use dynamic objects?
The thread comes from a comment I made in Nikita's discussion about deprecating dynamic properties on normal objects. There was no grand vision behind it, just a long-standing dislike of the name "stdClass", and a concern that documenting "extends stdClass" as a way to enable dynamic properties on a custom class would make the confusion worse.
> So what are the use-cases where dynamic objects still make sense, and just as importantly what about those use-cases is still suboptimal besides just the class name?
To be honest, I think most uses of stdClass would be better served by anonymous classes, and would have been written that way if the feature had existed for longer. The one thing missing from anonymous classes right now is an elegant way to capture data from outer scope when you create them. Regards, -- Rowan Tommins [IMSoP]
  116012
September 8, 2021 15:37 mike@newclarity.net (Mike Schinkel)
> On Sep 8, 2021, at 5:44 AM, Rowan Tommins collins@gmail.com> wrote: >> Is it just a special-cases that will cause as much confusion for developers trying to understand why there are too names for the same thing as it will help those who will better understand the new name > > That is certainly a risk, and a factor in favour of having some plan to retire the old name, even if only after a long overlap.
Ironically I believe if we add `DynamicObject` as an alias of `stdClass` where `DynamicObject::class === 'stdClass'` that would not provide much ability to retire `stdClass` any sooner than if we just deprecated it today. All future code that needs to refer to the class name will still refer to `stdClass`, so we won't be gaining much by creating an alias. OTOH if we introduce a **completely new class** named `DynamicObject` having exactly the same behavior as a `stdClass` object (at least initialize) then we could add any new behavior to `DynamicObject` and leave `stdClass` fully in-tact for as long as we need to, except for notes in the docs that say to move to `DynamicObject` and tools like PhpStorm, Psalm and Phan could start suggesting a move away from `stdClass` right away. With a new independent class `'DynamicObject' === get_class( new DynamicObject )` would always be true, and people who want to future proof their code could start replacing `stdClass` with `DynamicObject. People that don't (want to) make the change could maybe get a warning in PHP 9 and then maybe we remove `stdClass` in PHP and they must evolve or stick with PHP 8. #fwiw I think `DynamicObject` is the best semantic name floated thus far albeit sadly on the rather long side. `DynamicClass`, also long, could work too... ======== Of course having a different class begs the question of what `get_class((object)array())` should return? Clearly for BC it needs to be `stdClass`, but as long as it is `stdClass` then we get no real benefit from aliasing with DynamicObject. Further, although the expression style of `(object)['prop' => 'value']` is the simplest way to initialize a basic `object` with literals in a single expression it is still a bad workaround that is far from intuitive for new developers, and feels like we are just leveraging an accidental capability rather than a well-designed language feature. If we want `DynamicObject` to eventually replace `stdClass` why not ALSO provide new syntax to give people a reason to switch to it? Why not leverage the named constructor concept but as a special case for `DynamicObject`, and only for `DynamicObject` (not `stdClass`); allow any arbitrary names to be used in constructor promotion? Instead of this: $obj = (object)array( "foo" => 1, "bar" => "hello", "baz" => true, );o We could do this instead: $obj = new DynamicObject( foo: 1, bar: "hello", baz: true, ); AND since we are talking a singular special case, why not also add a `DynamicObject()` function to streamline it a bit: $obj = DynamicObject( foo: 1, bar: "hello", baz: true, ); OR we could get inspired by the shortening of `function()` to `fn()` and used obj() (since `do()` is probably unworkable): $obj = obj( foo: 1, bar: "hello", baz: true, ); OR my personal favorite (though I know that would be a bridge too far for many) why not just this, too? $obj = { foo: 1, bar: "hello", baz: true, }; While we are at it, DynamicObject could add a few really useful methods such as `fromArray()`, `toJSON()` and more? (And if I really want to get crazy, `toSqlUpdate()`, `toSqlInsert()`, etc, but now I fear I am just scaring people off.) If we gave a better developer experience with literal initializers and useful methods then we've give a lot of developers reason to update their code to use `DynamicObject` and thus we could deprecate `stdClass` sooner. A couple more things; add a `JSON_OUTPUT_DYNAMIC_OBJECT` flag to output to DynamicObject for `json_decode()`, add a 3rd parameter for flags to var_export() for the same reason, a `'return_dynamic_object'` option for `unserialize()`, and so on. (BTW, we don't have to do ALL these things in one RFC. But we could go ahead and discuss the potential of having future RFCs that would add these features.)
>> or is it more fully addressing the use-case for why people use dynamic objects? > > The thread comes from a comment I made in Nikita's discussion about deprecating dynamic properties on normal objects. There was no grand vision behind it, just a long-standing dislike of the name "stdClass", and a concern that documenting "extends stdClass" as a way to enable dynamic properties on a custom class would make the confusion worse.
Yes, I followed that discussion. But the fact you had a discussion doesn't ensure that the proposed solution is the proper one. Hence why I was trying to illicit a discussion about what would be more proper.
> The one thing missing from anonymous classes right now is an elegant way to capture data from outer scope when you create them.
I'm not exactly sure what that means, but if I had to guess I think maybe you are talking about a literally initialization syntax which is something I covered above? -Mike P.S.
> There are some places that have to return some "real" name for the class, like get_class($obj). The current proposal is that "stdClass" be kept as the real name, at least initially, so you would see this: > > $obj = new DynamicObject; > echo get_class($obj); // stdClass
Yes, and that is where I see it to be most problematic, although admittedly for a built-in class with no predefined methods or properties the places where it could break an application are certainly many fewer than aliases for userland classes. But I digress on this latter point...
> You can get a better idea of what's being proposed using the class_alias() function, which lets you create an alias for a user-defined class: https://3v4l.org/a4EI0#v8.0.10
Thanks for educating me on the existence of `class_alias()`; 12+ years of professional PHP and I can't remember ever noticing that existed. If it didn't exist I would challenge why we need it, especially because it breaks the boolean which I had thought was true, that $class_name === get_class( new $class_name() ). But since it does exist me arguing that would be moot.
>> So what are the use-cases where dynamic objects still make sense, and just as importantly what about those use-cases is still suboptimal besides just the class name? > > To be honest, I think most uses of stdClass would be better served by anonymous classes, and would have been written that way if the feature had existed for longer.
Hmm. That seems like replacing apples with oranges. I assume we would also disallow dynamic properties in anonymous classes too, right? After all, they are just statically declared classes that the developer do not assign a name. Anonymous class objects have different strengths and weaknesses and thus are useful in different contexts than dynamic objects.
> My guess would be that the name reported by get_class etc would mainly be used in tests and debugging, whereas instanceOf checks (and type constraints) will be used in actual "business logic" code paths. Existing code will expect "instanceOf stdClass" to work, and new code will expect "instanceOf DynamicObject" to work, so an alias (in either direction) seems the better compromise.
Besides, don't forget `stdClass::class` in addition to `get_class()`. Anyway, with respect, I'd like to hear use-cases from others who are the ones saying they are using a lot of stdClass objects in their existing code. They may have insight that neither you nor I have on the topic.
  116013
September 8, 2021 16:32 kjarli@gmail.com (Lynn)
On Wed, Sep 8, 2021 at 5:38 PM Mike Schinkel <mike@newclarity.net> wrote:

> A couple more things; add a `JSON_OUTPUT_DYNAMIC_OBJECT` flag to output to > DynamicObject for `json_decode()`, add a 3rd parameter for flags to > var_export() for the same reason, a `'return_dynamic_object'` option for > `unserialize()`, and so on. >
It would also be interesting to enter a user-defined class here to work as a reversed JsonSerializable. Could be a static factory method for all I care, and would omit the requirement of serialization libraries for "simple" things where you still want decent object typing. Could also work together with interfaces accepting properties, effectively making the result of json_decode an anonymous class that still adheres to an interface. In case of the custom 'deserialization' you can throw exceptions if the format is not correct. In the case of an anonymous class with an interface it could throw an exception if the structure doesn't match (possibly controlled by flags?).
  116015
September 8, 2021 19:58 andreas@dqxtech.net (Andreas Hennings)
On Wed, 8 Sept 2021 at 18:33, Lynn <kjarli@gmail.com> wrote:
> > On Wed, Sep 8, 2021 at 5:38 PM Mike Schinkel <mike@newclarity.net> wrote: > > > A couple more things; add a `JSON_OUTPUT_DYNAMIC_OBJECT` flag to output to > > DynamicObject for `json_decode()`, add a 3rd parameter for flags to > > var_export() for the same reason, a `'return_dynamic_object'` option for > > `unserialize()`, and so on. > > > > It would also be interesting to enter a user-defined class here to work as > a reversed JsonSerializable. Could be a static factory method for all I > care, and would omit the requirement of serialization libraries for > "simple" things where you still want decent object typing. Could also work > together with interfaces accepting properties, effectively making the > result of json_decode an anonymous class that still adheres to an > interface. In case of the custom 'deserialization' you can throw exceptions > if the format is not correct. In the case of an anonymous class with an > interface it could throw an exception if the structure doesn't match > (possibly controlled by flags?).
I think we want round-trips of json_decode() + json_encode() to work loss-free, without any custom classes or extra parameters. Currently, '{}' and '[]' have different results with json_decode(). assert(json_encode(json_decode('{}')) === '{}'); // Perfect round-trip. assert(json_encode(json_decode('{}', JSON_OBJECT_AS_ARRAY)) === '[]'); // Lossy round-trip. This can only work if we have a built-in data transfer object, distinct from arrays. Currently this is \stdClass, using the dynamic property mechanism. But one could easily imagine a different kind of data transfer object, which would use a different mechanism instead of dynamic properties. A native implementation of JsonSerializable does not really work here, because ->jsonSerialize() would have to return \stdClass to result in '{}'. Instead, what about something with a "->getData(): array" method, but then json_decode() would encode it as '{}'? assert(json_decode('{}') instanceof DataTransferObject); assert(json_decode('{}')->getData() === []); assert(json_encode(json_decode('{}')->getData()) === '[]'); assert(json_encode(json_decode('{}')) === '{}'); For the proposed rename: - If we can fully deprecate and replace dynamic properties long-term, I would rather keep the name \stdClass until it dies. - Instead of an alias or rename, I would rather have a new data transfer object class which would not rely on dynamic properties, but on a new mechanism. Imo, creating an alias won't actually make life easier for anyone, unless we can fully remove and replace \stdClass. It would mean that developers need to learn both names, and understand how aliases work - something you don't really need to learn otherwise. Think of the silly wtfs that can occur if people don't fully understand aliases, or are not aware that DynamicObject is just an alias, while \ReflectionClass and get_class() still return 'stdClass' as the class name. -- Andreas
  116016
September 8, 2021 20:48 andreas@dqxtech.net (Andreas Hennings)
Wow!
I notice that ArrayObject already does everything we would need for
json_encode().

assert(json_encode(new ArrayObject([5])) === '{"0":5}');

However, it does so in a very strange way, not using any of the public
methods, but also not using dynamic properties.
It seems there is a hard-coded internal implementation when calling
json_encode() or converting to array.
https://3v4l.org/rAc4K

I think we would want something more clean and transparent.






On Wed, 8 Sept 2021 at 21:58, Andreas Hennings <andreas@dqxtech.net> wrote:
> > On Wed, 8 Sept 2021 at 18:33, Lynn <kjarli@gmail.com> wrote: > > > > On Wed, Sep 8, 2021 at 5:38 PM Mike Schinkel <mike@newclarity.net> wrote: > > > > > A couple more things; add a `JSON_OUTPUT_DYNAMIC_OBJECT` flag to output to > > > DynamicObject for `json_decode()`, add a 3rd parameter for flags to > > > var_export() for the same reason, a `'return_dynamic_object'` option for > > > `unserialize()`, and so on. > > > > > > > It would also be interesting to enter a user-defined class here to work as > > a reversed JsonSerializable. Could be a static factory method for all I > > care, and would omit the requirement of serialization libraries for > > "simple" things where you still want decent object typing. Could also work > > together with interfaces accepting properties, effectively making the > > result of json_decode an anonymous class that still adheres to an > > interface. In case of the custom 'deserialization' you can throw exceptions > > if the format is not correct. In the case of an anonymous class with an > > interface it could throw an exception if the structure doesn't match > > (possibly controlled by flags?). > > I think we want round-trips of json_decode() + json_encode() to work > loss-free, without any custom classes or extra parameters. > > Currently, '{}' and '[]' have different results with json_decode(). > > assert(json_encode(json_decode('{}')) === '{}'); // Perfect round-trip. > assert(json_encode(json_decode('{}', JSON_OBJECT_AS_ARRAY)) === '[]'); > // Lossy round-trip. > > This can only work if we have a built-in data transfer object, > distinct from arrays. > Currently this is \stdClass, using the dynamic property mechanism. > > But one could easily imagine a different kind of data transfer object, > which would use a different mechanism instead of dynamic properties. > > A native implementation of JsonSerializable does not really work here, > because ->jsonSerialize() would have to return \stdClass to result in > '{}'. > Instead, what about something with a "->getData(): array" method, but > then json_decode() would encode it as '{}'? > > assert(json_decode('{}') instanceof DataTransferObject); > assert(json_decode('{}')->getData() === []); > assert(json_encode(json_decode('{}')->getData()) === '[]'); > assert(json_encode(json_decode('{}')) === '{}'); > > For the proposed rename: > - If we can fully deprecate and replace dynamic properties long-term, > I would rather keep the name \stdClass until it dies. > - Instead of an alias or rename, I would rather have a new data > transfer object class which would not rely on dynamic properties, but > on a new mechanism. > > Imo, creating an alias won't actually make life easier for anyone, > unless we can fully remove and replace \stdClass. > It would mean that developers need to learn both names, and understand > how aliases work - something you don't really need to learn otherwise. > Think of the silly wtfs that can occur if people don't fully > understand aliases, or are not aware that DynamicObject is just an > alias, while \ReflectionClass and get_class() still return 'stdClass' > as the class name. > > -- Andreas
  116017
September 8, 2021 20:59 rowan.collins@gmail.com (Rowan Tommins)
On 08/09/2021 16:37, Mike Schinkel wrote:

> All future code that needs to refer to the class name will still refer to `stdClass`, so we won't be gaining much by creating an alias.
Just to be clear, the only code that would need to change is code that dynamically gets *out* the class name, from get_class(), var_export(), reflection, and the like. Using the class name in code, like "$foo = new stdClass;" and "$foo instanceof stdClass", would carry on working just fine, whichever way we defined the alias.
> Besides, don't forget `stdClass::class` in addition to `get_class()`.
The ::class syntax is purely string replacement (apart from some rare edge cases), and works identically whether the class exists, is an alias, or doesn't exist at all.
> I assume we would also disallow dynamic properties in anonymous classes too, right? After all, they are just statically declared classes that the developer do not assign a name.
The difference I see is that stdClass/DynamicObject allows you to add or remove properties from an object *after it has been created*. I think a lot of use cases don't actually need that, and would benefit from error messages when doing so accidentally. You mentioned short-hand syntaxes like this:
> $obj = { > foo: 1, > bar: "hello", > baz: true, > };
I would love for that, or some other short-hand, to be equivalent to this: $obj = new class(foo: 1, bar: "hello", baz: true) {     public $foo;     public $bar;     public $baz;     public function __construct($foo, $bar, $baz) {        $this->foo  = $foo;        $this->bar = $bar;        $this->baz = $baz;     } } That is, an anonymous class, with exactly those three properties. If you *also* want to be able to define extra properties after it's created, you could opt into that using whatever mechanism a named class would (parent class, trait, attribute, etc; see other thread). Similarly, the objects created by json_decode or PDO_FETCH_OBJECT only need the *initial* properties to be dynamic, not to allow properties to be added later. Regards, -- Rowan Tommins [IMSoP]
  116018
September 8, 2021 21:12 andreas@dqxtech.net (Andreas Hennings)
On Wed, 8 Sept 2021 at 23:00, Rowan Tommins collins@gmail.com> wrote:
> > On 08/09/2021 16:37, Mike Schinkel wrote: > > > All future code that needs to refer to the class name will still refer to `stdClass`, so we won't be gaining much by creating an alias. > > > Just to be clear, the only code that would need to change is code that > dynamically gets *out* the class name, from get_class(), var_export(), > reflection, and the like. Using the class name in code, like "$foo = new > stdClass;" and "$foo instanceof stdClass", would carry on working just > fine, whichever way we defined the alias. > > > > Besides, don't forget `stdClass::class` in addition to `get_class()`. > > > The ::class syntax is purely string replacement (apart from some rare > edge cases), and works identically whether the class exists, is an > alias, or doesn't exist at all. > > > > I assume we would also disallow dynamic properties in anonymous classes too, right? After all, they are just statically declared classes that the developer do not assign a name. > > > The difference I see is that stdClass/DynamicObject allows you to add or > remove properties from an object *after it has been created*. I think a > lot of use cases don't actually need that, and would benefit from error > messages when doing so accidentally. > > You mentioned short-hand syntaxes like this: > > > $obj = { > > foo: 1, > > bar: "hello", > > baz: true, > > }; > > I would love for that, or some other short-hand, to be equivalent to this: > > $obj = new class(foo: 1, bar: "hello", baz: true) { > public $foo; > public $bar; > public $baz; > public function __construct($foo, $bar, $baz) { > $this->foo = $foo; > $this->bar = $bar; > $this->baz = $baz; > } > } > > That is, an anonymous class, with exactly those three properties. If you > *also* want to be able to define extra properties after it's created, > you could opt into that using whatever mechanism a named class would > (parent class, trait, attribute, etc; see other thread). > > Similarly, the objects created by json_decode or PDO_FETCH_OBJECT only > need the *initial* properties to be dynamic, not to allow properties to > be added later.
Even if no properties can be added after construction, this would still mean that the list of properties can be determined at run-time. There are two ways this can work: 1. Every new instance has its own anonymous class, even if they were created by the same statement in code. 2. Different instances created by the same statement in code have the same anonymous class, but this class supports dynamic properties. Btw now that I think of it, I have seen lots of code where objects from PDO are modified post construction, also with new properties being added.
> > > Regards, > > -- > Rowan Tommins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >
  116020
September 8, 2021 21:33 mike@newclarity.net (Mike Schinkel)
> On Sep 8, 2021, at 4:59 PM, Rowan Tommins collins@gmail.com> wrote: >> I assume we would also disallow dynamic properties in anonymous classes too, right? After all, they are just statically declared classes that the developer do not assign a name. > > The difference I see is that stdClass/DynamicObject allows you to add or remove properties from an object *after it has been created*. I think a lot of use cases don't actually need that, and would benefit from error messages when doing so accidentally.
The concern is *not* were they are not needed, but instead where they *are* needed. Such as when loading JSON from a source that does not ensure the schema is 100% stable over time. That's also why I was hoping some of those complaining about deprecating stdClass would explain explain their use-cases as they may know of use-cases we are not considering.
> You mentioned short-hand syntaxes like this: > >> $obj = { >> foo: 1, >> bar: "hello", >> baz: true, >> }; > > I would love for that, or some other short-hand, to be equivalent to this: > > $obj = new class(foo: 1, bar: "hello", baz: true) { > public $foo; > public $bar; > public $baz; > public function __construct($foo, $bar, $baz) { > $this->foo = $foo; > $this->bar = $bar; > $this->baz = $baz; > } > } > > That is, an anonymous class, with exactly those three properties. If you *also* want to be able to define extra properties after it's created, you could opt into that using whatever mechanism a named class would (parent class, trait, attribute, etc; see other thread).
Exactly! Actually the same functionality for named classes is something I have wanted since literally the very first day I starting using PHP so I wouldn't have to run userland code that loops through an array that runs for so many object instantiations. I have to believe it would be more performant if in C. (Reasons I do that? Loading from JSON, XML and/or SQL.). We've need either a magic method or ability to pass a close for name translations and short circuiting. That functionality is the primary reason I almost always use a base class; it would be nice to get rid of that requirement.
> Similarly, the objects created by json_decode or PDO_FETCH_OBJECT only need the *initial* properties to be dynamic, not to allow properties to be added later.
How do you define "initial?" -Mike
  116007
September 8, 2021 10:08 nicolas.grekas+php@gmail.com (Nicolas Grekas)
> In the thread for deprecation of dynamic properties, Rowan suggested that > we alias "stdClass" to "DynamicObject" in > https://externals.io/message/115800#115802. I wanted to split this > discussion off into a separate thread, as this can be decided > independently. > > The rationale for this is that "stdClass" is something of a misnomer: The > name makes it sound like this is a common base class, as used in a number > of other languages. However, PHP does not have a common base class. The > only way in which "stdClass" is "standard" is that it is the return type of > casting an array to (object). > > The actual role of stdClass is to serve as a container for dynamic > properties, thus the suggested DynamicObject name. > > What do people think about adding such an alias? Is this worthwhile? >
Hi Nikita, Rowan, I'm reading the discussion about the side of the alias. Can't we solve these concerns by making DynamicObject extend stdClass instead of aliasing? That wouldn't allow an stdClass object to get through the DynamicObject typehint, but that shouldn't be an issue since no such code has yet been written, isn't it? Nicolas
  116008
September 8, 2021 12:01 rowan.collins@gmail.com (Rowan Tommins)
On 08/09/2021 11:08, Nicolas Grekas wrote:
> I'm reading the discussion about the side of the alias. Can't we solve > these concerns by making DynamicObject extend stdClass instead of > aliasing? That wouldn't allow an stdClass object to get through the > DynamicObject typehint, but that shouldn't be an issue since no such > code has yet been written, isn't it?
I've been pondering inheritance options, but I don't think they really help - whichever approach we use, one of these assertions will be false: assert( new DynamicObject instanceof stdClass ); // false if stdClass is a sub-class of DynamicObject assert( new stdClass instanceof DynamicObject ); // false if DynamicObject is a sub-class of stdClass assert( get_class(new stdClass) === 'stdClass' ); // false if stdClass is an alias of DynamicObject assert( get_class(new DynamicObject) === 'DynamicObject' ); // false if DynamicObject is an alias of stdClass My guess would be that the name reported by get_class etc would mainly be used in tests and debugging, whereas instanceOf checks (and type constraints) will be used in actual "business logic" code paths. Existing code will expect "instanceOf stdClass" to work, and new code will expect "instanceOf DynamicObject" to work, so an alias (in either direction) seems the better compromise. Regards, -- Rowan Tommins [IMSoP]
  116014
September 8, 2021 17:01 kjarli@gmail.com (Lynn)
On Mon, Sep 6, 2021 at 5:28 PM Nikita Popov ppv@gmail.com> wrote:

> What do people think about adding such an alias? Is this worthwhile? >
What if `DynamicObject` becomes an interface instead of an alias? In the future `stdClass` could be deprecated and replaced by anonymous classes using the `DynamicObject` interface to prevent manual initialization. I feel like renaming (this seems to be the end-goal) will not solve some of the problems that `stdClass` brings to php. Hamza Ahmad brought the interface up in the other thread: https://externals.io/message/115800#115806 .. ```php // core php interface DynamicObject { /* ... /*} // in case of removal, legacy support could be kept as class stdClass implements DynamicObject { use DynamicObjectTrait; // perhaps also core php? } // a library wants a DynamicObject for whatever reason? final class MyCustomDataObject implements DynamicObject { public function __get(string $name) : mixed { // as a developer I now have full control over where // this data comes from and is stored internally } /* ... */ } ``` To me it feels like making this an interface gives PHP more flexibility in dealing with this in the future. One intermediate step could be to turn `stdClass` into an interface that extends `DynamicObject`. 8.2 could deprecate manual instantiation of `stdClass`, meaning that in 9.0 it could be removed as class and turned into an interface, reducing the need to update code relying on the `stdClass` types. `get_class` would still return `stdClass` in 8.x and thus not break when using a `DynamicObject`.