Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefix local types with parent type-name #41

Merged
merged 3 commits into from
Jan 3, 2025

Conversation

veewee
Copy link
Member

@veewee veewee commented Jan 2, 2025

Q A
Type improvement
BC Break yes
Fixed issues

Summary

Introduced for phpro/soap-client#561
The newly introduced ParentContext can also be used as a starting point to fix issues like phpro/soap-client#540

The PR changes the way the name of an attribute type is calculated:

  • If the attribute has a base-type, that one will be used.
  • If it has an inline simpleType, that one will be used.
  • If it has an inline simpleType, that extends a restriction without any additional restriction rules, the name of the restricted base type will be used.
  • If there are restriction rules (e.g. enumerations or others), the declaring parent type will be detected:
    • In case of extending types, the system will try to detect the type-name of the type that is actually declaring the attribute. This resolves inheritence conflicts.

This PR also changes the way element type names are calculated:

  • The element name will be used for global elements
  • The type name will be used for local elements with a fallback to the element name if the type is anonymous.
  • When the type consists of an inline simple-type, the name of the element will be prefixed with the name of the parent type for more uniqueness.

In order to detect the parents, a newly ParentContext is introduced.

@veewee veewee force-pushed the attribute-type-names branch from 8e37325 to 5ea8ffe Compare January 2, 2025 12:04
@MudrakIvan
Copy link

MudrakIvan commented Jan 2, 2025

Hello @veewee,

from my testing, when I have the restriction for an attribute, then the prepending works, but when I have the restriction for an element, the prepending does not seem to work.

Here is the WSDL I'm testing on (currently, only the types are important):

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://example.com/customerdetails"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://example.com/customerdetails"
    name="CustomerDetailsService">

    <!-- Data Types -->
    <types>
        <schema xmlns="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://example.com/customerdetails"
            xmlns:tns="http://example.com/customerdetails"
            elementFormDefault="qualified">

            <complexType name="Customer">
                <sequence>
                    <element name="customerName" type="string" />
                    <element name="customerEmail" type="string" />
                </sequence>
            </complexType>

            <complexType name="CertifedCustomer">
                <complexContent>
                    <extension base="tns:Customer">
                        <sequence>
                            <element name="certificationLevel" type="string" />
                        </sequence>
                    </extension>
                </complexContent>
            </complexType>

            <element name="GetCustomerDetailsRequest">
                <complexType>
                    <sequence>
                        <element name="customerId" type="string" />
                    </sequence>
                </complexType>
            </element>
            <element name="GetCustomerDetailsResponse">
                <complexType>
                    <sequence maxOccurs="unbounded">
                        <choice>
                            <element name="Customer" type="tns:Customer" />
                            <element name="CertifedCustomer" type="tns:CertifedCustomer" />
                        </choice>
                    </sequence>
                </complexType>
            </element>

            <element name="WsTestResponse">
                <complexType>
                    <sequence>
                        <element name="WsTestId" type="string">
                        </element>
                        <element name="PosledniPrijataDavka">
                            <complexType>
                                <sequence>
                                    <element name="Cislo" type="unsignedLong">
                                    </element>
                                    <element name="Stav">
                                        <simpleType>
                                            <restriction base="string">
                                                <enumeration value="Neodeslana" />
                                                <enumeration value="Odeslana" />
                                                <enumeration value="OdeslanaOdmitnuta" />
                                                <enumeration value="PotvrzenaBezChyb" />
                                                <enumeration value="PotvrzenaSChybou" />
                                            </restriction>
                                        </simpleType>
                                    </element>
                                </sequence>
                            </complexType>
                        </element>
                        <element name="PosledniOdeslanaDavka">
                            <complexType>
                                <sequence>
                                    <element name="Cislo" type="unsignedLong">
                                    </element>
                                    <element name="Stav">
                                        <simpleType>
                                            <restriction base="string">
                                                <enumeration value="Neodeslana" />
                                                <enumeration value="Odeslana" />
                                                <enumeration value="OdeslanaOdmitnuta" />
                                                <enumeration value="PotvrzenaBezChyb" />
                                                <enumeration value="PotvrzenaSChybou" />
                                            </restriction>
                                        </simpleType>
                                    </element>
                                </sequence>
                            </complexType>
                        </element>
                    </sequence>
                </complexType>
            </element>

            <complexType name="tSouborHP">
                <sequence>
                    <element name="Konverze">
                        <simpleType>
                            <restriction base="string">
                                <enumeration value="0" />
                                <enumeration value="1" />
                            </restriction>
                        </simpleType>
                    </element>
                    <element name="TypTisku">
                        <simpleType>
                            <restriction base="string">
                                <enumeration value="0" />
                                <enumeration value="1" />
                            </restriction>
                        </simpleType>
                    </element>
                </sequence>
            </complexType>

            <complexType name="VehicleCoreType">
                <sequence>
                    <element name="VehType" minOccurs="0" type="string" />

                    <element name="DriveType">
                        <simpleType>
                            <restriction base="string">
                                <enumeration value="AWD" />
                                <enumeration value="4WD" />
                                <enumeration value="Unspecified" />
                            </restriction>
                        </simpleType>
                    </element>
                </sequence>
            </complexType>
        </schema>
    </types>

    <!-- Message Definitions -->
    <message name="GetCustomerDetailsRequestMessage">
        <part name="parameters" element="tns:GetCustomerDetailsRequest" />
    </message>
    <message name="GetCustomerDetailsResponseMessage">
        <part name="parameters" element="tns:GetCustomerDetailsResponse" />
    </message>

    <!-- Port Type (Abstract Interface) -->
    <portType name="CustomerDetailsPortType">
        <operation name="GetCustomerDetails">
            <input message="tns:GetCustomerDetailsRequestMessage" />
            <output message="tns:GetCustomerDetailsResponseMessage" />
        </operation>
    </portType>

    <!-- Binding (Concrete Implementation) -->
    <binding name="CustomerDetailsBinding" type="tns:CustomerDetailsPortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="GetCustomerDetails">
            <soap:operation soapAction="http://example.com/GetCustomerDetails" />
            <input>
                <soap:body use="literal" />
            </input>
            <output>
                <soap:body use="literal" />
            </output>
        </operation>
    </binding>

    <!-- Service Definition -->
    <service name="CustomerDetailsService">
        <documentation>This service provides customer details based on customer ID.</documentation>
        <port name="CustomerDetailsPort" binding="tns:CustomerDetailsBinding">
            <soap:address location="http://example.com/customerdetails/service" />
        </port>
    </service>
