Skip to content

Commit 6b8b705

Browse files
committed
With nullable typed readonly properties (from PHP 8.1+) we face situations where properties are not set after deserialization (Typed property...must not be accessed before initialization). This subscriber helps to handle these cases by adding properties to the payload so that they end up being null.
1 parent eb7adca commit 6b8b705

File tree

5 files changed

+61
-13
lines changed

5 files changed

+61
-13
lines changed

src/CXml/Model/ItemDetail.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ protected function __construct(
4848
private readonly MoneyWrapper $unitPrice,
4949
#[Serializer\SerializedName('PriceBasisQuantity')]
5050
#[Serializer\XmlElement(cdata: false)]
51-
private readonly ?PriceBasisQuantity $priceBasisQuantity,
51+
private readonly ?PriceBasisQuantity $priceBasisQuantity = null,
5252
) {
5353
}
5454

src/CXml/Model/Message/PunchOutOrderMessageHeader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class PunchOutOrderMessageHeader
2323
final public const OPERATION_INSPECT = 'inspect';
2424

2525
#[Serializer\XmlAttribute]
26-
private readonly ?string $operationAllowed;
26+
private ?string $operationAllowed = null; /* cant be 'readonly' bc must be initialized with null -> jms deserialization */
2727

2828
#[Serializer\SerializedName('ShipTo')]
2929
private ?ShipTo $shipTo = null;

src/CXml/Model/MultilanguageString.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class MultilanguageString
1010
{
1111
public function __construct(
1212
#[Serializer\XmlValue(cdata: false)]
13-
private readonly ?string $value,
13+
private readonly ?string $value = null,
1414
#[Serializer\XmlAttribute]
1515
private readonly ?string $type = null,
1616
#[Serializer\XmlAttribute(namespace: 'http://www.w3.org/XML/1998/namespace')]

src/CXml/Model/Request/OrderRequestHeader.php

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,18 @@ class OrderRequestHeader
4343
#[Serializer\Type('array<CXml\Model\BusinessPartner>')]
4444
private array $businessPartners;
4545

46+
#[Serializer\XmlElement]
47+
#[Serializer\SerializedName('ShipTo')]
48+
private ?ShipTo $shipTo = null; /* cant be 'readonly' bc must be initialized with null -> jms deserialization */
49+
4650
protected function __construct(
4751
#[Serializer\XmlAttribute]
4852
#[Serializer\SerializedName('orderID')]
4953
private readonly string $orderId,
5054
#[Serializer\XmlAttribute]
5155
#[Serializer\SerializedName('orderDate')]
5256
private readonly DateTimeInterface $orderDate,
53-
#[Serializer\XmlElement]
54-
#[Serializer\SerializedName('ShipTo')]
55-
private readonly ?ShipTo $shipTo,
57+
?ShipTo $shipTo, /* cant be 'readonly' bc must be initialized with null -> jms deserialization */
5658
#[Serializer\XmlElement]
5759
#[Serializer\SerializedName('BillTo')]
5860
private readonly BillTo $billTo,
@@ -67,15 +69,11 @@ protected function __construct(
6769
#[Serializer\XmlList(entry: 'Contact', inline: true)]
6870
private ?array $contacts = null,
6971
) {
70-
if (null === $contacts) {
71-
return;
72-
}
72+
$this->shipTo = $shipTo;
7373

74-
if ([] === $contacts) {
75-
return;
74+
if (null !== $contacts) {
75+
Assertion::allIsInstanceOf($contacts, Contact::class);
7676
}
77-
78-
Assertion::allIsInstanceOf($contacts, Contact::class);
7977
}
8078

8179
public static function create(

tests/CXmlTest/Model/SerializerTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,4 +410,54 @@ public function testDeserializeOneRowXml(): void
410410

411411
$this->assertXmlStringEqualsXmlString($xml, $resultingXml);
412412
}
413+
414+
public function testDeserializeNullProperty(): void
415+
{
416+
$xml =
417+
'<?xml version="1.0" encoding="UTF-8"?>
418+
<cXML payloadID="payload-id" timestamp="2000-01-01T00:00:00+00:00">
419+
<Header>
420+
<From>
421+
<Credential domain="AribaNetworkUserId">
422+
<Identity>admin@acme.com</Identity>
423+
</Credential>
424+
</From>
425+
<To>
426+
<Credential domain="DUNS">
427+
<Identity>012345678</Identity>
428+
</Credential>
429+
</To>
430+
<Sender>
431+
<Credential domain="AribaNetworkUserId">
432+
<Identity>sysadmin@buyer.com</Identity>
433+
<SharedSecret>abracadabra</SharedSecret>
434+
</Credential>
435+
<UserAgent>Network Hub 1.1</UserAgent>
436+
</Sender>
437+
</Header>
438+
<Request>
439+
<OrderRequest>
440+
<OrderRequestHeader orderDate="2000-01-01" orderID="order-id" type="new">
441+
<Total>
442+
<Money currency="EUR">0.00</Money>
443+
</Total>
444+
<BillTo>
445+
<Address>
446+
<Name xml:lang="en">name</Name>
447+
</Address>
448+
</BillTo>
449+
</OrderRequestHeader>
450+
</OrderRequest>
451+
</Request>
452+
</cXML>';
453+
454+
$cxml = Serializer::create()->deserialize($xml);
455+
456+
/** @var OrderRequest $orderRequest */
457+
$orderRequest = $cxml->getRequest()->getPayload();
458+
459+
// Error: Typed property CXml\Model\Request\OrderRequestHeader::$shipTo must not be accessed before initialization
460+
$shipTo = $orderRequest->getOrderRequestHeader()->getShipTo();
461+
$this->assertNull($shipTo);
462+
}
413463
}

0 commit comments

Comments
 (0)