WsdlToPhp / PackageGenerator

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

WSDL not reacting how others do #231

Open faithfulman18 opened 3 years ago

faithfulman18 commented 3 years ago

Hello,

I've used the generator on a WSDL that has a number of other .xsd and dtd files associated with it. And one where I had to manually download the WSDL and convert it. But otherwise i'm running the process as normal and that worked fine and I imported it into my project.

However, going to use it there is an inner object that contains almost every property like this. However, it appears that the only property in these sub objects is showing as this:

  $vendorBodyHeader = new \THIS_WSDL\StructType\vendorHeader_CType();
  $vendorBodyHeader->ConsumerName = new \THIS_WSDL\StructType\ConsumerName_Type();
  $vendorBodyHeader->ConsumerProduct = new \THIS_WSDL\StructType\ConsumerProduct_Type();
  $vendorBodyHeader->InternalEnv = new \THIS_WSDL\StructType\InternalEnv_Type();
<?php

namespace THIS_WSDL\StructType;

use \WsdlToPhp\PackageBase\AbstractStructBase;

/**
 * This class stands for ConsumerName_Type StructType
 * Meta information extracted from the WSDL
 * - documentation: The name of the service consumer (Business name)
 * @subpackage Structs
 */
class ConsumerName_Type extends AbstractStructBase
{
    /**
     * The _
     * @var string
     */
    public $_;
    /**
     * Constructor method for ConsumerName_Type
     * @uses ConsumerName_Type::set_()
     * @param string $_
     */
    public function __construct($_ = null)
    {
        $this
            ->set_($_);
    }

I don't seem to have a way to set these variables or I'm not understanding what is expected. Any ideas what is going on?

As a side note, when I opened this WSDL in SOAPUI, each one seems to be more of a string and I can just type it in to the proper tag.

oh, also, when running the call, this section is all blank not even bring in these properties into the soap packet.

??

mikaelcom commented 3 years ago

The CType name rings a bell to me from an old WSDL.

What is bothering you? The fact that the ConsumerName_Type only contains the _ property? I can say that it happens in other WSDL that the string value is contained by an underscore and that's it.

Le me know if you can share the WSDL here or at contact@mikael-delsol.fr.

faithfulman18 commented 3 years ago

The issue is that when doing something like this:

    $vendorBodyHeader = new \THIS_WSDL\StructType\vendorHeader_CType();
    $vendorBodyHeader->ConsumerName = new \THIS_WSDL\StructType\ConsumerName_Type("NAMELISTED");
    $vendorBodyHeader->ConsumerProduct = new \THIS_WSDL\StructType\ConsumerProduct_Type("PRODUCTLISTED");

I'd expect the soap packet to contain something like:

        <ns1:vendorHeader>
          <ns1:ConsumerName>NAMELISTED</ns1:ConsumerName>
          <ns1:ConsumerProduct>PRODUCTLISTED</ns1:ConsumerProduct>
          ...
          ...
          ...
          ...
          <ns1:thisPropertyNotBeingUsed/>
        </ns1:vendorHeader>

But instead it shows up like this:

        <ns1:vendorHeader>
          <ns1:thisPropertyNotBeingUsed/>
        </ns1:vendorHeader>

So, not essentially two problems.

