石鹸を使用するアプリケーションを構築しています。
WSDL の生成には Zend_Soap_Autodiscover を使用し、リクエストの処理には SoapClient と SoapServer を使用しています。
私が抱えている問題は、サーバーとクライアントの両方が SOAP バージョン 1.1 に設定されていても、クライアントが VersionMismatch を返し続けることです。
この問題を解決する方法がわかりません。しばらくの間、立ち往生しています。したがって、どんな助けも大いに感謝します。
クライアントで更新メソッドを呼び出そうとしています:
public function update() {
$this->_soap = Soap_Client_General::getInstance();
try {
$this->_soap->fetchNewVersion();
} catch (Exception $e) {
var_dump($e);
}
}
しかし、これは私に次のダンプを与えています:
object(SoapFault)#12 (10) {
["message":protected]=>
string(13) "Wrong Version"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(59) "/path/app/library/Update/Client.php"
["line":protected]=>
int(27)
["trace":"Exception":private]=>
array(4) {
[0]=>
array(6) {
["file"]=>
string(59) "/path/app/library/Update/Client.php"
["line"]=>
int(27)
["function"]=>
string(6) "__call"
["class"]=>
string(10) "SoapClient"
["type"]=>
string(2) "->"
["args"]=>
array(2) {
[0]=>
string(15) "fetchNewVersion"
[1]=>
array(0) {
}
}
}
[1]=>
array(6) {
["file"]=>
string(59) "/path/app/library/Update/Client.php"
["line"]=>
int(27)
["function"]=>
string(15) "fetchNewVersion"
["class"]=>
string(10) "SoapClient"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[2]=>
array(6) {
["file"]=>
string(55) "/path/app/application.class.php"
["line"]=>
int(25)
["function"]=>
string(6) "update"
["class"]=>
string(13) "Update_Client"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[3]=>
array(6) {
["file"]=>
string(39) "/path/index.php"
["line"]=>
int(24)
["function"]=>
string(2) "go"
["class"]=>
string(11) "Application"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
}
["previous":"Exception":private]=>
NULL
["faultstring"]=>
string(13) "Wrong Version"
["faultcode"]=>
string(15) "VersionMismatch"
["faultcodens"]=>
string(41) "http://schemas.xmlsoap.org/soap/envelope/"
}
サーバーのソープ処理は、Zend Framework アプリケーションの SOAP モジュールで行われています。このモジュールのすべてのコントローラーは、基本コントローラー クラスから拡張されます。
My_Controller_Base_Soap
<?php
class My_Controller_Base_Soap extends My_Controller_Base {
protected $_uri;
public function __construct(\Zend_Controller_Request_Abstract $request, \Zend_Controller_Response_Abstract $response, array $invokeArgs = array()) {
parent::__construct($request, $response, $invokeArgs);
$this->_uri = $this->view->serverUrl() . $this->view->baseUrl('soap/');
//Disable Layout
$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender();
Application_Model_Log::log('Soap Module Called: ' . implode('-', $request->getParams()) . ' from: '. $_SERVER['REMOTE_ADDR'] , 'soap');
}
//Generates WSDL for given class
protected function handleWSDL($classname) {
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setClass($classname);
$autodiscover->handle();
}
//Handles the soap requests for the giving location and classname
protected function handleSOAP($loc, $classname) {
try {
$options = array('uri' => 'urn:' . $classname,
'location' => $loc,
'soap_version' => SOAP_1_1,
'cache_wsdl' => WSDL_CACHE_NONE);
$server = new SoapServer(null, $options);
$server->setClass($classname);
$server->handle();
} catch (Exception $e) {
Application_Model_Log::log('SoapException: ' . $e->getMessage(), 'soap');
}
}
}
上記のクラスは My_Controller_Base から拡張され、さらに My_Controller_Base は Zend_Controller_Action から拡張されます
石鹸/GeneralController
<?php
class Soap_GeneralController extends My_Controller_Base_Soap {
const CLASS_OBJECT = 'Soap_Model_General';
public function indexAction() {
$this->handleSOAP($this->view->baseUrl('soap/general/wsdl'), self::CLASS_OBJECT);
}
public function wsdlAction() {
$this->handleWSDL(self::CLASS_OBJECT);
}
}
Zend_Soap_Autoloader はクラスを使用して、wsdl 生成のベースを以下に基づいています。
Soap_Model_General
<?php
class Soap_Model_General extends Soap_Model_Base_Abstract{
/**
* Retrieve new Version
*
* @return string
*/
public function fetchNewVersion(){
if (!$this->_authenticated)
throw new SOAPFault("Not Authenticated.", 401);
}
}
このクラスは、認証メソッドを含む抽象基本クラスから拡張されます。
Soap_Model_Base_Abstract
<?php
abstract class Soap_Model_Base_Abstract {
/**
* This property represents authentication status
* @var boolean
*/
protected $_authenticated = false;
/**
* The site object
* @var Application_Model_Website
*/
protected $_website;
/**
* Authenticates the SOAP request. (This one is the key to the authentication, it will be called upon the server request)
*
* @param array
* @return array
*/
public function authenticate($login) {
if (!empty($login->siteid) && !empty($login->hash)) {
//Determain if credentials are correct
$found = false;
$website = Application_Model_Website::find($login->siteid);
if ($website instanceof Application_Model_Site) {
//Is the authentication hash is correct? (siteid, secretkey and remote ip correct?)
$hash = md5($website->id . $website->secretkey . $website->ip);
if ($hash == $login->hash) {
$found = true;
}
}
//Verify the send IP
if ($found && $website->ip == $_SERVER['REMOTE_ADDR']) {
//Accept authentication
$this->_authenticated = true;
//set Application_Model_Site to site property
$this->_website = $website;
return true;
} else {
throw new SOAPFault("Incorrect credentials.", 401);
}
} else {
throw new SOAPFault("Invalid authentication format. Values may not be empty and are case-sensitive.", 401);
}
}
}
基本的に、WSDL にアプローチすると、完全に生成された WSDL が得られます。
次に、クライアントをサーバーに接続します。
クライアントのコードは次のとおりです。
Soap_Client_General
<?php
class Soap_Client_General extends Soap_Client_Abstract {
private static $instance = NULL;
public static function getInstance() {
//get config
$config = Zend_Registry::get('config');
$wsdl = $config->general->server->general . '/wsdl/';
if (!self::$instance) {
return parent::getInstance($wsdl);
}
return self::$instance;
}
}
Soap_Client_Abstract
<?php
abstract class Soap_Client_Abstract {
private function __construct() {
}
/**
* Return client instance or create intitial
*
* @return SoapClient
* @access public
*/
public static function getInstance($wsdl) {
$ns = "auth";
$config = Zend_Registry::get('config');
$siteid = $config->general->siteid;
$secretkey = $config->general->secretkey;
$hash = md5($siteid . $secretkey . $_SERVER['SERVER_ADDR']);
//Create our Auth Object to pass to the SOAP service with our values
$auth->siteid = $siteid;
$auth->hash = $hash;
$auth_vals = new SoapVar($auth, SOAP_ENC_OBJECT);
//The 2nd variable, 'authenticate' is a method that exists inside of the SOAP service
$authenticate = new SoapHeader($ns, 'authenticate', $auth_vals, false);
$client = new SoapClient($wsdl, array('cache_wsdl' => WSDL_CACHE_NONE, 'soap_version' => SOAP_1_1));
$client->__setSoapHeaders(array($authenticate));
return $client;
}
private function __clone() {
}
}
十分な情報を提供できたことを願っています。そして、この件について何か方向性を教えていただければ幸いです。