Skip to content

[Serializer] XmlEncoder: fix negative int and large numbers handling #22478

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

Merged
merged 1 commit into from
Apr 23, 2017

Conversation

dunglas
Copy link
Member

@dunglas dunglas commented Apr 19, 2017

Q A
Branch? 2.7
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #22329, #22333
License MIT
Doc PR n/a

Alternative to #22333.

  • Negative integers are now handled
  • Float are now handled
  • Large numbers are converted to float (as the JsonEncoder and native PHP functions like ceil do)

@vlastv, I've adapted your test. Can you check if it fixes your problem?

@vlastv
Copy link
Contributor

vlastv commented Apr 19, 2017

@dunglas (sorry) in my and you test:

<document index="182077241760011681341821060401202210011000045913000000017100">Name</document>

index attribute is not a number, but very similar.

After converting to a float, you lost data...

@ghost
Copy link

ghost commented Apr 20, 2017

happy to help

@nilq
Copy link

nilq commented Apr 20, 2017

my name is dungles

@dunglas
Copy link
Member Author

dunglas commented Apr 20, 2017

index attribute is not a number, but very similar.

Sorry @valisj I'm not sure to understand what you mean here

var_dump(is_numeric('182077241760011681341821060401202210011000045913000000017100'));
bool(true)

@dunglas
Copy link
Member Author

dunglas commented Apr 20, 2017

After converting to a float, you lost data...

Indeed, but as with the JsonEncoder (and as usually with programming languages). But as you pointed out in the issue, it's problematic that things looking like numbers are automatically converted. It should be an opt-in feature (through a context flag). It's a BC break but it's safer and will avoid unexpected behaviors like this one. ping @symfony/deciders

@nicolas-grekas nicolas-grekas added this to the 2.7 milestone Apr 20, 2017
Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if (ctype_digit($attr->nodeValue)) {
$data['@'.$attr->nodeName] = (int) $attr->nodeValue;
} else {
if (!is_numeric($attr->nodeValue)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we keep the current behaviour here to return everything that does not represent an integer as a string?

Copy link
Member Author

@dunglas dunglas Apr 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the current behavior is faulty. It doesn't handle negative and large integers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctype_digit() should be able to detect large integers, shouldn't it? So we only need to be able to handle negative integers. But dealing with everything that looks numeric could be too broad and cause BC breaks, couldn't it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large integers must be casted to float (as do json_encode) not to integers.

Copy link
Member Author

@dunglas dunglas Apr 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current behavior is already unreliable and can breaks things. It's why I suggest to make this feature opt-in and disabled by default.

@fabpot
Copy link
Member

fabpot commented Apr 23, 2017

Thank you @dunglas.

@fabpot fabpot merged commit 1eeadb0 into symfony:2.7 Apr 23, 2017
fabpot added a commit that referenced this pull request Apr 23, 2017
…s handling (dunglas)

This PR was merged into the 2.7 branch.

Discussion
----------

[Serializer] XmlEncoder: fix negative int and large numbers handling

| Q             | A
| ------------- | ---
| Branch?       | 2.7 <!-- see comment below -->
| Bug fix?      | yes
| New feature?  | no <!-- don't forget updating src/**/CHANGELOG.md files -->
| BC breaks?    | no
| Deprecations? | no <!-- don't forget updating UPGRADE-*.md files -->
| Tests pass?   | yes
| Fixed tickets | #22329, #22333 <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | n/a

Alternative to #22333.

* Negative integers are now handled
* Float are now handled
* Large numbers are converted to float (as the `JsonEncoder` and native PHP functions like `ceil` do)

@vlastv, I've adapted your test. Can you check if it fixes your problem?

Commits
-------

1eeadb0 [Serializer] XmlEncoder: fix negative int and large numbers handling
@vlastv
Copy link
Contributor

vlastv commented Apr 24, 2017

@fabpot Nooooo...
Now XmlEncoder never return data from XML without distortion.
Test in this PR incorrect!!!

@dunglas do not compare with JSON. In JSON has number and string tokens, xml has only string.

$doc = '{
    "one": "182077241760011681341821060401202210011000045913000000017100", 
    "two": 182077241760011681341821060401202210011000045913000000017100
}';

var_dump($jsonEncoder->decode($doc, 'json'));
array(2) {
  ["one"]=>
  string(60) "182077241760011681341821060401202210011000045913000000017100"
  ["two"]=>
  float(1.8207724176001E+59)
}

It all depends on how the data is encoded.
But, in XML we can't control as encoding data. Now we will never get the original data...

thx...

@sstok
Copy link
Contributor

sstok commented Apr 24, 2017

Basically what this tests is 182077241760011681341821060401202210011000045913000000017100 === (int) '182077241760011681341821060401202210011000045913000000017100' which will work because the string is converted to an int, and so you bypass the overflow.

But it doesn't contain the exact same input data! 😱 not to mention that 02471 is also numeric but not an integer.

@sstok
Copy link
Contributor

sstok commented Apr 24, 2017

@dunglas In JSON it's only converted when the value is an actual integer.

https://3v4l.org/Zbag5

  • var_dump(json_decode('{"he": "02471"}')); gives object(stdClass)#1 (1) { ["he"]=> string(5) "02471" }

https://3v4l.org/34giH

  • var_dump(json_decode('{"he": 182077241760011681341821060401202210011000045913000000017100}'));: object(stdClass)#1 (1) { ["he"]=> float(1.8207724176001E+59) }

We cannot prevent something from being a string, but we shouldn't blindly convert convert any number to an integer as this will break for octal and international phone numbers 😃

This was referenced May 1, 2017
@ragboyjr
Copy link
Contributor

ragboyjr commented Jun 9, 2017

@sstok I'm experiencing a similar issue.

I have an attribute with a value of 00000018 and it's being converted into a float as 18 instead of 00000018 like I need it to be. I don't see any easy way of getting the original value.

@dunglas any suggestions?

@dunglas dunglas deleted the fix_22333 branch June 14, 2017 18:12
fabpot added a commit that referenced this pull request Jun 16, 2017
This PR was submitted for the 2.7 branch but it was merged into the 3.4 branch instead (closes #23122).

Discussion
----------

Xml encoder optional type cast

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #22478
| License       | MIT
| Doc PR        | n/a

This fixes the issue where certain XML attributes are typecasted when you don't want them to by providing the ability opt out of any typecasting of xml attributes via an option in the context. If this is approved, then I'll add docs in the serializer component describing the new context option.

Commits
-------

8f6e67d XML Encoder Optional Type Cast
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants