PHP SoapClient 调用响应缺少部分答案

发布于 2024-08-13 13:35:40 字数 5293 浏览 2 评论 0原文

我在 PHP 解析 SoapClient 调用响应时遇到问题。对于某些类型的答案,它返回空 stdClass 对象的数组,而不是初始化的 stdClass 对象。

服务器是在tomcat6上使用axis2部署的java webservice。有问题的服务调用的 Java 签名是 public Course getCourseDetails(Long courseId) Course 是一个标准 POJO,定义如下:

public class Course {
    private Long id;
    private List<Hole> holes;
    private String name;
    private String tees;

    //etc...
}

Hole 是一个仅包含原始成员的标准 POJO。

当使用 PHP 调用时,holes 成员是一个具有正确长度的数组,但每个空洞都是空的。

$args = array();
$args["courseId"] = $courseId;
$response = $client->getCourseDetails($args);
$course = $response->return;
//course has all of its primitive members set correctly: good
$holes = $course->holes;
//holes is an array with count = 18: good
$hole = $holes[0];
//hole is an empty stdClass: bad

使用 $soapClient->__getLastResponse() 打印返回的 XML,看起来正确的表示形式是:

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:getCourseDetailsResponse xmlns:ns="http://webservice.golfstats">
<ns:return xmlns:ax21="http://datastructures.server.golfstats/xsd" xmlns:ax22="http://util.java/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ax24="http://uuid.eaio.com/xsd" xsi:type="ax21:Course">
<ax21:courseLocation>Faketown, VA</ax21:courseLocation>
<ax21:courseName>Fake Links</ax21:courseName>
<ax21:dateAdded>2003-01-02</ax21:dateAdded>
<ax21:holes><ax21:id>1</ax21:id><ax21:number>1</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>2</ax21:id><ax21:number>2</ax21:number><ax21:par>3</ax21:par><ax21:yardage>150</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>3</ax21:id><ax21:number>3</ax21:number><ax21:par>5</ax21:par><ax21:yardage>502</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>4</ax21:id><ax21:number>4</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>5</ax21:id><ax21:number>5</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>6</ax21:id><ax21:number>6</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>7</ax21:id><ax21:number>7</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>8</ax21:id><ax21:number>8</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>9</ax21:id><ax21:number>9</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>10</ax21:id><ax21:number>10</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>11</ax21:id><ax21:number>11</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>12</ax21:id><ax21:number>12</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>13</ax21:id><ax21:number>13</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>14</ax21:id><ax21:number>14</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>15</ax21:id><ax21:number>15</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>16</ax21:id><ax21:number>16</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>17</ax21:id><ax21:number>17</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>18</ax21:id><ax21:number>18</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:id>1</ax21:id>
<ax21:rating>68.5</ax21:rating>
<ax21:slope>113</ax21:slope>
<ax21:tees>Blue</ax21:tees>
</ns:return>
</ns:getCourseDetailsResponse>
</soapenv:Body>
</soapenv:Envelope>

为什么每个孔都是空的 stdClass? SoapClient 解析响应的级别数是否存在已知限制?

I am having trouble with PHP parsing of a SoapClient call's response. For some types of answers, it is returning arrays of empty stdClass objects instead of initialized stdClass objects.

The server is a java webservice deployed with axis2 on tomcat6. The Java signature of the problematic service call is public Course getCourseDetails(Long courseId) Course is a standard POJO defined as:

public class Course {
    private Long id;
    private List<Hole> holes;
    private String name;
    private String tees;

    //etc...
}

Hole is a standard POJO with only primative members.

When called with PHP, the holes member is an array with the correct length, but each hole is empty.

$args = array();
$args["courseId"] = $courseId;
$response = $client->getCourseDetails($args);
$course = $response->return;
//course has all of its primitive members set correctly: good
$holes = $course->holes;
//holes is an array with count = 18: good
$hole = $holes[0];
//hole is an empty stdClass: bad

Printing out the returned XML with $soapClient->__getLastResponse() what looks like the correct representation:

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:getCourseDetailsResponse xmlns:ns="http://webservice.golfstats">
<ns:return xmlns:ax21="http://datastructures.server.golfstats/xsd" xmlns:ax22="http://util.java/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ax24="http://uuid.eaio.com/xsd" xsi:type="ax21:Course">
<ax21:courseLocation>Faketown, VA</ax21:courseLocation>
<ax21:courseName>Fake Links</ax21:courseName>
<ax21:dateAdded>2003-01-02</ax21:dateAdded>
<ax21:holes><ax21:id>1</ax21:id><ax21:number>1</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>2</ax21:id><ax21:number>2</ax21:number><ax21:par>3</ax21:par><ax21:yardage>150</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>3</ax21:id><ax21:number>3</ax21:number><ax21:par>5</ax21:par><ax21:yardage>502</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>4</ax21:id><ax21:number>4</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>5</ax21:id><ax21:number>5</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>6</ax21:id><ax21:number>6</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>7</ax21:id><ax21:number>7</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>8</ax21:id><ax21:number>8</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>9</ax21:id><ax21:number>9</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>10</ax21:id><ax21:number>10</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>11</ax21:id><ax21:number>11</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>12</ax21:id><ax21:number>12</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>13</ax21:id><ax21:number>13</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>14</ax21:id><ax21:number>14</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>15</ax21:id><ax21:number>15</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>16</ax21:id><ax21:number>16</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>17</ax21:id><ax21:number>17</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:holes><ax21:id>18</ax21:id><ax21:number>18</ax21:number><ax21:par>4</ax21:par><ax21:yardage>345</ax21:yardage></ax21:holes>
<ax21:id>1</ax21:id>
<ax21:rating>68.5</ax21:rating>
<ax21:slope>113</ax21:slope>
<ax21:tees>Blue</ax21:tees>
</ns:return>
</ns:getCourseDetailsResponse>
</soapenv:Body>
</soapenv:Envelope>

Why is each hole an empty stdClass? Are there known limitations to the number of levels SoapClient will parse a response?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

绻影浮沉 2024-08-20 13:35:40

我有类似的问题。我经历了你经历的每一次迭代。我偶然通过更改 PHP.INI 文件或 ini_set('soap.wsdl_cache', WSDL_CACHE_NONE); 禁用了缓存“soap.wsdl_cache”,并且在下一个请求时填充了所有丢失的数据。这种情况很容易发生,因为“soap.wsdl_cache_ttl”默认设置为“86400”,即 60 天。

我发现肥皂服务器的代码发生了变化。创建一个新的 wsdl。客户端的缓存 wsdl 此时已过时。您可能会认为,至少每个请求都会发出某种校验和哈希,以验证 wsdl 是否已更改,但事实并非如此。

为了解决此问题并仍然使用缓存,我创建了一个可以在本地使用的 wsdl 文件。

    $cache = Services_Utilities::getCacheResource();
    if (!$cache->test(self::CACHE_KEY)) {
        $data = file_get_contents($wsdl);
        $cache->save($data, self::CACHE_KEY);
        file_put_contents($newWsdl, $data);
        if (file_exists($newWsdl)) {
            $wsdl = $newWsdl;
        }
    } else {
        if (file_exists($newWsdl)) {
            $wsdl = $newWsdl;
        }
    }

    // Remove $newWsdl when necessary
    // unset($newWsdl);

希望这对您或任何碰巧路过并遇到类似问题的人有所帮助。

I had a similar issue. I went through every iteration you went through. On a fluke I disabled caching "soap.wsdl_cache" either by changing the PHP.INI file or ini_set('soap.wsdl_cache', WSDL_CACHE_NONE); and on my next request all the missing data was populated. This can easily happen because the "soap.wsdl_cache_ttl" is set to "86400", by default, which is 60 days.

What I found out was that the soap server had a code change. Creating a new wsdl. The client's cached wsdl was stale at that point. You would think that, at least, a checksum hash of some kind would go out with each request to verify that the wsdl had changed but it does not.

To resolve this issue and still use caching I created a wsdl file that I could consume locally.

    $cache = Services_Utilities::getCacheResource();
    if (!$cache->test(self::CACHE_KEY)) {
        $data = file_get_contents($wsdl);
        $cache->save($data, self::CACHE_KEY);
        file_put_contents($newWsdl, $data);
        if (file_exists($newWsdl)) {
            $wsdl = $newWsdl;
        }
    } else {
        if (file_exists($newWsdl)) {
            $wsdl = $newWsdl;
        }
    }

    // Remove $newWsdl when necessary
    // unset($newWsdl);

Hope this helps you or anyone else that happens to stop by and have a similar problem.

电影里的梦 2024-08-20 13:35:40

您是否通过调试或打印 PHP 对象的内容(print_r、var_dump)来弄清楚这一切?

您是否尝试过打印实际的 SOAP 响应字符串(而不是 PHP 对象)?您可以通过创建设置了调试选项的 SoapClient 来完成此操作:

$soapClient = new SoapClient( "http://your.soap.server.com/services/yourWsdl.wsdl", array("trace" => 1));

然后,当您使用客户端进行 SOAP 调用时,您可以查看请求和响应字符串。

$response = $soapClient->getCourseDetails($params);
$requestAsString = $soapClient->__getLastRequest();
$responseAsString = $soapClient->__getLastResponse();

这可能会帮助您了解 SoapClient 在将响应转换为 PHP 对象时正在做什么。 有关 __getLastResponse() 的详细信息

Did you figure this all out by debugging or printing out the contents of the PHP object (print_r, var_dump)?

Have you tried printing out the actual SOAP response string (not the PHP object)? You can do this by creating the SoapClient with the debug option set:

$soapClient = new SoapClient( "http://your.soap.server.com/services/yourWsdl.wsdl", array("trace" => 1));

Then when you use the client to make your SOAP call, you can take a look at both the request and the response strings.

$response = $soapClient->getCourseDetails($params);
$requestAsString = $soapClient->__getLastRequest();
$responseAsString = $soapClient->__getLastResponse();

This might help you figure out what SoapClient is doing when it's converting the response to a PHP object. More info on __getLastResponse().

若水般的淡然安静女子 2024-08-20 13:35:40

这似乎是 PHP 中的一个错误。 http://bugs.php.net/bug.php?id=49070

不幸的是,错误跟踪器不允许我对此发表评论。

This appears to be a bug in PHP. http://bugs.php.net/bug.php?id=49070

Unfortunately, the bug tracker won't let me comment on it.

阳光的暖冬 2024-08-20 13:35:40

近一年半后,我们开始了……

根据我最近的半类似经历,这不是 php bug。这是一个与 Web 服务的编写方式以及 PHP 如何读取输出相关的问题。我遇到了类似的问题(甚至到 getLastResponse 返回正确的 XML),并发现并不是 PHP 或我的 SOAP 函数有问题,而是“损坏”函数的结果不是明确的定义的光标。

错误的游标定义示例:

PROCEDURE GetBlahByBlahID(IN IN_BLAH_ID VARCHAR, IN IN_BLAHPKG VARCHAR,                                     
OUT result CURSOR
) BEGIN ...

良好的游标定义示例:

PROCEDURE GetBlahByBlahID(IN IN_BLAH_ID VARCHAR, IN IN_BLAHPKG VARCHAR,                                     
OUT result CURSOR (  BLAH VARCHAR(250),
                     BLAH2 VARCHAR(250),
                     BLAH_DATE DATE,
                     BLAH3 VARCHAR(250))) BEGIN ...

显然,Java 可以很好地处理“错误”/非显式输出,但 PHP 返回一个空对象数组。

不确定这是否对您有帮助,但是将 Web 服务功能输出定义为上面的“好”方式解决了我的问题。

Here we go nearly a year and a half later...

In my recent semi-similar experience this was not a php bug. It is an issue related to the way your webservice is written and how PHP reads the output. I was experiencing a similar problem (even down to getLastResponse returning the correct XML) and came to find that it wasn't so much PHP or my SOAP function that had an issue but that the result of the "broken" function was not an explicitly defined cursor.

Example of bad cursor definition:

PROCEDURE GetBlahByBlahID(IN IN_BLAH_ID VARCHAR, IN IN_BLAHPKG VARCHAR,                                     
OUT result CURSOR
) BEGIN ...

Example of good cursor definition:

PROCEDURE GetBlahByBlahID(IN IN_BLAH_ID VARCHAR, IN IN_BLAHPKG VARCHAR,                                     
OUT result CURSOR (  BLAH VARCHAR(250),
                     BLAH2 VARCHAR(250),
                     BLAH_DATE DATE,
                     BLAH3 VARCHAR(250))) BEGIN ...

Apparently Java can handle the "bad"/non explicit output just fine, but PHP returns an array of null objects.

Not sure if this will help you, but defining the web service function output as the "good" way above fixed my problem.

清醇 2024-08-20 13:35:40

为了解决这个问题,您可以获取soap 响应的xml 字符串并将其转换为一个对象。

$soapClient = new \SoapClient($wsdl, ['trace' => 1]); // trace is essential to get the lastResponse string
$soapClient->__call($function_name, $arguments);
$soapResponse = $soapClient->__getLastResponse();
$filteredSoapResponse = str_ireplace("soap:", "", $soapResponse); // remove the "soap:" namespace
$responseObject = simplexml_load_string($filteredSoapResponse);

所有数据都应该位于 $responseObject 中,即使是您以前看不到的数据。

To solve this, you can get the xml-string of the soap response and cast that into an object.

$soapClient = new \SoapClient($wsdl, ['trace' => 1]); // trace is essential to get the lastResponse string
$soapClient->__call($function_name, $arguments);
$soapResponse = $soapClient->__getLastResponse();
$filteredSoapResponse = str_ireplace("soap:", "", $soapResponse); // remove the "soap:" namespace
$responseObject = simplexml_load_string($filteredSoapResponse);

All the data should be in the $responseObject, even the data you couldn't see before.

回首观望 2024-08-20 13:35:40

对我来说,问题是模式定义中缺少字段。

<xsd:import schemaLocation="https://*****.svc?xsd=xsd2" namespace="http://FunctionName"/>

数据传输正确,但 PHP 只显示远程模式中列出的字段。

所以我必须重新创建服务架构。

For me the issue was a missing field in the definition of the schema.

<xsd:import schemaLocation="https://*****.svc?xsd=xsd2" namespace="http://FunctionName"/>

The data was transferred correctly but PHP did only show the fields that were listed in the remote schema.

So i had to recreate the service schema.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文