  1. The data I'm setting, doesn't get into the soap packet (what is causing this?)
  2. Some random properties I'm not setting show up in the outbound packet, but only some. If I set just those two, and there are 10 properties that I could define, only some of them populate. Shouldn't they all populate? What makes only some of them show up even though they don't have some data in them.

Hopefully that makes sense.

Thanks,

Kevin

mikaelcom commented 3 years ago

I'd bet it's the SoapClient class based on the WSDL that does not handle correctly the PHP data transformation to the SOAP XML Request.

The simpliest way to validate my hypothesis is to use the native SoapClient class, without using any of the generated classes and passing a basic array to the method with the required data and see how it goes.

Le me know then, good luck

faithfulman18 commented 3 years ago

If it is that, is there any recourse or are you saying I'm stuck? Or is there just a different soapClient class that is needed?

And what is the easiest way to swap out the soapClient. Would it be this:

\WsdlToPhp\PackageBase\AbstractSoapClientBase::DEFAULT_SOAP_CLIENT_CLASS => '\SoapClient',

?

mikaelcom commented 3 years ago

The native PHP SoapClient is funtionning well in most of the cases, most does not mean all...

You can look to the NuSOAP client or the PHPro SoapClient (never used it).

This would mean you have to use the soapclient option when generating the package in order to use your own implementation of SoapClient base class which would allow you to use another SoapClient class instead of the native PHP SoapClient, the way it is used within the PackageEws365 package

faithfulman18 commented 3 years ago

When running the basic php soapClient processes lots of stuff is incorrect including the array I passed in, while the data shows up it lists the property as param1 instead of the correctly named property name.

So, with that said, I assume that is to mean I'll need to use a custom soap client? Is there a good way to know what all I'll need to customize and how to know what is not allowing the data to come through correctly?

mikaelcom commented 3 years ago

So, with that said, I assume that is to mean I'll need to use a custom soap client? Is there a good way to know what all I'll need to customize and how to know what is not allowing the data to come through correctly?

I can't tell unless having the WSDL in hand. I work as a freelance if you need further help ;)

faithfulman18 commented 3 years ago

Unfortunately, this is a vendor relationship and due to an NDA I'm not able to share the WSDL.

However, I discovered something. (And I didn't have to switch to using a custom soapClient)

If I fill in all the fields with the appropriate objects at creation of the initial object, instead of defining all the properties later, everything fills into the soap packet just fine.

$vendorBodyHeader = new \THIS_WSDL\StructType\vendorHeader_CType(
  new \THIS_WSDL\StructType\Version1_Type("");
  new \THIS_WSDL\StructType\ConsumerName_Type("NAMELISTED");
  new \THIS_WSDL\StructType\Version2_Type("");
  new \THIS_WSDL\StructType\ConsumerName_Type("NAMELISTED");
  ...
  );

However, I thought the WSDL Package Generator didn't require those all to be set if the --validation=false ? Maybe I've misunderstood how that works?

I like to code like the below, because I find it easier to read.

 $vendorBodyHeader = new \THIS_WSDL\StructType\vendorHeader_CType();
 $vendorBodyHeader->ConsumerName = $consumerName;
 $vendorBodyHeader->ConsumerProduct = $consumerProduct;

Creating the initial object and below it setting all the parameters after the fact. And then when needing to set a property that is an object defining it as a variable and setting it up above.

But apparently, you have to fill in initial parameters when you first instantiate?? I also noticed that it seemed to only care about certain properties being filled in during this process, and as long as they were setup correctly initially, then you could add other properties that come after them later on just fine.

So, essentially, the below worked fine too because I defined the version 1 and version 2 properties with their objects directly when instantiating vendorHeader_CType:

 $vendorBodyHeader = new \THIS_WSDL\StructType\vendorHeader_CType(
              new \THIS_WSDL\StructType\Version1_Type(''), 
              null, 
              new \THIS_WSDL\StructType\Version2_Type(''), 
              null);
 $vendorBodyHeader->ConsumerName = $consumerName;
 $vendorBodyHeader->ConsumerProduct = $consumerProduct;

Any ideas what causes this?

Thanks,

Kevin

mikaelcom commented 3 years ago
  1. I've always been in favor of using setters and getters (personal taste). This is why the setters are fluent allowing to chain the calls
    $object
    ->setConsumerProduct($product)
    ->setConsumerName($consumerName)
  2. Setters are always generated are defined for the follownig reasons
    • The setter ensures you set property with the right value type.
      • Until now, only setters of a property of a generated class where typehinted such as public function setConsumerName(\THIS_WSDL\StructType\ConsumerName_Type $consumerName = null)
      • From next major release, setter for scalar properties will be typehinted too such as public function setVersion(?int $version = null). In your case it'll be public function setConsumerName(?\THIS_WSDL\StructType\ConsumerName_Type $consumerName = null)
    • Using the setter, the value is checked within the setter upon the validation rule(s) if there is any (validation rules are optionnal and can be disabled with false, they are enabled by default)
    • In addition, using the setter, you benefite from the feature that handles nillable properties that MUST not be sent into the XML request if they are not set. As by default they are null, they are nevertheless sent into the XML request which can be rejected by the Soap Server. The setter automatically takes care of removing the property from the object (unset($this->propertyName)). if you call back the setter with a not-null value, the property is defined back into the object.
  3. Each property is declared public which allows you to use the arrow notation to set the value
    • I might change it to protected for the next major release, I have to make some tests before ;). This means you wouldn't be able to use the arrow notation anymore except if I implement the magic __set method which would then wire it the internal setter anyway to benefit from the reasons explained above
  4. The class constructor __construct method allows to instantiate the object without any parameter as soon as no property is required. If there is a required property, you are obligated to pass the parameter(s) to the constructor
    • __construct($consumerName = null, $consumerProduct = null)
      • no issue by using $object = new Object(), you can then use the setter or not
    • Default value is sometimes provided by the WSDL leading to __construct($consumerName = null, $version = '2.2')
      • no issue by using $object = new Object(), $version property has 2.2 as value by default, you can then use the setter or not
    • __construct($version, $consumerName = null). Required properties are always put at first in the constructor. You MUST then use the syntax
      • $object = new Object(2.2), you can then use the setter or not

That's for the clarification of the generated code, I should create a kind of manifest about the generated code...

Concerning your issue, is the $consumerName variable a string or an instance of \THIS_WSDL\StructType\ConsumerName_Type?

faithfulman18 commented 3 years ago

Well, if it needs to be an instance of a \THIS_WSDL\StructType\ConsumerName_Type, then I would still do that mapping it to the $consumerName variable.

However, it's interesting. In the generated WSDL to PHP output, it is:

It is an instance of this: "\THIS_WSDL\StructType\ConsumerNameType" but with only with a parameter of "$" which is a string.

However, as long as the "Version1_Type" instance was set initially, I can either pass it the instance or set it as a string manually like below and it seems to still work normally:

