PHP SoapClient + Zend_Soap_Autodiscover + VersionMismatch

时间:2013-03-18 11:46:09

标签: php zend-framework soap wsdl

我正在构建一个使用soap的应用程序。

我正在使用Zend_Soap_Autodiscover进行WSDL生成,使用SoapClient和SoapServer来处理请求。

我遇到的问题是客户端不断提供VersionMismatch,即使服务器和客户端都设置为soap 1.1版。

我不知道如何解决这个问题,并且已经坚持了一段时间。所以任何帮助都会受到极大的关注。

我正在尝试在客户端调用更新方法:

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/"
}

My Server的soaphandling正在Zend Framework Application的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() {

    }

}

我希望我已经提供了足够的信息。我希望你能在这件事上给我一些指导。

0 个答案:

没有答案