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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions