class XMLCurler { private $username = '[redacted]'; private $password = '[redacted]'; private $url = 'https://[redacted].xplan.iress.com.au/RPC2/'; public $ch; // the curl handle public $token; public $results; public function __construct() { if ($this->connect()) { if ($this->login()) { echo "<div class=\"success\">Successful Connection & Login. Token: {$this->token}</div>"; } } } public function __destruct() { if ($this->ch) { $this->disconnect(); } } public function connect() { if (!$this->ch = curl_init($this->url)) { // generate curl handle echo "<div class=\"error\">CURL Error While Connecting (check url)"; return false; } return true; } public function disconnect() { curl_close($this->ch); } public function processResponse($response) { if (!$response) { echo "<div class=\"error\">CURL Error While Attempting to Login - No XML token string<br><b>",curl_error($this->ch),"</b></div>"; return false; } $decoded = xmlrpc_decode($response); if (is_array($decoded) && xmlrpc_is_fault($decoded)) { echo "<div class=\"error\">Error Response: {$decoded['faultString']} ({$decoded['faultCode']})</div>"; return false; } return $decoded; } public function login() { $postfields = xmlrpc_encode_request('edai.Login',array($this->username,$this->password)); // package as xml curl_setopt($this->ch,CURLOPT_HTTPHEADER,array('Content-Type: text/xml')); curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,true); curl_setopt($this->ch,CURLOPT_POSTFIELDS,$postfields); curl_setopt($this->ch,CURLOPT_SSL_VERIFYHOST,0); // not advised,I need to find out how to avoid this curl_setopt($this->ch,CURLOPT_SSL_VERIFYPEER,I need to find out how to avoid this if (!$token = $this->processResponse(curl_exec($this->ch))) { return false; } if (!preg_match("~^[\w+]{20}$~",$token)) { echo "<div class=\"error\">Invalid/Unexpected Token Generated<br><b>$token</b>"; return false; } $this->token = $token; // cache the valid token return true; } public function listChildren($path) { $method = "edai.ListChildren"; $request = xmlrpc_encode_request($method,array($this->token,$path)); echo "<div class=\"notice\">XMLRPC Encoded Request (for $method): <pre>",htmlentities($request),"</pre></div>"; curl_setopt($this->ch,$request); if (!$results = $this->processResponse(curl_exec($this->ch))) { return false; } $this->results = $results; // cache the valid results return true; } public function search($basepath,$queryxml) { $method = "edai.Search"; /** Desperate/Manual xml construction ... * $xml = new DOMDocument("1.0","utf-8"); * $xml->appendChild($methodCall = $xml->createElement("methodCall")); * $methodCall->appendChild($methodName = $xml->createElement("methodName")); * $methodCall->appendChild($params = $xml->createElement("params")); * $params->appendChild($param1 = $xml->createElement("param")); * $param1->appendChild($value1 = $xml->createElement("value")); * $value1->appendChild($string1 = $xml->createElement("string")); * $params->appendChild($param2 = $xml->createElement("param")); * $param2->appendChild($value2 = $xml->createElement("value")); * $value2->appendChild($string2 = $xml->createElement("string")); * $params->appendChild($param3 = $xml->createElement("param")); * $param3->appendChild($value3 = $xml->createElement("value")); * $value3->appendChild($string3 = $xml->createElement("string")); * $string3->appendChild($EntitySearch = $xml->createElement("EntitySearch")); * $EntitySearch->appendChild($SearchResult1 = $xml->createElement("SearchResult")); * $SearchResult1->setAttribute("field","first_name"); * $EntitySearch->appendChild($SearchResult2 = $xml->createElement("SearchResult")); * $SearchResult2->setAttribute('field',"last_name"); * $EntitySearch->appendChild($SearchQuick = $xml->createElement("SearchQuick")); * $SearchQuick->appendChild($s = $xml->createElement("s")); * $xpath = new DOMXPath($xml); * $result1 = $xpath->query("//methodName"); * $result1->item(0)->nodeValue = $method; * $result2 = $xpath->query("//params/param[1]/value/string"); * $result2->item(0)->nodeValue = $this->token; * $result3 = $xpath->query("//params/param[2]/value/string"); * $result3->item(0)->nodeValue = "entitymgr/client"; * $result4 = $xpath->query("//SearchQuick/s"); * $result4->item(0)->nodeValue = "last_name:Smith"; * $xml->formatOutput = true; * $request = $xml->saveXML(); */ /** Desperately attempted passing array ... * $queryarray = array( * "EntitySearch" => array( * array( * "SearchResult" => array( * "@attr" => array( * "field" => "first_name" * ) * ) * ),* array( * "SearchResult" => array( * "@attr" => array( * "field" => "last_name" * ) * ) * ),* array( * "SearchQuick" => array( * "s" => "last_name:Smith" * ) * ) * ) * ); */ $request = xmlrpc_encode_request($method,$basepath,$queryxml)); // this mutates the nested $queryxml string // Causes: //Error Response: UNKNOWN(CORBA.UNKNOWN(omniORB.UNKNOWN_PythonException,CORBA.COMPLETED_MAYBE)) (-32505) //$request = html_entity_decode($request); // repair encoded entities //$request = preg_replace('~(?:>\K\s+)|(?:\s+(?=<))~','',$request); // strip every whitespace character between tags (hack) // Causes: // Error Response: ExpatError(Syntax error: line 1,column 0 (byte 0)) (-32505) echo "<div class=\"notice\">XMLRPC Encoded Request (for $method): <pre>",$request); if (!$results = $this->processResponse(curl_exec($this->ch))) { return false; } $this->results = $results; // cache the valid results return true; } }
以下是我打电话的方式. edai.ListChildren起作用,因为我不必发送任何XML数据. edai.Search不起作用,因为我无法在XML请求中正确准备XML查询.
$XC = new XMLCurler(); /* edai.ListChildren works as desired/expected */ $path = "/entitymgr/client"; if ($XC->listChildren($path)) { echo "<div>List of Children Successful.<pre>"; var_export($XC->results); echo "</pre></div>"; } /* edai.Search does not work */ $basepath = "entitymgr/client"; $queryxml = <<<XML <EntitySearch> <SearchResult field="first_name"/> <SearchResult field="last_name"/> <SearchQuick><s>last_name:Smith</s></SearchQuick> </EntitySearch> XML; if ($XC->search($basepath,$queryxml)) { echo "<div>Search Successful.<pre>"; var_export($XC->results); echo "</pre></div>"; }
这是the attempted request and error message.
我几周前联系了iress.com,他们叫我松散地确认我有权访问API,并告诉我他们已经联系了 – 跟进电话没有发生我会喜欢回到这个项目的工作.
我确实知道史密斯的姓氏与我的查询相符.
我没有Python经验,所以错误响应对我没有帮助.我做了比我发布的更多的冰雹尝试,但我厌倦了浪费我的时间.我不知道第三个参数是否要嵌套在< value>,< param>,< struct>,< string>,< array>,< xml>或其他内容中完全.
如果有人对如何为请求准备XML查询有任何建议,我将运行它们并提供反馈.
我也很高兴收到关于类设计,安全性问题以及完全不同的PHP方法的建议,以便让edai.Search返回一些有用的数据.
根据@ThW的要求,这里是xml尝试的集合及其各自的错误响应:https://pastebin.com/dYtwXWxz
也许xmlrpc_encode_request调用可以使用命名参数:
$params = [ 'context' => $this->token,'basepath' => $basepath,// try with a leading slash,too,in spite of the docs listing it w/o one 'queryxml' => $queryxml,]; $request = xmlrpc_encode_request($method,$params); // this mutates the nested $queryxml string
如果这不起作用,请停止使用代码并安装SoapUI或Postman或Insomnia或类似代码,并手动构建您的请求.
我怀疑你会在半小时内收到一份工作请求,并且可以向后工作以调试你的代码/重写你的代码.如果我可以访问API,我会为你做的.
要检查的事项:
>编码是否有所作为(应该是utf8而不是)?>需要将XML查询视为字符串,因此请确保在GUI客户端发出请求时将其编码/包装在CDATA标记中.根据您选择的客户端,可能会为您完成,只需确保完成