 $vendorBodyHeader = new \THIS_WSDL\StructType\vendorHeader_CType(
              new \THIS_WSDL\StructType\Version1_Type(''), 
              null);
 $vendorBodyHeader->ConsumerName = 'The Consumer Name'

So, if I understand, essentially this is working as you'd expect it to and something in the actual WSDL is required to be set initially? When I generate the packages using the --validation=false flag, that isn't actually accomplishing anything?

mikaelcom commented 3 years ago

Can you paste the \THIS_WSDL\StructType\vendorHeader_CType class source code? Without the WSDL and the source code, I'm blind...

So, if I understand, essentially this is working as you'd expect it to and something in the actual WSDL is required to be set initially?

Can't say wihout the WSDL

When I generate the packages using the --validation=false flag, that isn't actually accomplishing anything?

It's not generating the validation rules within the setters, that's all.

faithfulman18 commented 3 years ago

Hi Mikael,

I've sent you a sanitized and stripped down version, of the class file via email to: contact@mikael-delsol.fr

Hopefully that helps.

Kevin

mikaelcom commented 3 years ago

It's defined as I was expecting it :), so I'm not sure to understand your issue.

What I understood it that you set the $consumerName variable with a string value. Thus it can't work (the XML request should not contain the string value) from my point of view as it's waiting for a \THIS_WSDL\StructType\ConsumerName_Type instance. Moreover, setting it from the instantiation or using the arrow notation afterwards has the same behaviour, only the contructor and the setter would fire an error if you pass a string for the consumerName property. You must respect the properties type to have the XML request well generated by the native PHP SoapClient class.

Let me know if there is still something not working taking into account what I just detailed.

faithfulman18 commented 3 years ago

Okay, well it seems to be working okay. So, I'll go from here. Thanks for all the above explanations!

mikaelcom commented 3 years ago

Always a pleasure to make my work more comprehensible and to ensure it's not unpredictable :sweat_smile:.

I'm working on a manifest that'll contain a resume of what is generated and how the classes/files are intended to be used. I hope it'll be helpful. Feel free to comment it when it'll be pushed ;)

faithfulman18 commented 3 years ago

Sounds great. Thanks!