Skip to content
This repository was archived by the owner on Apr 25, 2026. It is now read-only.

Commit 55baad7

Browse files
committed
Fixed deserialization of integer with plus sign.
I was improving test coverage when I found the bug.
1 parent 9d4e7dc commit 55baad7

11 files changed

Lines changed: 68 additions & 135 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 2.0.2 (Future)
2+
- Fixed a bug where integers with a plus sign where deserialized incorrectly
3+
`i:+1;` was deserialized as `49`.
4+
15
# 2.0.1 (2024-11-18)
26

37
## Bugfixes

PhpSerializerNET.Test/DataTypes/IPhpObjectStruct.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the Mozilla Public
55
**/
66

77
namespace PhpSerializerNET.Test.DataTypes {
8+
[PhpClass("MyPhpObject")]
89
public struct IPhpObjectStruct : IPhpObject {
910
private string _className;
1011
public string GetClassName() {

PhpSerializerNET.Test/Deserialize/ArrayDeserialization.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ public void ExplicitToArray() {
179179

180180
Assert.Equal(new string[] { "Hello", "World", "12345" }, result);
181181
}
182+
[Fact]
183+
public void ExplicitToObjectArray() {
184+
var result = PhpSerialization.Deserialize<object[]>("a:3:{i:0;s:5:\"Hello\";i:1;s:5:\"World\";i:2;i:12345;}");
185+
186+
Assert.Equal(new object[] { "Hello", "World", 12345 }, result);
187+
}
182188

183189
[Fact]
184190
public void ExplicitToListNonIntegerKey() {

PhpSerializerNET.Test/Deserialize/DoubleDeserialization.cs

Lines changed: 19 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -6,130 +6,38 @@ This Source Code Form is subject to the terms of the Mozilla Public
66
file, You can obtain one at http://mozilla.org/MPL/2.0/.
77
**/
88

9+
using PhpSerializerNET.Test.Deserialize.Options;
910
using Xunit;
1011

1112
namespace PhpSerializerNET.Test.Deserialize;
1213

1314
public class DoubleDeserializationTest {
1415

15-
[Fact]
16-
public void DeserializesNormalValue() {
17-
Assert.Equal(
18-
1.23456789,
19-
PhpSerialization.Deserialize<double>("d:1.23456789;")
20-
);
21-
Assert.Equal(
22-
1.23456789,
23-
PhpSerialization.Deserialize("d:1.23456789;")
24-
);
25-
}
26-
27-
[Fact]
28-
public void DeserializesOne() {
29-
Assert.Equal(
30-
(double)1,
31-
PhpSerialization.Deserialize("d:1;")
32-
);
33-
}
34-
35-
[Fact]
36-
public void DeserializesMinValue() {
37-
Assert.Equal(
38-
double.MinValue,
39-
PhpSerialization.Deserialize("d:-1.7976931348623157E+308;")
40-
);
41-
}
42-
43-
[Fact]
44-
public void DeserializesMaxValue() {
45-
Assert.Equal(
46-
double.MaxValue,
47-
PhpSerialization.Deserialize("d:1.7976931348623157E+308;")
48-
);
49-
}
50-
51-
[Fact]
52-
public void DeserializesInfinity() {
53-
Assert.Equal(
54-
double.PositiveInfinity,
55-
PhpSerialization.Deserialize("d:INF;")
56-
);
57-
}
58-
59-
[Fact]
60-
public void DeserializesNegativeInfinity() {
61-
Assert.Equal(
62-
double.NegativeInfinity,
63-
PhpSerialization.Deserialize("d:-INF;")
64-
);
65-
}
66-
67-
[Fact]
68-
public void DeserializesNotANumber() {
16+
[Theory]
17+
[InlineData("d:+1;", 1.0)]
18+
[InlineData("d:-1;", -1.0)]
19+
[InlineData("d:+1.23456789;", 1.23456789)]
20+
[InlineData("d:-1.23456789;", -1.23456789)]
21+
[InlineData("d:-1.7976931348623157E+308;", double.MinValue)]
22+
[InlineData("d:-1.7976931348623157e+308;", double.MinValue)]
23+
[InlineData("d:1.7976931348623157E+308;", double.MaxValue)]
24+
[InlineData("d:1.7976931348623157e+308;", double.MaxValue)]
25+
[InlineData("d:NAN;", double.NaN)]
26+
[InlineData("d:INF;", double.PositiveInfinity)]
27+
[InlineData("d:-INF;", double.NegativeInfinity)]
28+
public void DeserializesDoubles(string input, double expected) {
6929
Assert.Equal(
70-
double.NaN,
71-
PhpSerialization.Deserialize("d:NAN;")
30+
expected,
31+
PhpSerialization.Deserialize<double>(input)
7232
);
73-
}
74-
75-
[Fact]
76-
public void Explicit_DeserializesInfinity() {
77-
Assert.Equal(
78-
double.PositiveInfinity,
79-
PhpSerialization.Deserialize<double>("d:INF;")
80-
);
81-
}
82-
83-
[Fact]
84-
public void Explicit_DeserializesNegativeInfinity() {
85-
Assert.Equal(
86-
double.NegativeInfinity,
87-
PhpSerialization.Deserialize<double>("d:-INF;")
88-
);
89-
}
90-
91-
[Fact]
92-
public void Explicit_DeserializesNotANumber() {
93-
Assert.Equal(
94-
double.NaN,
95-
PhpSerialization.Deserialize<double>("d:NAN;")
96-
);
97-
}
98-
99-
[Fact]
100-
public void Explicit_Nullable_DeserializesInfinity() {
101-
Assert.Equal(
102-
double.PositiveInfinity,
103-
PhpSerialization.Deserialize<double?>("d:INF;")
104-
);
105-
}
106-
107-
[Fact]
108-
public void Explicit_Nullable_DeserializesNegativeInfinity() {
109-
Assert.Equal(
110-
double.NegativeInfinity,
111-
PhpSerialization.Deserialize<double?>("d:-INF;")
112-
);
113-
}
114-
115-
[Fact]
116-
public void Explicit_Nullable_DeserializesNotANumber() {
117-
Assert.Equal(
118-
double.NaN,
119-
PhpSerialization.Deserialize<double?>("d:NAN;")
120-
);
121-
}
122-
123-
[Fact]
124-
public void DeserializesToNullable() {
12533
Assert.Equal(
126-
3.1415,
127-
PhpSerialization.Deserialize<double?>("d:3.1415;")
34+
expected,
35+
PhpSerialization.Deserialize(input)
12836
);
12937
}
13038

13139
[Fact]
132-
public void DeserializeDoubleToInt() {
40+
public void ExplicetlyDeserializesToInteger() {
13341
double number = PhpSerialization.Deserialize<double>("d:10;");
13442
Assert.Equal(10, number);
13543
}

PhpSerializerNET.Test/Deserialize/IPhpObjectDeserialization.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public void DeerializesIPhpObject() { // #Issue 25
2222
[Fact]
2323
public void DeerializesPhpObjectDictionary() {
2424
var result = PhpSerialization.Deserialize<PhpObjectDictionary>("O:11:\"MyPhpObject\":1:{s:3:\"Foo\";s:0:\"\";}");
25-
Assert.Equal( // strings:
25+
Assert.Equal(
2626
"MyPhpObject",
2727
result.GetClassName()
2828
);
@@ -31,7 +31,15 @@ public void DeerializesPhpObjectDictionary() {
3131
[Fact]
3232
public void DeerializesIPhpObjectStruct() {
3333
var result = PhpSerialization.Deserialize<IPhpObjectStruct>("O:11:\"MyPhpObject\":1:{s:3:\"Foo\";s:0:\"\";}");
34-
Assert.Equal( // strings:
34+
Assert.Equal(
35+
"MyPhpObject",
36+
result.GetClassName()
37+
);
38+
}
39+
[Fact]
40+
public void DeserializesIPhpObjectStruct_Implicit() {
41+
var result = PhpSerialization.Deserialize("O:11:\"MyPhpObject\":1:{s:3:\"Foo\";s:0:\"\";}") as IPhpObject;
42+
Assert.Equal(
3543
"MyPhpObject",
3644
result.GetClassName()
3745
);

PhpSerializerNET.Test/Deserialize/IntegerDeserialization.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,12 @@ public class IntegerDeserializationTest {
1313
[Theory]
1414
[InlineData("i:0;", 0)]
1515
[InlineData("i:1;", 1)]
16+
[InlineData("i:+1;", 1)]
17+
[InlineData("i:-1;", -1)]
1618
[InlineData("i:2147483647;", int.MaxValue)]
1719
[InlineData("i:-2147483648;", int.MinValue)]
1820
public void Deserialize(string input, int expected) {
1921
Assert.Equal(expected, PhpSerialization.Deserialize<int>(input));
20-
}
21-
22-
[Theory]
23-
[InlineData("i:0;", 0)]
24-
[InlineData("i:1;", 1)]
25-
[InlineData("i:2147483647;", int.MaxValue)]
26-
[InlineData("i:-2147483648;", int.MinValue)]
27-
public void DeserializeImplicit(string input, int expected) {
2822
Assert.Equal(expected, PhpSerialization.Deserialize(input));
2923
}
3024

@@ -43,9 +37,7 @@ public void DeserializeIntToDouble() {
4337
[Fact]
4438
public void ExplictCastFormatException() {
4539
var ex = Assert.Throws<DeserializationException>(() =>
46-
PhpSerialization.Deserialize<int>(
47-
"s:3:\"1b1\";"
48-
)
40+
PhpSerialization.Deserialize<int>("s:3:\"1b1\";")
4941
);
5042
Assert.IsType<System.FormatException>(ex.InnerException);
5143
Assert.Equal("Exception encountered while trying to assign '1b1' to type Int32. See inner exception for details.", ex.Message);

PhpSerializerNET.Test/Deserialize/StringDeserialization.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ public void DeserializeEmptyStringExplicit() {
4949
EmptyStringToDefault = true
5050
})
5151
);
52+
Assert.Null(
53+
PhpSerialization.Deserialize<object>("s:0:\"\";", new PhpDeserializationOptions {
54+
EmptyStringToDefault = true
55+
})
56+
);
57+
Assert.Equal(
58+
"",
59+
PhpSerialization.Deserialize<object>("s:0:\"\";", new PhpDeserializationOptions {
60+
EmptyStringToDefault = false
61+
})
62+
);
5263
Assert.Equal(
5364
"string",
5465
PhpSerialization.Deserialize<string>("s:6:\"string\";", new PhpDeserializationOptions {

PhpSerializerNET.Test/Deserialize/Validation/TestIntegerValidation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class TestIntegerValidation {
1414
[InlineData("i:NaN;", "Unexpected token at index 2. 'N' is not a valid part of a number.")]
1515
[InlineData("i:12345b:;", "Unexpected token at index 7. 'b' is not a valid part of a number.")]
1616
[InlineData("i:12345.;", "Unexpected token at index 7. '.' is not a valid part of a number.")]
17+
[InlineData("i:12345", "Unexpected end of input. Expected ':' at index 6, but input ends at index 6")]
1718
public void ThrowsOnMalformedInteger(string input, string exceptionMessage) {
1819
var exception = Assert.Throws<DeserializationException>(() => {
1920
PhpSerialization.Deserialize(input);

PhpSerializerNET/Deserialization/PhpDeserializer.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ internal object Deserialize(Type targetType) {
3333
return this.DeserializeToken(targetType);
3434
}
3535

36-
internal T Deserialize<T>() {
37-
return (T)this.Deserialize(typeof(T));
38-
}
39-
4036
private object DeserializeToken() {
4137
var token = this._tokens[this._currentToken];
4238
this._currentToken++;
@@ -209,7 +205,7 @@ int tokenPosition
209205
}
210206

211207
if (targetType == typeof(bool)) {
212-
if (this._options.NumberStringToBool && value is "0" or "1") {
208+
if (this._options.NumberStringToBool && (value == "0" || value == "1")) {
213209
return value == "1";
214210
}
215211
}

PhpSerializerNET/Deserialization/PhpTokenValidator.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ internal PhpTokenValidator(in ReadOnlySpan<byte> input) {
2525
}
2626

2727
internal void GetToken() {
28-
switch ( this._input[this._position++]) {
28+
switch (this._input[this._position++]) {
2929
case (byte)'b':
3030
this.GetCharacter(':');
3131
this.GetBoolean();
@@ -102,11 +102,11 @@ private void GetFloat() {
102102
),
103103
};
104104
}
105-
if (i == this._position) {
106-
throw new DeserializationException(
107-
$"Unexpected token at index {i}: Expected floating point number, but found ';' instead."
108-
);
109-
}
105+
if (i == this._position) {
106+
throw new DeserializationException(
107+
$"Unexpected token at index {i}: Expected floating point number, but found ';' instead."
108+
);
109+
}
110110
this._position = i;
111111

112112
// Edgecase: input ends here without a delimeter following. Normal handling would give a misleading exception:

0 commit comments

Comments
 (0)