Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicitly specifying a type #534

Closed
LazyTechwork opened this issue Jul 29, 2024 · 10 comments
Closed

Explicitly specifying a type #534

LazyTechwork opened this issue Jul 29, 2024 · 10 comments

Comments

@LazyTechwork
Copy link

Q A
Version 4.0.0

Support Question

Hello! My WSDL provider asked me to explicitly specify the type of request parameter.

Example request (here value has xsi:type attribute with xsd:string value):

<SetPersonExtraFieldValue
	xmlns="http://parsec.ru/Parsec3IntergationService">
	<personEditSessionID>b2aea303-f736-4a29-b835-670c2384551f</personEditSessionID>
	<templateID>4d7371ef-e856-4371-88da0365277c2032</templateID>
	<value xsi:type="xsd:string">valid value</value>
</SetPersonExtraFieldValue>

I tried this:

$this->client->setPersonExtraFieldValue(new SetPersonExtraFieldValue(
    personEditSessionID: $editingSessionID,
    templateID: $extraField->getTEMPLATE_ID(),
    value: new SoapVar($extraField->getVALUE(), XSD_STRING, 'string', Xmlns::xsd()->value(), 'VALUE')
));

But I am getting this exception: Failed encoding type LazyTechwork\\Parsec\\Types\\SetPersonExtraFieldValue as {http://parsec.ru/Parsec3IntergationService:setpersonextrafieldvalue}. Failed at path \"SetPersonExtraFieldValue.value\".

If I simply pass a string value as a parameter, I see this in system: System.Xml.XmlNode[]

@LazyTechwork
Copy link
Author

References to #369

@veewee
Copy link
Contributor

veewee commented Jul 30, 2024

Can you provide the WSDL for this service?

@LazyTechwork
Copy link
Author

It's not public, but I can provide necessary chunks of WSDL.

<wsdl:definitions
	xmlns:s="http://www.w3.org/2001/XMLSchema"
	xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
	xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
	xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
	xmlns:tns="http://parsec.ru/Parsec3IntergationService"
	xmlns:s1="http://microsoft.com/wsdl/types/"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
	xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://parsec.ru/Parsec3IntergationService">
	<s:complexType name="ArrayOfExtraFieldValue">
		<s:sequence>
			<s:element minOccurs="0" maxOccurs="unbounded" name="ExtraFieldValue" nillable="true" type="tns:ExtraFieldValue"/>
		</s:sequence>
	</s:complexType>
	<s:complexType name="ExtraFieldValue">
		<s:complexContent mixed="false">
			<s:extension base="tns:BaseObject">
				<s:sequence>
					<s:element minOccurs="1" maxOccurs="1" name="TEMPLATE_ID" type="s1:guid"/>
					<s:element minOccurs="0" maxOccurs="1" name="VALUE"/>
				</s:sequence>
			</s:extension>
		</s:complexContent>
	</s:complexType>
	<s:element name="SetPersonExtraFieldValue">
		<s:complexType>
			<s:sequence>
				<s:element minOccurs="1" maxOccurs="1" name="personEditSessionID" type="s1:guid"/>
				<s:element minOccurs="1" maxOccurs="1" name="templateID" type="s1:guid"/>
				<s:element minOccurs="0" maxOccurs="1" name="value"/>
			</s:sequence>
		</s:complexType>
	</s:element>
	<s:element name="SetPersonExtraFieldValues">
		<s:complexType>
			<s:sequence>
				<s:element minOccurs="1" maxOccurs="1" name="personEditSessionID" type="s1:guid"/>
				<s:element minOccurs="0" maxOccurs="1" name="values" type="tns:ArrayOfExtraFieldValue"/>
			</s:sequence>
		</s:complexType>
	</s:element>
</wsdl:definitions>

@veewee
Copy link
Contributor

veewee commented Jul 30, 2024

Hello @LazyTechwork,

That's not a parsable wsdl.
However, I suppose it's a literal document and that this specific service requires any/anyTypes to be SOAP-encoded.

Can you try something like this:

use Soap\Encoding\Encoder\Context;
use Soap\Encoding\Encoder\SimpleType\ScalarTypeEncoder;
use Soap\Encoding\Encoder\XmlEncoder;
use VeeWee\Reflecta\Iso\Iso;

$registry->addSimpleTypeConverter(
    'http://www.w3.org/2001/XMLSchema',
    'anyType',
    new class implements XmlEncoder {
        public function iso(Context $context): Iso
        {
            return (new ScalarTypeEncoder())->iso(new Context(
                $context->type,
                $context->metadata,
                $context->registry,
                $context->namespaces,
                BindingUse::ENCODED
            ));
        }
    }
);

When the BindingUse is set to ENCODED, the xsi:type will be automatically added by the encoding package.
I'm assuming the type is xs:anyType. You can use the wsdl binary in your vendor/bin folder to check if it has a different type if this is not working.

If this works, We could add a $context->withBindingUse() shortcut to it.

@LazyTechwork
Copy link
Author

I added this and got Failed encoding type LazyTechwork\\Parsec\\Types\\SetPersonExtraFieldValues as {http://parsec.ru/Parsec3IntergationService:setpersonextrafieldvalues}. Failed at path \"SetPersonExtraFieldValues.values.ExtraFieldValue.VALUE\".

@LazyTechwork
Copy link
Author

That's not a parsable wsdl.

Here it is: https://pastebin.com/7MNiHgBQ

@veewee
Copy link
Contributor

veewee commented Jul 30, 2024

Hello,

The error you are getting is most likely because an invalid value gets passed in.

I've quickly set up a POC and this seems to not throw any errors:

<?php
use Soap\Encoding\Driver;
use Soap\Encoding\Encoder\Context;
use Soap\Encoding\Encoder\SimpleType\ScalarTypeEncoder;
use Soap\Encoding\Encoder\XmlEncoder;
use Soap\Encoding\EncoderRegistry;
use Soap\Wsdl\Loader\StreamWrapperLoader;
use Soap\WsdlReader\Locator\ServiceSelectionCriteria;
use Soap\WsdlReader\Model\Definitions\BindingUse;
use Soap\WsdlReader\Wsdl1Reader;
use VeeWee\Reflecta\Iso\Iso;

require_once __DIR__ . '/vendor/autoload.php';

$wsdl = (new Wsdl1Reader(new StreamWrapperLoader()))('parsec.wsdl');
$driver = Driver::createFromWsdl1(
    $wsdl,
    ServiceSelectionCriteria::defaults(),
    $registry = EncoderRegistry::default(),
);

$registry->addSimpleTypeConverter(
    'http://www.w3.org/2001/XMLSchema',
    'anyType',
    new class implements XmlEncoder {
        public function iso(Context $context): Iso
        {
            return (new ScalarTypeEncoder())->iso(new Context(
                $context->type,
                $context->metadata,
                $context->registry,
                $context->namespaces,
                BindingUse::ENCODED
            ));
        }
    }
);


$encoded = $driver->encode('SetPersonExtraFieldValue', [
    [
        'personEditSessionID' => '962bf124-2b58-4ed3-b63f-adaa73a29e91',
        'templateID' => 'dcf55649-10d0-4572-a61b-2b804f5a25a1',
        'value' => 789,
    ]
]);
dump($encoded);

However, it is still not able to add the xsi:type because the ElementEncoder gets wrapped around it as a parent encoder before encoding the actual value. So in order for this to work, I'll most likely need to add a feature like Feature\IncludeXsiInformation which the encoder can implement so that the element encoder can use this to opt-in on the xsi:type generation.

Will try to pick it up later this week and keep you posted on the mather.

@veewee
Copy link
Contributor

veewee commented Aug 1, 2024

Hello @LazyTechwork,

I've made some changes to the encoding package to make this possible.
More info : php-soap/encoding#19

You can also find the code you need to get this xsi:type attribute added inside the description of that issue.
It results in following XML for the example POC I pasted above:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
        <tns:SetPersonExtraFieldValue xmlns:tns="http://parsec.ru/Parsec3IntergationService">
            <tns:personEditSessionID xmlns:tns="http://parsec.ru/Parsec3IntergationService">
                962bf124-2b58-4ed3-b63f-adaa73a29e91
            </tns:personEditSessionID>
            <tns:templateID xmlns:tns="http://parsec.ru/Parsec3IntergationService">
                dcf55649-10d0-4572-a61b-2b804f5a25a1
            </tns:templateID>
            <value xmlns:s="http://www.w3.org/2001/XMLSchema" xsi:type="s:int"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">789
            </value>
        </tns:SetPersonExtraFieldValue>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

@veewee
Copy link
Contributor

veewee commented Aug 1, 2024

Just noticed the value element is not prefixed anymore.
A fix was provided in php-soap/encoding#20 with a new and improved code example on how to use it in your specific case:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
        <tns:SetPersonExtraFieldValue xmlns:tns="http://parsec.ru/Parsec3IntergationService">
            <tns:personEditSessionID xmlns:tns="http://parsec.ru/Parsec3IntergationService">
                962bf124-2b58-4ed3-b63f-adaa73a29e91
            </tns:personEditSessionID>
            <tns:templateID xmlns:tns="http://parsec.ru/Parsec3IntergationService">
                dcf55649-10d0-4572-a61b-2b804f5a25a1
            </tns:templateID>
            <tns:value xmlns:s="http://www.w3.org/2001/XMLSchema" xsi:type="s:int"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:tns="http://parsec.ru/Parsec3IntergationService">789
            </tns:value>
        </tns:SetPersonExtraFieldValue>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

@LazyTechwork
Copy link
Author

@veewee Thank you very much! It really works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants