soap-client icon indicating copy to clipboard operation
soap-client copied to clipboard

unsignedInt type not supported

Open RosskoDCA opened this issue 1 year ago • 11 comments

Q A
Version 3.1.1

Support Question

I tried fixed this in this PR but it didn't work. I am not sure, if this is an issue or future request or something else, so I am asking for support.

There's WSDL document in attachment (I hope I clean up that file correctly) where is xs type unsignedInt

For now my config looks like this:

$options = ExtSoapOptions::defaults($wsdl, [
    'login' => '...',
    'password' => '...',
])->withClassMap(RonClassmap::getCollection())->withTypeMap(RonTypemap::getCollection());

return Config::create()
    ->setEngine($engine = DefaultEngineFactory::create($options))
    ->setTypeDestination('src/TerminalData/Type')
    ->setTypeNamespace('App\TerminalData\Type')
    ->setClientDestination('src/TerminalData')
    ->setClientNamespace('App\TerminalData')
    ->setClassMapName('TerminalDataClassmap')
    ->setClassMapDestination('src/TerminalData')
    ->setClassMapNamespace('App\TerminalData')
    ->addRule(new Rules\AssembleRule(new StrictTypesAssembler()))
    ->addRule(new Rules\AssembleRule(new Assembler\FinalClassAssembler()))
    ->addRule(new Rules\AssembleRule(new Assembler\FluentSetterAssembler()))
    ->addRule(new Rules\AssembleRule(new Assembler\GetterAssembler(
        (new Assembler\GetterAssemblerOptions())
            ->withDocBlocks(false)
            ->withBoolGetters()
            ->withOptionalValue()
    )))
    ->addRule(new Rules\AssembleRule(new PropertyAssembler(
        (new Assembler\PropertyAssemblerOptions())
            ->withDocBlocks(false)
            ->withTypeHints()
            ->withOptionalValue()
    )))
    ->addRule(new Rules\AssembleRule(new Assembler\ImmutableSetterAssembler(
        (new Assembler\ImmutableSetterAssemblerOptions())
            ->withDocBlocks(false)
    )))
    ->addRule(
        new Rules\IsRequestRule(
            $engine->getMetadata(),
            new Rules\MultiRule([
                new Rules\AssembleRule(new Assembler\RequestAssembler()),
                new Rules\AssembleRule(new Assembler\ConstructorAssembler(new Assembler\ConstructorAssemblerOptions())),
            ])
        )
    )
    ->addRule(
        new Rules\IsResultRule(
            $engine->getMetadata(),
            new Rules\MultiRule([
                new Rules\AssembleRule(new Assembler\ResultAssembler()),
            ])
        )
    )
    ->addRule(
        new Rules\IsExtendingTypeRule(
            $engine->getMetadata(),
            new Rules\AssembleRule(new Assembler\ExtendingTypeAssembler())
        )
    )
    ->addRule(
        new Rules\IsAbstractTypeRule(
            $engine->getMetadata(),
            new Rules\AssembleRule(new Assembler\AbstractClassAssembler())
        )
    )
;

that classmap is generated and typemap is basically the same as default one, but I added custom type for DateTime (I was dealing with this in past, the dateTime converter converts the XML to attribute with name <dateTime> but I needed <dateTimeFrom> etc.)

When I run the types generate command, it generates that unsignedInt type like

private ?\App\TerminalData\Type\UnsignedInt $relayMask = null;

because of this \Phpro\SoapClient\CodeGenerator\Model\Property::getType the 'usnignedInt' is not "knownType"

But it is native soap type also, which means, we can not find definition in WSDL. Now I do not know how to avoid this. I tried and succeed overriding the PropertyAssember to my own and with some dirty hack I changed that unsignedInt to int or mixed, it doesn't metter, but then I realize there's getter & setter with App\TerminalData\Type\UnsignedInt type also. I do not want to rewrite those assemblers too.

To show I just exhaust all the options, now I have added new rule in config:

