Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 2.1.2 (future)

## Bugfixes
- Fixed reference numbering for `null` values: In PHP serialization, `null` values (`N;`) are counted in reference numbering, but the library was not incrementing the reference counter for them. This caused incorrect reference resolution when `null` values appeared before referenced objects.
- Fixed reference numbering for value references: In PHP, value references (`r:`) are counted towards reference numbering, while variable references (`R:`) are not. The library was treating both types the same way (not counting either), which caused reference resolution failures when multiple value references appeared before a later reference target.

# 2.1.1 (2025-03-12)

## Bugfixes
Expand Down
19 changes: 19 additions & 0 deletions PhpSerializerNET.Test/DataTypes/MultiRef.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
**/

namespace PhpSerializerNET.Test.DataTypes;

public class MultiRef {
public MultiRefObj first { get; set; }
public MultiRefObj second { get; set; }
public MultiRefObj third { get; set; }
public MultiRefObj fourth { get; set; }
public MultiRefObj fifth { get; set; }
}

public class MultiRefObj {
public string id { get; set; }
}
21 changes: 21 additions & 0 deletions PhpSerializerNET.Test/DataTypes/ReferenceWithNulls.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
**/

namespace PhpSerializerNET.Test.DataTypes;

public class ReferenceWithNulls {
public ReferenceWithNullsItem first { get; set; }
public ReferenceWithNullsItem second { get; set; }
}

public class ReferenceWithNullsItem {
public object modifiers { get; set; }
public ReferenceWithNullsInner inner { get; set; }
}

public class ReferenceWithNullsInner {
public string value { get; set; }
}
24 changes: 24 additions & 0 deletions PhpSerializerNET.Test/Deserialize/ReferenceDeserializationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,28 @@ public void ReferencingArray() {
Assert.Equal("two", value.Second.bar);
}

[Fact]
public void ReferenceWithNullValuesBefore() {
var value = PhpSerialization.Deserialize<ReferenceWithNulls>(
"""a:2:{s:5:"first";a:2:{s:9:"modifiers";N;s:5:"inner";O:5:"Inner":1:{s:5:"value";s:4:"test";}}s:6:"second";a:2:{s:9:"modifiers";N;s:5:"inner";r:4;}}"""
);

Assert.Null(value.first.modifiers);
Assert.Null(value.second.modifiers);
Assert.Equal("test", value.first.inner.value);
Assert.Equal("test", value.second.inner.value);
}

[Fact]
public void MultipleValueReferencesBeforeTarget() {
var value = PhpSerialization.Deserialize<MultiRef>(
"""a:5:{s:5:"first";O:3:"Obj":1:{s:2:"id";s:1:"A";}s:6:"second";r:2;s:5:"third";r:2;s:6:"fourth";O:3:"Obj":1:{s:2:"id";s:1:"B";}s:5:"fifth";r:6;}"""
);

Assert.Equal("A", value.first.id);
Assert.Equal("A", value.second.id);
Assert.Equal("A", value.third.id);
Assert.Equal("B", value.fourth.id);
Assert.Equal("B", value.fifth.id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void AllowsValidArray() {
if (result is List<object>) {
Assert.Equal(24, ((List<object>)result).Count);
} else {
Assert.True(false, "Expected List<object> but got " + result.GetType().Name);
Assert.Fail("Expected List<object> but got " + result.GetType().Name);
}
}
}
21 changes: 18 additions & 3 deletions PhpSerializerNET/Deserialization/PhpTokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ private int GetLength() {
private void GetToken(bool countReference) {
switch (this._input[this._position++]) {
case (byte)'r':
this.GetValueReferenceToken(countReference);
break;
case (byte)'R':
this.GetReferenceToken();
this.GetVariableReferenceToken();
break;
case (byte)'b':
this.GetBooleanToken(countReference);
Expand All @@ -56,7 +58,7 @@ private void GetToken(bool countReference) {
PhpDataType.Null,
this._position - 1,
ValueSpan.Empty,
0
countReference ? ++this._reference : 0
);
this._position++;
break;
Expand Down Expand Up @@ -114,7 +116,7 @@ private void GetIntegerToken(bool reference) {
this._position++;
}

private void GetReferenceToken() {
private void GetVariableReferenceToken() {
this._position++;
int index = this.GetNumbers().GetInt(this._input);
if (index <= 0 || index > this._reference) {
Expand All @@ -124,6 +126,19 @@ private void GetReferenceToken() {
this._position++;
}

private void GetValueReferenceToken(bool reference) {
this._position++;
int index = this.GetNumbers().GetInt(this._input);
if (index <= 0 || index > this._reference) {
throw new DeserializationException($"Invalid reference: '{index}' can not be resolved.");
}
if (reference) {
this._reference++;
}
this._tokens[this._tokenPosition++] = new PhpToken(PhpDataType.Reference, this._position, ValueSpan.Empty, index);
this._position++;
}

private void GetFloatingToken(bool reference) {
this._position++;
this._tokens[this._tokenPosition++] = new PhpToken(
Expand Down
2 changes: 1 addition & 1 deletion PhpSerializerNET/PhpSerializerNET.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<Title>PhpSerializerNET</Title>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<Version>2.1.1</Version>
<Version>2.1.2</Version>
<Authors>StringEpsilon</Authors>
<Summary>A library for working with the PHP serialization format.</Summary>
<PackageTags>php-serialization, php-serializer, php-deserializer</PackageTags>
Expand Down