</definitions>

@veewee
Copy link
Member Author

veewee commented Jan 2, 2025

@MudrakIvan Can you try again with these new changes?
I've also fixed an error during encoding / decoding so if you could try it by actually sending a request to the SOAP server, that would be very nice! :)

@veewee veewee changed the title Prefix attribute type-names with parent type name. Prefix local types with parent type-name Jan 2, 2025
@MudrakIvan
Copy link

@veewee I've tested it, and everything seems to work (even the complex type with any element).

Would it be possible to prepend the name of the parent type for complex types as well (via some config, of course)? It could resolve some problems with different types with the same names in the same namespaces (but with other parents).

@veewee
Copy link
Member Author

veewee commented Jan 2, 2025

Can you provide an example of what you are trying to achieve?
I'm not sure if adding more typename changes is a good idea. This is all becoming quite complex to be fair ...

@MudrakIvan
Copy link

MudrakIvan commented Jan 2, 2025

Sure. It's really similar to the local enums.

XSD for the example:

<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://example.com/customerdetails"
    xmlns:tns="http://example.com/customerdetails"
    elementFormDefault="qualified">

    <complexType name="Customer">
        <sequence>
            <element name="customerName" type="string" />
            <element name="customerEmail" type="string" />
            <element name="address">
                <complexType>
                    <sequence>
                        <element name="street" type="string" />
                        <element name="city" type="string" />
                        <element name="state" type="string" />
                        <element name="zip" type="string" />
                        <element name="description" type="string" minOccurs="0" maxOccurs="1" />
                    </sequence>
                </complexType>
            </element>
        </sequence>
    </complexType>

    <complexType name="Company">
        <sequence>
            <element name="companyName" type="string" />
            <element name="companyEmail" type="string" />
            <element name="address">
                <complexType>
                    <sequence>
                        <element name="street" type="string" />
                        <element name="city" type="string" />
                        <element name="state" type="string" />
                        <element name="zip" type="string" />
                    </sequence>
                </complexType>
            </element>
        </sequence>
    </complexType>

</schema>

I want to determine which address I'm using because the nested schemes are typically different. This example does not make much sense, but structures like those are in the XSD of the external system I'm trying to communicate with.

@veewee
Copy link
Member Author

veewee commented Jan 2, 2025

Okay, I see ... It might indeed make sense to just prefix those by-default as well.
I'll try to see if I can come up with something for this tomorrow.

@veewee
Copy link
Member Author

veewee commented Jan 3, 2025

@MudrakIvan

I've looked into it deeper and won't do that for now:

This would make the generated code not compatible with PHP's ext-soap's classmap:

array(4) {
  [0] =>
  string(82) "struct Customer {
 string customerName;
 string customerEmail;
 address address;
}"
  [1] =>
  string(97) "struct address {
 string street;
 string city;
 string state;
 string zip;
 string description;
}"
  [2] =>
  string(79) "struct Company {
 string companyName;
 string companyEmail;
 address address;
}"
  [3] =>
  string(76) "struct address {
 string street;
 string city;
 string state;
 string zip;
}"
$sc = new \Soap\ExtSoapEngine\AbusedClient('foo.wsdl', [
    'classmap' => [
        'Customer' => Customer::class,
        'address' => Address::class,
        'Company' => Company::class
    ]
]);

Which means that if I apply this type-change, people on soap-client 2 and 3 will get classes that are not encodeable / decodable with PHP's soap driver. It's a breaking change I can't really afford at the moment.

Maybe in a few years, when people have migrated to the latest soap-client which uses the customly written encoder component it will be possible to sunset and introduce this behaviour.

It's something I could benifit from as well in the service I'm working with. But it won't be for now.

@veewee veewee merged commit ffb4e82 into php-soap:main Jan 3, 2025
14 checks passed
@veewee veewee added the enhancement New feature or request label Jan 3, 2025
@MudrakIvan
Copy link

@veewee I undrestand the BC concern. However, do you think it could be triggered by configuration (if it could be easily added) or via some custom TypeReplacer?

@veewee
Copy link
Member Author

veewee commented Jan 3, 2025

A TypeReplacer only runs on code generation, not when encoding. Besides that, it doesnt have a lot of parent type context. So it's probably not a good place.

It's not something that is easily added.
You mention complexTypes in complexTypes - but I suppose there are many more other situations possible where you'dd want to work this similar to this example.

If you want to make this configurable, the best place is to add some additional configuration to Wsdl1MetadataProvider that can somehow trigger this logic or makes it possible to hook into specific parts of the resolving process. As mentioned before, this all is quite complex already.

I'm currently not going to implement it myself. But I surely don't mind helping out if you care to craft a PR for that yourself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants