skip to Main Content

According to the documentation, the constructor of classes mapped with the classmap option of a SoapClient is not called, but the magic methods __get() and __set() are.

I tried to use the __set() magic method, however, I fail to make it happening. It looks like both constructor and magic method __set() are ignored.

The type defined by the WSDL document:

<xsd:simpleType name="THpSvcWCleRubrique">
    <xsd:restriction base="xsd:unsignedInt"/>
</xsd:simpleType>
...
<xsd:complexType name="THpSvcWTableauClesRubriques">
    <xsd:sequence>
        <xsd:element name="THpSvcWCleRubrique" type="hp:THpSvcWCleRubrique" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
</xsd:complexType>

The corresponding PHP class:

class THpSvcWTableauClesRubriques {

    /** @var int[] */
    private $THpSvcWCleRubrique;

    public function __get($name) {
        if ($name === "THpSvcWCleRubrique") {
            return $this->THpSvcWCleRubrique;
        }
    }

    public function __set($name, $value) {
        if ($name === "THpSvcWCleRubrique") {
            $this->THpSvcWCleRubrique = $value ?? [];
        }
    }
}

And the creation of the SoapClient:

$this->soapClient = new SoapClient($this->url, [
    "login" => $this->username,
    "password" => $this->password,
    "soap_version" => SOAP_1_2,
    "features" => SOAP_SINGLE_ELEMENT_ARRAYS,
    "classmap" => [
        "THpSvcWTableauClesRubriques" => THpSvcWTableauClesRubriques::class
    ]
]);

My issue is that when there is no element in an array, the SOAP web service returns null and I would like to interpret it as [] instead. Since the magic method is not called, I still have null instead of [].

Am I missing something? Or is there an option to make null interpreted as []?
The PHP version is 7.3.7.

2

Answers


  1. Chosen as BEST ANSWER

    I couldn't make it work with the __set() magic method.
    So here is what I did as a somewhat answer: overriding the PHP SoapClient to inject some business logic after construction of SOAP entities.

    The custom SOAP client:

    class SoapClient extends SoapClient {
    
        /**
         * @param string $name
         * @param array $args
         * @return mixed
         */
        public function __call($name, $args) {
            $result = parent::__call($name, $args);
            if ($result !== null && $result instanceof SoapEntity) {
                $result->afterConstruct();
            }
            return $result;
        }
    }
    

    Entities needing after construction logic may implement SoapEntity:

    interface SoapEntity {
        function afterConstruct(): void;
    }
    

    Usage:

    class THpSvcWTableauClesRubriques implements SoapEntity {
    
        /** @var int[] */
        public $THpSvcWCleRubrique;
    
        public function afterConstruct(): void {
            if ($this->THpSvcWCleRubrique === null) {
                $this->THpSvcWCleRubrique = [];
            }
        }
    }
    

  2. This answer is just to bring some clarity to the topic of soap and PHP. The documentation is often a little unclear at this point.

    With PHP 8 you can use typed class properties with default values. Just have a look at the following example:

    
    class Example
    {
        protected array $integerCollection = [];
    }
    

    Or even quicker just use property promotion constructors …

    class Example
    {
        public function __construct()
        {
            protected array $integerCollection = [],
        }
    
    }
    

    Both variants declare $integerCollection as array. So even the declared type in the WSDL is minOccurs="0" and therefor optional, the PHP data object property will still be an array.

    You have to know, that the PHP soap client never calls a constructor. It uses __get() and __set() if there is no class property, which is not named exactly like a complex type element definition from this class. If the PHP class has a property which maches the name directly, the magic methods won ‘t be called.

    To interpret single values (null is in this context a single value) as an array, just initialize the soap client with the following option:

    <?php
    
    declare(strict_types=1);
    
    namespace Marcel;
    
    use SoapClient;
    Use SoapFault;
    
    $wsdl = __DIR__ '/path/to/definition.wsdl';
    
    try {
        $client = new SoapClient(
            $wsdl,
            [
                ...
                'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
                ...
            ]
        );
    } catch (SoapFault) {
        // error handling
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search