I spend two nights with this topic while working on a german SOAP Webservice which allows town website to synchronize with a central database about all different kinds of Request they can convern from the goverment and where they have to go for this. The WSDL was very simple for years, there were no attributes when pushing back messages to this Webservice. We just build up objects with stdClass and gave them the according properties. like $stdClassObj->PROPERTY = 'value';
Then they started using an attributes for certain properties for multilanguage purposes and added a new Object called LocalizedText and all properties which are in multilange inherited from this property. Which is where those two long nights began, because I found ALOT of different "possibile" solutions for this "simple problem" and maybe some of them work depending on the complexity of the WSDL structure. However good luck if you read this and this URLS are still online: The WSDL: https://ws-mv-schul.zfinder.de/V6_30/?wsdl And theres is also a german documentation which is not even readfriendly when you know german but at least some kind of docs: https://ws-mv-schul.zfinder.de/doc/6_30/index.html
As long as its online you maybe can connect to it to access some informations (->__getTypes()), but you cant use the function without an authentification.
So I started my tries to push back informations to this API with the correct attributes and no soluation I found on the whole internet worked for me. Some say "just put an array into SoapVar" and it will recognize what to with it and maybe this works for simple Services but not in my case. Maybe it works on other solutions because they just include one namespace in the xsd:schema section but in my case there are serveral namespaces and also subincludes of other .xsd schemes which include even more namespaces and so on. Just putting an array in one of the SOAP Type properties or even an stdClass inside an SoapVar object just resulted in nested nodes instead of an attribute.
Expected <whatever language="de_DE">Text</whatever> resulted in <whatever><_>Text</_><language>de_DE</language></whatever> with arrays or simple objects inside or outside of SoapVar. I tried arrays and objects nested / not nested and in all other combinations and IT DID NOT WORK FOR ME! So once again all tries with arrays or stdClass like $soapObject->Property = ['_' => 'Test', 'language' => 'de_DE'] or $soapObject->Property = new \SoapVar(['_' => 'Test', 'language' => 'de_DE'], SOAP_ENC_OBJECT) or a
$property = new \stdClass(); $property->_ 'Test'; $property->language 'de_DE'; $soapObject->Property = new \SoapVar($property, SOAP_ENC_OBJECT)
Resulted always in the wrong XML at submitting causing errors. The above mentioned solution by using SoapVar with property XSD_ANYXML set did work for me for testing purposes, but this was not a clean solutation for me because I just did not believe that this cannot work without creating an own XmlElement. Anyway I already started to loose my hope when I saw the answers here and on other platforms are getting older and older...
The SoapVar documentation on php.net is not explained at all, after the second construct argument like "SOAP_ENC_OBJECT" I did not understand what those parameters do and mean at all. I'm not working alot with strange XML markups so this is not my main area at developing - maybe others understand more conretly what those construct params do and mean and feel free to provide more details about everything of this:
SoapVars...
public __construct( mixed $data, ?int $encoding, ?string $typeName = null, ?string $typeNamespace = null, ?string $nodeName = null, ?string $nodeNamespace = null )
Tried some different things with those params but nothing really worked and resulted in more or less more worse XML results.
Sorry telling you this long story but I just want you to know that googeling will result in alot of answers which say "this worked" or "this worked" and the conclusion is just: It depends on the WSDL complexcity.
So I also started thinking about creating alot of own new Classes and use the SoapClients $classmap itself but before started trying this I finally found the solution (when I was drunk): I knew it must work somehow with SoapVar and it does, without using the XSD_ANYXML way, but you must know how to configure your new \SoapVar() to build the correct XML. First of all: The SoapClient must be in WSDL mode, so provide the correct WDSL URI as first parameter when bulding SoapClient. For non-wsdl mode I guess only the XSD_ANYXML can work.
So in my case alot of properties always inherit from LocalizedText and are always the same so I created ONE namespaces class in my project which has the same properties like LocalizedText from WDSL.
I also gave the class the same name but I'm not sure if this is even necessary.
<?php namespace Namespace\XY\SimpleObjects; class LocalizedText { protected $_ = ''; protected $language = ''; public function __construct($_, $language) { $this->_ = $_; $this->language = $language; } }
Then I started to create object like this and put them into SoapVar... still does NOT WORK. Then I provided the third parameter of SoapVar trying to tell the object what kind of property I want to provide: STILL DOES NOT WORK. In my case there are just to many nested kind of namespaces, "tns", inclusions of other .xsd schemes which even include more things and so on. So of course SoapVar / Client was not able to figure out what property I really want to provide which of course resulted in errors or bad XML.
Then finally I started to figure out more about "typeNamespace" (in the mentioned doc I saw tns: and I though its the same but tns: is according to W3C "this namespace", experts please provide more explanation here). So I got deeper and deeper inside the includes. Finally, maybe much to late saw xmlns:tns="http://www.tsa.de/infodienste" in xsd:schema but always inside other xmls:xy but of course one thing in case of namespaces was always the same inside the includes: The URL "http://www.tsa.de/infodienste". Providing this url and of course EVEN IF THIS IS ONLY A URI, THIS IS A NAMESPACE WE NEED, to for SoapVar constructors constr parameter typeNameSpace almost brought the solution but still errors because I still provived $typeName to the name of each property like "foo", "bar" depending on which property my Algorithm is currently parsing. I dont know why this did not work for me but finally I stopped this and just said: All those properties are inherit from this "LocalizedText" (always debug for yourself $client->__getTypes()!) and have no other differences to each other (lucky in my case?) and then it finally started working by adding properties like this:
$property = new \Namespace\XY\LocalizedText( "value", 'de_DE' ); $soapObject->PROPERTY= new \SoapVar( $property, SOAP_ENC_OBJECT, 'LocalizedText', // In my case properties always inherit from this in WDSL 'http://www.tsa.de/infodienste' // Always the same in every schema or include );
I'm happy this works for me and even more for other people and if you read this and create SOAP Webservices yourself, feel free to provide additional tips and tricks about this or finally add a real documentation to SoapVar to php.net who are not working alot this stuff to save their lifetime ;-)
I wasted alot of time by "partially" understanding this instead of simply using XSD_ANYXML, important for my education but I still dont feel safe about the other params of SoapVar.