Skip to content

Commit

Permalink
With nullable typed readonly properties (from PHP 8.1+) we face situ…
Browse files Browse the repository at this point in the history
…ations 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.
  • Loading branch information
mathielen committed Jan 16, 2025
1 parent eb7adca commit 6b8b705
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/CXml/Model/ItemDetail.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected function __construct(
private readonly MoneyWrapper $unitPrice,
#[Serializer\SerializedName('PriceBasisQuantity')]
#[Serializer\XmlElement(cdata: false)]
private readonly ?PriceBasisQuantity $priceBasisQuantity,
private readonly ?PriceBasisQuantity $priceBasisQuantity = null,
) {
}

Expand Down
2 changes: 1 addition & 1 deletion src/CXml/Model/Message/PunchOutOrderMessageHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class PunchOutOrderMessageHeader
final public const OPERATION_INSPECT = 'inspect';

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

#[Serializer\SerializedName('ShipTo')]
private ?ShipTo $shipTo = null;
Expand Down
2 changes: 1 addition & 1 deletion src/CXml/Model/MultilanguageString.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MultilanguageString
{
public function __construct(
#[Serializer\XmlValue(cdata: false)]
private readonly ?string $value,
private readonly ?string $value = null,
#[Serializer\XmlAttribute]
private readonly ?string $type = null,
#[Serializer\XmlAttribute(namespace: 'http://www.w3.org/XML/1998/namespace')]
Expand Down
18 changes: 8 additions & 10 deletions src/CXml/Model/Request/OrderRequestHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,18 @@ class OrderRequestHeader
#[Serializer\Type('array<CXml\Model\BusinessPartner>')]
private array $businessPartners;

#[Serializer\XmlElement]
#[Serializer\SerializedName('ShipTo')]
private ?ShipTo $shipTo = null; /* cant be 'readonly' bc must be initialized with null -> jms deserialization */

protected function __construct(
#[Serializer\XmlAttribute]
#[Serializer\SerializedName('orderID')]
private readonly string $orderId,
#[Serializer\XmlAttribute]
#[Serializer\SerializedName('orderDate')]
private readonly DateTimeInterface $orderDate,
#[Serializer\XmlElement]
#[Serializer\SerializedName('ShipTo')]
private readonly ?ShipTo $shipTo,
?ShipTo $shipTo, /* cant be 'readonly' bc must be initialized with null -> jms deserialization */
#[Serializer\XmlElement]
#[Serializer\SerializedName('BillTo')]
private readonly BillTo $billTo,
Expand All @@ -67,15 +69,11 @@ protected function __construct(
#[Serializer\XmlList(entry: 'Contact', inline: true)]
private ?array $contacts = null,
) {
if (null === $contacts) {
return;
}
$this->shipTo = $shipTo;

if ([] === $contacts) {
return;
if (null !== $contacts) {
Assertion::allIsInstanceOf($contacts, Contact::class);
}

Assertion::allIsInstanceOf($contacts, Contact::class);
}

public static function create(
Expand Down
50 changes: 50 additions & 0 deletions tests/CXmlTest/Model/SerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -410,4 +410,54 @@ public function testDeserializeOneRowXml(): void

$this->assertXmlStringEqualsXmlString($xml, $resultingXml);
}

public function testDeserializeNullProperty(): void
{
$xml =
'<?xml version="1.0" encoding="UTF-8"?>
<cXML payloadID="payload-id" timestamp="2000-01-01T00:00:00+00:00">
<Header>
<From>
<Credential domain="AribaNetworkUserId">
<Identity>[email protected]</Identity>
</Credential>
</From>
<To>
<Credential domain="DUNS">
<Identity>012345678</Identity>
</Credential>
</To>
<Sender>
<Credential domain="AribaNetworkUserId">
<Identity>[email protected]</Identity>
<SharedSecret>abracadabra</SharedSecret>
</Credential>
<UserAgent>Network Hub 1.1</UserAgent>
</Sender>
</Header>
<Request>
<OrderRequest>
<OrderRequestHeader orderDate="2000-01-01" orderID="order-id" type="new">
<Total>
<Money currency="EUR">0.00</Money>
</Total>
<BillTo>
<Address>
<Name xml:lang="en">name</Name>
</Address>
</BillTo>
</OrderRequestHeader>
</OrderRequest>
</Request>
</cXML>';

$cxml = Serializer::create()->deserialize($xml);

/** @var OrderRequest $orderRequest */
$orderRequest = $cxml->getRequest()->getPayload();

// Error: Typed property CXml\Model\Request\OrderRequestHeader::$shipTo must not be accessed before initialization
$shipTo = $orderRequest->getOrderRequestHeader()->getShipTo();
$this->assertNull($shipTo);
}
}

0 comments on commit 6b8b705

Please sign in to comment.