Skip to content

Nullable group isn't reflected in generated types. #586

@Knappster

Description

@Knappster

Bug Report

Q A
Version 4.2.0

Summary

A group with minOccurs="0" isn't marking types as nullable.

Current behaviour

Thanks for your help on my previous issue #585, however I don't think they are related.

When parsing the scpSimpleQuery response an attempt to set StoreCardResult::$securityDetails to null is made, however the property isn't marked as nullable.

Type class:

<?php

namespace App\SoapClient\Capita\Types;

class StoreCardResult
{
    /**
     * @var \App\SoapClient\Capita\Types\Status
     */
    private \App\SoapClient\Capita\Types\Status $status;

    /**
     * @var null | \App\SoapClient\Capita\Types\StoredCardDetails
     */
    private ?\App\SoapClient\Capita\Types\StoredCardDetails $storedCardDetails = null;

    /**
     * @var null | \App\SoapClient\Capita\Types\ErrorDetails
     */
    private ?\App\SoapClient\Capita\Types\ErrorDetails $errorDetails = null;

    /**
     * @var null | string
     */
    private ?string $isoCountryCode = null;

    /**
     * @var \App\SoapClient\Capita\Types\SecurityDetails
     */
    private \App\SoapClient\Capita\Types\SecurityDetails $securityDetails;

...

WSDL snippets:

<xs:complexType name="storeCardResult">
    <xs:sequence>
        <xs:element name="status" type="status" />
        <xs:choice minOccurs="0">
            <xs:element name="storedCardDetails" type="storedCardDetails" />
            <xs:element name="errorDetails" type="errorDetails" />
        </xs:choice>
        <xs:element minOccurs="0" name="isoCountryCode" type="isoCountryCode" />
        <xs:group minOccurs="0" ref="securityDetailsGroup" />
    </xs:sequence>
</xs:complexType>

...

<xs:group name="securityDetailsGroup">
    <xs:sequence>
        <xs:element name="securityDetails" type="securityDetails" />
    </xs:sequence>
</xs:group>

Raw response body:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header />
    <SOAP-ENV:Body>
        <scpSimpleQueryResponse xmlns="http://www.capita-software-services.com/scp/simple">
            <requestId xmlns="http://www.capita-software-services.com/scp/base">68bae67885fb9</requestId>
            <scpReference xmlns="http://www.capita-software-services.com/scp/base">
                m812ovyws7o64jvlza5gznygbp8n7kv</scpReference>
            <transactionState xmlns="http://www.capita-software-services.com/scp/base">COMPLETE</transactionState>
            <storeCardResult xmlns="http://www.capita-software-services.com/scp/base">
                <status>NOT_ATTEMPTED</status>
            </storeCardResult>
            <paymentResult>
                <status xmlns="http://www.capita-software-services.com/scp/base">SUCCESS</status>
                <paymentDetails>
                    <paymentHeader xmlns="http://www.capita-software-services.com/scp/base">
                        <transactionDate>2025-09-05T13:33:44Z</transactionDate>
                        <machineCode>00248</machineCode>
                        <uniqueTranId>LWPRKTCFT7F6</uniqueTranId>
                        <paymentMethod>
                            <type>CARD</type>
                        </paymentMethod>
                    </paymentHeader>
                    <authDetails xmlns="http://www.capita-software-services.com/scp/base">
                        <authCode>110185</authCode>
                        <amountInMinorUnits>2000</amountInMinorUnits>
                        <maskedCardNumber>446203******0000</maskedCardNumber>
                        <cardDescription>VISA</cardDescription>
                        <cardType>DEBIT</cardType>
                        <merchantNumber>12345678</merchantNumber>
                        <expiryDate>1225</expiryDate>
                        <continuousAuditNumber>858</continuousAuditNumber>
                    </authDetails>
                    <saleSummary>
                        <items>
                            <itemSummary>
                                <lineId xmlns="http://www.capita-software-services.com/scp/base">1</lineId>
                                <continuousAuditNumber
                                    xmlns="http://www.capita-software-services.com/scp/base">858</continuousAuditNumber>
                            </itemSummary>
                        </items>
                    </saleSummary>
                </paymentDetails>
                <securityDetails xmlns="http://www.capita-software-services.com/scp/base">
                    <threeDSecure>
                        <authenticationStatus>AUTHENTICATION_SUCCESSFUL</authenticationStatus>
                        <authenticationIndicator>Y</authenticationIndicator>
                        <enrolementStatus>ENROLLED</enrolementStatus>
                        <enrolementIndicator>Y</enrolementIndicator>
                        <checkedAt>2025-09-05T13:33:43Z</checkedAt>
                        <scheme>VISA</scheme>
                        <eci>05</eci>
                        <xid>7925584b-e6f4-4dde-8790-b0ca5c405f12</xid>
                        <threeDSVersion>2.2.0</threeDSVersion>
                        <acsTransactionId>649f09d0-0c40-4eb5-8937-f112581c8329</acsTransactionId>
                        <dsTransactionId>cfa25789-4808-4810-b5a1-2bea4b8bee8a</dsTransactionId>
                        <countryCode>826</countryCode>
                    </threeDSecure>
                    <resultAVVCVV>111100</resultAVVCVV>
                </securityDetails>
            </paymentResult>
        </scpSimpleQueryResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Stack trace:

[previous exception] [object] (TypeError(code: 0): Cannot assign null to property App\\SoapClient\\Capita\\Types\\StoreCardResult::$securityDetails of type App\\SoapClient\\Capita\\Types\\SecurityDetails at /var/www/api/vendor/veewee/reflecta/src/Reflect/property_set.php:44)
[stacktrace]
#0 /var/www/api/vendor/veewee/reflecta/src/Reflect/property_set.php(44): ReflectionProperty->setValue(Object(App\\SoapClient\\Capita\\Types\\StoreCardResult), NULL)
#1 /var/www/api/vendor/veewee/reflecta/src/Reflect/Type/ReflectedProperty.php(127): {closure:VeeWee\\Reflecta\\Reflect\\property_set():44}(Object(ReflectionProperty))
#2 /var/www/api/vendor/veewee/reflecta/src/Reflect/property_set.php(43): VeeWee\\Reflecta\\Reflect\\Type\\ReflectedProperty->apply(Object(Closure))
#3 /var/www/api/vendor/veewee/reflecta/src/Reflect/properties_set.php(43): VeeWee\\Reflecta\\Reflect\\property_set(Object(App\\SoapClient\\Capita\\Types\\StoreCardResult), 'securityDetails', NULL)
#4 /var/www/api/vendor/azjezz/psl/src/Psl/Iter/reduce_with_keys.php(31): {closure:VeeWee\\Reflecta\\Reflect\\properties_set():43}(Object(App\\SoapClient\\Capita\\Types\\StoreCardResult), 'securityDetails', NULL)
#5 /var/www/api/vendor/veewee/reflecta/src/Reflect/properties_set.php(37): Psl\\Iter\\reduce_with_keys(Array, Object(Closure), Object(App\\SoapClient\\Capita\\Types\\StoreCardResult))
#6 /var/www/api/vendor/veewee/reflecta/src/Lens/properties.php(35): VeeWee\\Reflecta\\Reflect\\properties_set(Object(App\\SoapClient\\Capita\\Types\\StoreCardResult), Array, NULL)
#7 /var/www/api/vendor/veewee/reflecta/src/Lens/Lens.php(99): {closure:VeeWee\\Reflecta\\Lens\\properties():35}(Object(App\\SoapClient\\Capita\\Types\\StoreCardResult), Array)
#8 /var/www/api/vendor/veewee/reflecta/src/Iso/object_data.php(35): VeeWee\\Reflecta\\Lens\\Lens->set(Object(App\\SoapClient\\Capita\\Types\\StoreCardResult), Array)
#9 /var/www/api/vendor/veewee/reflecta/src/Iso/Iso.php(82): {closure:VeeWee\\Reflecta\\Iso\\object_data():35}(Array)
#10 /var/www/api/vendor/php-soap/encoding/src/Encoder/ObjectEncoder.php(131): VeeWee\\Reflecta\\Iso\\Iso->from(Array)
#11 /var/www/api/vendor/php-soap/encoding/src/Encoder/ObjectEncoder.php(61): Soap\\Encoding\\Encoder\\ObjectEncoder->from(Object(Soap\\Encoding\\Encoder\\Context), Object(Soap\\Encoding\\Encoder\\ObjectAccess), Object(Soap\\Encoding\\Xml\\Node\\Element))
#12 /var/www/api/vendor/veewee/reflecta/src/Iso/Iso.php(82): Soap\\Encoding\\Encoder\\ObjectEncoder->{closure:Soap\\Encoding\\Encoder\\ObjectEncoder::iso():60}(Object(Soap\\Encoding\\Xml\\Node\\Element))
#13 /var/www/api/vendor/php-soap/encoding/src/Encoder/OptionalElementEncoder.php(76): VeeWee\\Reflecta\\Iso\\Iso->from(Object(Soap\\Encoding\\Xml\\Node\\Element))
#14 /var/www/api/vendor/veewee/reflecta/src/Iso/Iso.php(82): Soap\\Encoding\\Encoder\\OptionalElementEncoder::{closure:Soap\\Encoding\\Encoder\\OptionalElementEncoder::iso():55}(Object(Soap\\Encoding\\Xml\\Node\\Element))
#15 /var/www/api/vendor/php-soap/encoding/src/Encoder/ErrorHandlingEncoder.php(60): VeeWee\\Reflecta\\Iso\\Iso->from(Object(Soap\\Encoding\\Xml\\Node\\Element))
#16 /var/www/api/vendor/veewee/reflecta/src/Iso/Iso.php(82): Soap\\Encoding\\Encoder\\ErrorHandlingEncoder::{closure:Soap\\Encoding\\Encoder\\ErrorHandlingEncoder::iso():58}(Object(Soap\\Encoding\\Xml\\Node\\Element))
#17 /var/www/api/vendor/php-soap/encoding/src/Encoder/ObjectEncoder.php(149): VeeWee\\Reflecta\\Iso\\Iso->from(Object(Soap\\Encoding\\Xml\\Node\\Element))
#18 /var/www/api/vendor/azjezz/psl/src/Psl/Dict/map_with_key.php(35): Soap\\Encoding\\Encoder\\ObjectEncoder::{closure:Soap\\Encoding\\Encoder\\ObjectEncoder::from():134}('storeCardResult', Object(Soap\\Engine\\Metadata\\Model\\Property))
#19 /var/www/api/vendor/php-soap/encoding/src/Encoder/ObjectEncoder.php(132): Psl\\Dict\\map_with_key(Array, Object(Closure))
#20 /var/www/api/vendor/php-soap/encoding/src/Encoder/ObjectEncoder.php(61): Soap\\Encoding\\Encoder\\ObjectEncoder->from(Object(Soap\\Encoding\\Encoder\\Context), Object(Soap\\Encoding\\Encoder\\ObjectAccess), Object(Soap\\Encoding\\Xml\\Node\\Element))
#21 /var/www/api/vendor/veewee/reflecta/src/Iso/Iso.php(82): Soap\\Encoding\\Encoder\\ObjectEncoder->{closure:Soap\\Encoding\\Encoder\\ObjectEncoder::iso():60}(Object(Soap\\Encoding\\Xml\\Node\\Element))
#22 /var/www/api/vendor/php-soap/encoding/src/Encoder/ErrorHandlingEncoder.php(60): VeeWee\\Reflecta\\Iso\\Iso->from(Object(Soap\\Encoding\\Xml\\Node\\Element))
#23 /var/www/api/vendor/veewee/reflecta/src/Iso/Iso.php(82): Soap\\Encoding\\Encoder\\ErrorHandlingEncoder::{closure:Soap\\Encoding\\Encoder\\ErrorHandlingEncoder::iso():58}(Object(Soap\\Encoding\\Xml\\Node\\Element))
#24 /var/www/api/vendor/azjezz/psl/src/Psl/Vec/map.php(40): VeeWee\\Reflecta\\Iso\\Iso->from(Object(Soap\\Encoding\\Xml\\Node\\Element))
#25 [internal function]: {closure:Psl\\Vec\\map():40}(Object(Soap\\Encoding\\Xml\\Node\\Element))
#26 /var/www/api/vendor/azjezz/psl/src/Psl/Vec/map.php(36): array_map(Object(Closure), Array)
#27 /var/www/api/vendor/php-soap/encoding/src/Encoder/Method/ResponseEncoder.php(101): Psl\\Vec\\map(Array, Object(Closure))
#28 /var/www/api/vendor/php-soap/encoding/src/Encoder/Method/ResponseEncoder.php(43): Soap\\Encoding\\Encoder\\Method\\ResponseEncoder->decode(Object(Soap\\Encoding\\Encoder\\Method\\MethodContext), '<SOAP-ENV:Envel...')
#29 /var/www/api/vendor/veewee/reflecta/src/Iso/Iso.php(82): Soap\\Encoding\\Encoder\\Method\\ResponseEncoder->{closure:Soap\\Encoding\\Encoder\\Method\\ResponseEncoder::iso():43}('<SOAP-ENV:Envel...')
#30 /var/www/api/vendor/php-soap/encoding/src/Decoder.php(34): VeeWee\\Reflecta\\Iso\\Iso->from('<SOAP-ENV:Envel...')
#31 /var/www/api/vendor/php-soap/encoding/src/Driver.php(62): Soap\\Encoding\\Decoder->decode('scpSimpleQuery', Object(Soap\\Engine\\HttpBinding\\SoapResponse))
#32 /var/www/api/vendor/php-soap/engine/src/SimpleEngine.php(27): Soap\\Encoding\\Driver->decode('scpSimpleQuery', Object(Soap\\Engine\\HttpBinding\\SoapResponse))
#33 /var/www/api/vendor/php-soap/engine/src/LazyEngine.php(28): Soap\\Engine\\SimpleEngine->request('scpSimpleQuery', Array)
#34 /var/www/api/vendor/phpro/soap-client/src/Phpro/SoapClient/Caller/EngineCaller.php(27): Soap\\Engine\\LazyEngine->request('scpSimpleQuery', Array)
#35 /var/www/api/vendor/phpro/soap-client/src/Phpro/SoapClient/Caller/EventDispatchingCaller.php(30): Phpro\\SoapClient\\Caller\\EngineCaller->__invoke('scpSimpleQuery', Object(App\\SoapClient\\Capita\\Types\\ScpSimpleQueryRequest))
#36 /var/www/api/app/SoapClient/Capita/CapitaClient.php(45): Phpro\\SoapClient\\Caller\\EventDispatchingCaller->__invoke('scpSimpleQuery', Object(App\\SoapClient\\Capita\\Types\\ScpSimpleQueryRequest))

If I'm wrong in my assumptions please let me know. Still learning :P

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions