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
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
Am 06.09.2021 um 18:08 schrieb Benjamin Morel benjamin.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
 
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
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
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 invar_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
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
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
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]
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
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
- 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]
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:
- 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. - 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.
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
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:
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]
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.
A couple more things; add a
JSON_OUTPUT_DYNAMIC_OBJECTflag to output to
DynamicObject forjson_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?).
A couple more things; add a
JSON_OUTPUT_DYNAMIC_OBJECTflag to output to
DynamicObject forjson_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
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.
A couple more things; add a
JSON_OUTPUT_DYNAMIC_OBJECTflag to output to
DynamicObject forjson_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
thenjson_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 andget_class()still return 'stdClass'
as the class name.-- Andreas
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::classin addition toget_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]
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, fromget_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::classin addition toget_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:
- Every new instance has its own anonymous class, even if they were 
created by the same statement in code. - 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]--
To unsubscribe, visit: https://www.php.net/unsub.php
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
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
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]
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 
.
// 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.