->addRule(
        new Rules\PropertynameMatchesRule(
            new Rules\MultiRule([
                new Rules\AssembleRule(
                    new PropertyAssembler(
                        (new Assembler\PropertyAssemblerOptions())
                            ->withDocBlocks(false)
                            ->withTypeHints(false)
                            ->withOptionalValue()
                    )
                ),
                new Rules\AssembleRule(
                    new Assembler\FluentSetterAssembler(
                        (new Assembler\FluentSetterAssemblerOptions())
                            ->withDocBlocks(false)
                            ->withTypeHints(false)
                            ->withReturnType(false)
                    )
                ),
                new Rules\AssembleRule(
                    new Assembler\GetterAssembler(
                        (new Assembler\GetterAssemblerOptions())
                            ->withDocBlocks(false)
                            ->withBoolGetters(false)
                            ->withOptionalValue()
                    )
                ),
            ]),
            '/^relayMask$/'
        )
    )

which just remove the type from that one property. But I am not satisfied with this.

Is there a way to changed unsignedInt to something acceptable in PHP? I mean, there's range problem with native type int in PHP (in 32bit systems), there's logical problem with negative numbers, and there's also logical problem with decimal places if we map that type to float.

I hope I describe it understandable and I hope you can help me with this.

Daniel

attachment: terminal_data.zip

RosskoDCA avatar Mar 06 '24 09:03 RosskoDCA

Thanks for reporting,

Can you swap:

    ->setEngine($engine = DefaultEngineFactory::create($options))

With

    ->setEngine($engine = CodeGeneratorEngineFactory::create(
        $options->getWsdl(),
        // Optional if you wanna load a local file instead of over HTTP : new FlatteningLoader(new StreamWrapperLoader())
    ))

As documented in the v3 changelog : https://github.com/phpro/soap-client/blob/v3.x/UPGRADING.md#v2-to-v3

For me, this generates:

    /**
     * @var int
     */
    private int $relayMask;

    /**
     * @return int
     */
    public function getRelayMask() : int
    {
        return $this->relayMask;
    }

    /**
     * @param int $relayMask
     * @return static
     */
    public function withRelayMask(int $relayMask) : static
    {
        $new = clone $this;
        $new->relayMask = $relayMask;

        return $new;
    }

(I've just used a configuration from an old project to quickly test, so the generated methods might be different from what you configured)

veewee avatar Mar 06 '24 12:03 veewee

@veewee Definitelly i can try, but the WSDL document (in my particular case) is behind basic auth. Can you help me how to pass the authentication ?

RosskoDCA avatar Mar 06 '24 12:03 RosskoDCA

Something like this:

use Http\Client\Common\Plugin\AuthenticationPlugin;
use Http\Client\Common\PluginClient;
use Http\Discovery\Psr18ClientDiscovery;
use Http\Message\Authentication\BasicAuth;
use Soap\Psr18Transport\Wsdl\Psr18Loader;
use Soap\Wsdl\Loader\FlatteningLoader;


    ->setEngine($engine = CodeGeneratorEngineFactory::create(
        'http://yourremotewsdl',
        new FlatteningLoader(Psr18Loader::createForClient(
            new PluginClient(
                Psr18ClientDiscovery::find(),
                [
                    new AuthenticationPlugin(
                        new BasicAuth('username', 'password')
                    )
                ]
            )
        ))
    ))

veewee avatar Mar 06 '24 12:03 veewee

Great. It works perfectly. There's just one last issue, which I believe you will solve far more sooner than me. I am posting the whole unchanged WSDL in attachment.

The last thing is, it generates class TContractNumbers as:

<?php

declare(strict_types=1);

namespace App\TerminalData\Type;

use \App\TerminalData\Type\ArrayType;

final class TContractNumbers extends ArrayType
{
}

but that arrayType don't exists.

RosskoDCA avatar Mar 06 '24 13:03 RosskoDCA

That looks like a bug in the code generation part indeed:

      <xs:complexType name="TContractNumbers">
        <xs:complexContent>
          <xs:restriction base="soapenc:Array">
            <sequence xmlns="http://www.w3.org/2001/XMLSchema"/>
            <xs:attribute ref="soapenc:arrayType" n1:arrayType="xs:string[]" xmlns:n1="http://schemas.xmlsoap.org/wsdl/"/>
          </xs:restriction>
        </xs:complexContent>
      </xs:complexType>

Results in

TContractNumbers
================

> urn:RON_PersonalData_Utils:TContractNumbers extends Array

+------------+--------------------------------------------------------------------+
| Meta       | Value                                                              |
+------------+--------------------------------------------------------------------+
| isAbstract | false                                                              |
| docs       |                                                                    |
| extends    | {                                                                  |
|            |     "type": "Array",                                               |
|            |     "namespace": "http:\/\/schemas.xmlsoap.org\/soap\/encoding\/", |
|            |     "isSimple": false                                              |
|            | }                                                                  |
+------------+--------------------------------------------------------------------+

Making the IsExtendingTypeRule kick in and extend Array (which get normalized to ArrayType since that is a reserved keyword)

The IsExtendingTypeRule should become smarter and skip some extensions like:

  • soapenc:array
  • enc12:array
  • apache:Map
  • ...

(reference - this logic is being used in the wsdl-reader : https://github.com/php-soap/wsdl-reader/blob/main/src/Metadata/Converter/Types/Rule/SkipArrayTypePropertiesRule.php. It's probably something to get rid of in the wsdl-reader part so that we get as good as possible meta-types and instead implement in here directly to make sure that those classes get ignored during code-generation in favour of regular array types.)

veewee avatar Mar 06 '24 13:03 veewee

Should I take a look and make PR?

RosskoDCA avatar Mar 06 '24 13:03 RosskoDCA

Sure, feel free to provide a PR. We can see where to go from there.

veewee avatar Mar 06 '24 13:03 veewee

I am afraid I don't even know how to start. Unfortunately my soap experience is limited.

RosskoDCA avatar Mar 06 '24 14:03 RosskoDCA

I can imagine, it's not an easy task to get started with I'm afraid. Also, whilst commuting I figured the information on how to solve it is not an ideal solution because it generates the TContractNumbers type which should be infered to be considered an array - or even better - a list<string> and not a generated type class.

To fix the issue, it requires a couple of things:

  • The wsdl-reader should not filter out known array types and instead parse them to the bottom XSD types. (As-in : remove the SkipArrayTypePropertiesRule)
  • Ideally, the array meta-data should also be made available through the WSDL reader : https://github.com/php-soap/wsdl-reader/issues/9
  • This package should {wrap the meta-data with manipulations](https://github.com/phpro/soap-client/blob/v3.x/src/Phpro/SoapClient/Soap/Metadata/ManipulatedMetadata.php) to normalize these special array types for code generation. (PHP's ext-soap SoapClient contains special encoders/decoders for these array types, so in code those should be regular arrays instead of e.g. TContractNumbers.

Even though the 3 points above describe what needs to happen, it won't be very easy to cover all possible XSD configurations from the first try. As-in: it would probably take me around a day to fix this decently as well :)

veewee avatar Mar 06 '24 15:03 veewee

I have to ask. Is there any chance you would look at it in near future?

RosskoDCA avatar Mar 07 '24 11:03 RosskoDCA

I'm a bit limited in spare-time at the moment, so don't expect this to land in the near future from my side. However, if you want me to prioritize this, my work-time is for sale:

https://github.com/php-soap/.github/blob/main/HELPING_OUT.md#want-to-help-out-

Feel free to reach out by mail (soap at phpro.be) if you are intereset in investing this feature into the project.

veewee avatar Mar 07 '24 12:03 veewee

i've faced with trouble on generate:classmap/types it return me an exeption with Expected "non-empty-string", got "string". How to fix that?

blessq12 avatar Apr 01 '24 10:04 blessq12

Added this specific array issue to the list for v4 at https://github.com/phpro/soap-client/issues/485. Closing this one for now.

veewee avatar Apr 23 '24 05:04 veewee

Hello there @RosskoDCA ,

I wanted to get back to this issue: We are finishing up v4 of the soap-client package which uses a new encoding system internally. With this new system, you should be able to get around this issue more easily.

We are eager to receive some early feedback on this new release. If you got some time, feel free to play around with the alpha version:

  • Release: https://github.com/phpro/soap-client/releases/tag/4.0.0-alpha1
  • Discussion: https://github.com/phpro/soap-client/discussions/527

veewee avatar Jul 05 '24 09:07 veewee