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

v4 codegen: php type encoder

Open rauanmayemir opened this issue 1 year ago • 5 comments

Q A
Version 4.0.0-alpha1

Support Question

I've been trying to generate soap client type definitions with the built-in generator, but there seems to be no way to specify e.g \Cake\Chronos\Chronos instead of \DateTimeImmutable. (Phpro\SoapClient\CodeGenerator\Util\Normalizer is set to normalize simple types to hard-coded interfaces)

I can specify custom SimpleType encoders for the client, but not for generating a client. :)

rauanmayemir avatar Sep 21 '24 16:09 rauanmayemir

Err, it was a bad example as \Cake\Chronos\Chronos implements \DateTimeInterface, so there would be no conflict. I meant to say eg:

  • \Cake\Chronos\ChronosDate instead of \DateTimeInterface for xsd:date
  • \Decimal\Decimal instead of float for xsd:decimal and xsd:double.

rauanmayemir avatar Sep 21 '24 16:09 rauanmayemir

Here's the weird thing. I was able to change php types for date or datetime fields, but for decimal it doesn't work.

I tried to write xsd as:

<xsd:element name="amount" type="xsd:decimal"/>

and as:

<xsd:element name="amount">
  <xsd:simpleType>
    <xsd:restriction base="xsd:decimal">
      <xsd:fractionDigits value="3"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:element>

Neither of them works, it just comes back as either float or mixed.

My DecimalReplacer:

final class DecimalReplacer implements TypeReplacer
{
    public function __invoke(XsdType $xsdType): XsdType
    {
        if (!$this->isDecimalType($xsdType)) {
            return $xsdType;
        }

        return $xsdType->copy(Decimal::class);
    }

    private function isDecimalType(XsdType $type): bool
    {
        $checks = [
            new IsOfType('http://www.w3.org/2001/XMLSchema', 'decimal'),
            new IsOfType('http://www.w3.org/2001/XMLSchema', 'double'),
        ];

        return any($checks, static fn (IsOfType $check): bool => $check($type));
    }
}

Another odd issue is that for datetime replacer, if I use a class like Chronos, it will be changed correctly:

    /**
     * @var \Cake\Chronos\Chronos
     */
    protected \Cake\Chronos\Chronos $from;

    /**
     * @var \Cake\Chronos\Chronos
     */
    protected \Cake\Chronos\Chronos $to;

But if I use \DateTimeImmutable, then the php type will be generated as mixed:

    /**
     * @var mixed
     */
    protected mixed $from;

    /**
     * @var mixed
     */
    protected mixed $to;

My DateTimeReplacer:

final class DateTimeReplacer implements TypeReplacer
{
    public function __construct(
        private ?IsOfType $check = null,
    ) {
        $this->check ??= new IsOfType('http://www.w3.org/2001/XMLSchema', 'dateTime');
    }

    public function __invoke(XsdType $xsdType): XsdType
    {
        if (!($this->check)($xsdType)) {
            return $xsdType;
        }

        return $xsdType->copy(Chronos::class);
    }
}

I thought the issue could come from classes being native (\DateTimeImmutable is native, \Decimal\Decimal comes from extension), but the decimal issue persists no matter what type I set.

rauanmayemir avatar Sep 28 '24 10:09 rauanmayemir

Hello @rauanmayemir,

About your first question: \DateTimeImmutable seems to work here. Do note that you need to prefix it with a backslash since it is in the PHP internal space.

The Decimal class is indeed not working. I'll have to dig into the specifics, but I suppose it is because the name of the class (without namespace) is "Decimal" which is reserverd for the translation of xsd:decimal to float in \Phpro\SoapClient\CodeGenerator\Util\Normalizer::$normalizations.

veewee avatar Sep 28 '24 12:09 veewee

You mean normalizer is stripping the namespace part and using the base class name? I tried doing return $xsdType->copy('\\Decimal\\Decimal');, but it still failed.

rauanmayemir avatar Sep 28 '24 13:09 rauanmayemir

I mean the problem is that the class name is Decimal. If you change it to Decimal2 it works. This is because the information gets normalized again in the constructor:

https://github.com/phpro/soap-client/blob/8946e9cd3537a0d2d77c49e8b882c48ee38f8d45/src/Phpro/SoapClient/CodeGenerator/Model/Property.php#L50-L57

I'll have to find away to avoid that unnecessary normalization

veewee avatar Sep 29 '24 12:09 veewee