You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP SoapClient未调用映射类__set魔术方法,如何将null转为空数组?

问题:SoapClient classmap映射类的__set()未触发,无法将null转为空数组

根据PHP官方文档,使用SoapClient的classmap选项映射的类,构造函数不会被调用,但__get()和__set()魔术方法会生效。但实际测试中,__set()和构造函数均未触发。

WSDL定义了THpSvcWTableauClesRubriques复杂类型,对应PHP类中试图通过__set()将null转为空数组[],但当SOAP服务返回无元素数组对应的null时,转换失败。

WSDL类型定义

<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>

PHP映射类

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 ?? [];
        }
    }
}

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
    ]
]);

当前PHP版本:7.3.7


解决方案

1. __set()未触发的核心原因

SoapClient在处理classmap映射类时,是通过ReflectionClass直接给类的私有属性赋值,完全绕过了__set()魔术方法——__set()仅在直接访问未定义/不可访问属性时才会被调用,这里的属性赋值逻辑被底层跳过了。

2. 可行解决方法

方法一:通过typemap自定义类型解析(推荐)

借助typemap实现自定义的XML到对象的转换逻辑,直接在构造函数中完成null转空数组的处理:

// 修改映射类,添加构造函数处理值
class THpSvcWTableauClesRubriques {
    /** @var int[] */
    public $THpSvcWCleRubrique;

    public function __construct($data = null) {
        $this->THpSvcWCleRubrique = $data['THpSvcWCleRubrique'] ?? [];
    }
}

// 实例化SoapClient时添加typemap配置
$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
    ],
    "typemap" => [
        [
            "type_name" => "THpSvcWTableauClesRubriques",
            "type_ns" => "http://你的WSDL命名空间", // 从WSDL的targetNamespace获取
            "from_xml" => function($xml) {
                $dom = new DOMDocument();
                $dom->loadXML($xml);
                $values = [];
                foreach ($dom->getElementsByTagName('THpSvcWCleRubrique') as $node) {
                    $values[] = (int)$node->nodeValue;
                }
                return new THpSvcWTableauClesRubriques(['THpSvcWCleRubrique' => $values]);
            }
        ]
    ]
]);

方法二:在__get()中做兜底处理

既然__set()无法触发,直接在获取属性时返回默认值,确保始终返回数组类型:

class THpSvcWTableauClesRubriques {
    /** @var int[] */
    private $THpSvcWCleRubrique;

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

    // 保留__set()用于手动赋值场景
    public function __set($name, $value) {
        if ($name === "THpSvcWCleRubrique") {
            $this->THpSvcWCleRubrique = $value ?? [];
        }
    }
}

方法三:实现ArrayAccess接口

让映射类实现ArrayAccess接口,通过数组式访问的逻辑处理null值:

class THpSvcWTableauClesRubriques implements ArrayAccess {
    /** @var int[] */
    private $THpSvcWCleRubrique = [];

    public function offsetExists($offset) {
        return $offset === 'THpSvcWCleRubrique';
    }

    public function offsetGet($offset) {
        return $this->THpSvcWCleRubrique;
    }

    public function offsetSet($offset, $value) {
        if ($offset === 'THpSvcWCleRubrique') {
            $this->THpSvcWCleRubrique = $value ?? [];
        }
    }

    public function offsetUnset($offset) {
        if ($offset === 'THpSvcWCleRubrique') {
            $this->THpSvcWCleRubrique = [];
        }
    }
}

注:PHP 7.3版本的SoapClient确实存在直接操作映射类属性的行为,升级到PHP 7.4+可能会有不同表现,但以上方法在7.3版本均可正常生效。

内容的提问来源于stack exchange,提问作者kagmole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 16:34:53