-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow stdClass objects in payload #54
Comments
IMHO we need to allow \stdClass objects in payload and accept that our messages are not immutable. |
How will you check all properties are also valid, |
I would add an instanceof \stdClass check here: https://github.com/prooph/common/blob/master/src/Messaging/MessageDataAssertion.php#L73 |
Ok, what happens if I do
How would |
and
so how to you come to this?
|
@prolic non php system sending a message, for example JS client sends a json body and you turn this into a command payload |
Can be bypassed very simple:
|
I wouldn't allow |
@prolic yeah of course, but my example only illustrates the problem. In a nested structure it becomes hard to do this type conversation manually and it is impossible to do this a generic way because you simply don't have the information if the property of the nested object is an array or object when it is empty |
Is this just because of json encoding btw? Are there any real world examples on edge-cases that need it? |
About nesting:
What should the big difference be? Empty array or empty object, I don't care. A getter method on the message object should handle that difference, and decide what do to. I cannot imagine a payload where empty array or empty object make a semantic difference. |
Same thoughts here, which is why I asked for edge cases |
@Ocramius yes, it is because of json encoding. And I run into the issue several times. consider this: echo json_encode(['config' => ['optionalOptions' => []]]); var config = JSON.parse(phpResponse);
config.optionalOptions.enableStrictJsonMode = true; //error wrong type, optionalOptions is an array |
@prolic yeah, as long as your message is only handled by php but a message can be handled by any system, you don't know it so you should ensure that your json encoded message contains correct types. |
Hmm, there are various solutions here:
|
Hard to decide for me, but in general I have a strong feeling against this proposal.
seem like a better solution to me. |
Another one: documenting a limitation. This last one might be a hard one to bite, but it might also be a good temporary solution, waiting until some better approaches emerge. After all, this thread was started only 40 mins ago. |
@Ocramius I thought about the same: We should add something like that to the documentation: "You should design your message payload/API in a way that empty objects are not possible" |
anyway, I'll play with that idea: having own ArrayObject-alike structure that implements JsonSerializable |
Testet with Zend\Json:
So maybe we just switch to this instead of php's native functions and we are done. |
@prolic the problem is with encoding PHP array to JSON (same problem with Zend\Json): $obj = ['foo' => []];
json_encode($obj); // '{"foo":[]}'
$obj = ['foo' => new \stdClass];
json_encode($obj); // '{"foo":{}}'
$clientObj = json_decode('{"foo":{}}', true);
json_encode($clientObj); // '{"foo":[]}' <-- client gets different structure back: schema is invalid
$clientObj = json_decode('{"foo":{}}');
json_encode($clientObj); // '{"foo":{}}' <-- client gets same structure back: everything ok
$clientObj = json_decode('{"foo":{}}');
$proophCommand = Command::withPayload($clientObj); //Not allowed because $clientObj contains \stdClass object Note: the last example does not throw an exception in the first place (except you ensure it in your message implementation) but when you do: |
If we have the role that payload has to be array or scalar, this implies
|
we have the rule that everything insight the payload must be an array or scalar: https://github.com/prooph/common/blob/master/src/Messaging/MessageDataAssertion.php#L82 |
PHP array can have a numeric index => JSON array This means, that our definition of an array is not precise enough because we (json_encode) cannot distinguish between these two if PHP array is empty The solution would be to use PHP \stdClass when it is a (JSON) object and PHP array when it is a (JSON) array, but \stdClass sucks |
Ok but we can add a test if array is assoc or not and use this info before
|
Yes, that is indeed the reason for going with an alternate data-structure. @codeliner is the write to a hashmap the only scenario where this fails? |
@codeliner can you write a test case that fails now and we can try
|
About stdclass and immutability, i am just writing fast here without
|
@prolic only deep clone would work with recursive clone. There is also https://github.com/myclabs/DeepCopy by @mnapoli to handle circular references. @Ocramius yeah, only problem is that an empty assoc should result in a JSON object and an empty array should result in a JSON array: |
I turned back to my problem space with the feedback collected here and thought about the problem again. I agree now that an empty object in a data structure is an edge case and can be avoided easily. A nested object without any properties can be replaced by null to express the same state: It is simply too much work required to improve the situation. We should add a note or warning in the docs and link to this issue. I keep it open until docs are updated, but I don't think that we need to investigate further work. It is not worth the effort. |
Glad that the simplest workaround was picked for now: huge rabbit-hole otherwise :-) |
Still add tests for array's with circular references? |
@bweston92 yeah, will do |
Strongly not agree: null is null, empty object is empty object. Its logically two different things and idea just always use null looks incorrect for me =/ |
@tensor146 That's true, but we decided to not introduce more than needed complexity. We have this mismatch in PHP when working with arrays and json_encode and while there are solutions available no of them is perfect. You can read the details in the history of the issue. ArrayObject is not an option because it is passed by reference (unlike a real array) so we would lose the simple semi-immutable structure of our message objects. Immutability (or semi-immutability) is more important for us than null vs. empty object. |
Arrays are copied instead of passed by reference. If you ensure that the array only contains arrays and scalar types and you pass such an array to an object that only allows read access you easily get an immutable object (like our message classes)
so far so good, BUT:
This means, that I cannot have a message object that is both: immutable and 100% correctly json encodable/decodable at the same time except I use some kind of deep copy mechanism when passing data into the message and out. I can image the latter influences performance.
What can we do to improve the situation?
The text was updated successfully, but these errors were encountered: