WsdlToPhp / PackageGenerator

Generates a PHP SDK based on a WSDL, simple and powerful, WSDL to PHP
https://providr.io
MIT License
422 stars 73 forks source link

Generating structs for arbitrary XSD schemas #211

Closed rauanmayemir closed 4 years ago

rauanmayemir commented 4 years ago

It's a fairly common practice for legacy systems to provide SOAP services with arbitrary payloads, i.e one of the fields being XML string for which there are separate XSD schemas you could use. It assumes people will use something like JAXB to further process the payload in your business domain.

However, it's not very common or streamlined in PHP, so dealing with XSDs separately is a pain point. I was wondering if it's possible to leverage WsdlToPhp facilities to at least generate structs for arbitrary xsds (somehow pass it to a generator to get struct classes in return) and be able to unmarshal XML string through those struct classes?

If it's not possible yet, what would be a good way to start implementing it?

mikaelcom commented 4 years ago

If not already known, but to purely put the current process down, the current generator takes as input a WSDL schema and:

So, to summarize simply, it would normally only need to make the XSD loadable by the native SoapClient class and the Structs to be detected to be able to make the generator generate the according classes. Possibly by creating a basic WSDL squeleton from which we would import the XSD, then by using the generator with this particular WSDL, to generate the corresponding Structs defined in the XSD. Clear enough? Firsly, it would need a manual test, creating a WSDL importing the XSD and test this WSDL converstion to PHP classes. Secondly, it would need to implement this process as a particular process in the generator.

Ready to work ? :stuck_out_tongue_winking_eye:

rauanmayemir commented 4 years ago

From a practical standpoint, manually wrapping XSDs into a dummy WSDL seems easier rather than messing with a generator config.

Is XML unmarshalling handled by WsdlToPhp, or natively by SOAP? (I mean, how do I get structs from response string) I'll try to come up with a simple manual repro.

rauanmayemir commented 4 years ago

Prior to that, I noticed that without a classmap, services will return stdClass instead of designated structs. Just wanted to understand where the 'magic' happens.

mikaelcom commented 4 years ago

Prior to that, I noticed that without a classmap, services will return stdClass instead of designated structs. Just wanted to understand where the 'magic' happens.

The magic comes from the combination of the generated Classmap::get() which returns an array passed to the SoapClient as the classmap option value. Using this option with the associative array of WSDL struct name and the corresponding generated PHP class allows the SoapClient class to map the XML response to the PHP instance(s).

Doing it in place of the SoapClient is necessary in your case though as the SoapClient won't have loaded the XSD correspond to the XML embedded in the XML request. It needs some work but can be done as soon as you're familiar with DOM or using the DomHandler project to walk through the embedded XML elements and creating the instances witht the generated __set_state method within each generated Struct class.

rauanmayemir commented 4 years ago

@mikaelcom Thanks for your help, simply copy-pasting any XSD schemas inside <wsdl:types> block lets the generator create structs for them.

I'll play with manual unmarshalling next.

mikaelcom commented 4 years ago

Great, Good luck!

rauanmayemir commented 4 years ago

TLDR: I switched to https://github.com/goetas-webservices/xsd2php for handling arbitrary payloads with XSD.

So creating a dummy WSDL and dumping all your XSDs in the types block works out beautifully, I got all my types with proper structs and validation.

I even tried to make an actual WSDL-service and mock SOAP's request phase returning the payload back, but it turned out to be too much work serializing data back and forth just to make SOAP parse the output.

So I went in different direction hoping I can get away with some hacked traversing of XML payload with DomHandler (which is great, btw) and unmarshalling it into structs. However, I got stuck at casting data. Even basic cases like <field>true<field> is a show-stopper unless you somehow also use the xsd types information. That seemed like too much hassle and too little confidence in type safety (as much as one could hope for it).

The downside is that xsd2php uses JMS Serializer that feels too heavy dependencies-wise. I would have preferred not to bring it, but surprisingly it just works.