ZF-5022: Zend_Soap_Autodiscover throws SoapFault VersionMismatch

Description

I have the following class used to set up the zend soap server:

<?php /** * Webservice_Lmpbec * * * * / class Webservice_Lmpbec { / /** * Zend_Db_Adapter * * @var Zend_Db_Adapter_Abstract */ private $_zendDb; /** * Input Data * * @var Webservice_Cldatac */ private $cldatac; /** * Output Data * * @var Webservice_Clbec */ private $clbec; /** * Error de validación * * @var array */ private $error;

/**
 * Constructor for this class
 * 
 * sets up the Db connection
 *
 */
function __construct ()
{
    $config = Zend_Registry::get('config');
    $this->_zendDb = Zend_Db::factory($config->database);
    $this->clbec = new Webservice_Clbec();
}

/**
 * fetchBec
 * 
 * Recieves client info including CID and
 * checks if already registered on the local DB
 * if so return BEC else registers the client and returns his
 * BEC id
 *
 * @param Webservice_Cldatac $input
 * @return Webservice_Clbec
 */
public function fetchBec(Webservice_Cldatac  $input)
{
    $this->setCldatac($input);

    $this->clbec->fecha = date('d/m/Y');
    $this->clbec->hora  = date('H:i:s');
    $this->clbec->referencia = $this->cldatac->referencia;

    if (!$this->authenticate() || !$this->validaData()){
        $this->clbec->respuesta = 'FAILED';
        $this->clbec->cd_respuesta = 1;
        $this->clbec->bec = '';
    } else {
        $this->clbec->respuesta = 'OK';
        $this->clbec->cd_respuesta = 0;
        $this->clbec->bec = '655765';
    }

    return $this->clbec;
}

private function validaData()
{
    $emailValidator = new Zend_Validate_EmailAddress();
    $dateValidator = new Zend_Validate_Date('Y-m-d');      

    $valid = true;

    if($this->cldatac->apaterno == ''){
        $this->clbec->cd_error = 4;
        $this->clbec->nb_error = 'Apaterno es un campo obligatorio.';
        $valid = false;          
    } elseif ($this->cldatac->nombre == '') {
        $this->clbec->cd_error = 4;
        $this->clbec->nb_error = 'El nombre es un campo obligatorio';
        $valid = false;
    } elseif ($this->cldatac->cid == ''){
        $this->clbec->cd_error = 3;
        $this->clbec->nb_error = 'CID es un campo obligatorio';
    } elseif (!$emailValidator->isValid($this->cldatac->email)) {
        $this->clbec->cd_error = 5;
        $this->clbec->nb_error = 'Dirección email inválida';
        $valid = false;
    } elseif (!$dateValidator->isValid($this->cldatac->fnyear.'-'.$this->cldatac->fnmonth.'-'.$this->cldatac->fnday)) {
        $this->clbec->cd_error = 6;
        $this->clbec->nb_error = 'Fecha de nacimiento inválida';
        $valid = false;
    }

    return $valid;
}

/**
 * Webservice authentication
 *
 * @param string $user
 * @param string $pass
 * @return boolean
 */
private function authenticate()
{
    $sql = "SELECT * FROM ws_user WHERE ws_user = ? AND ws_password = SHA1(?)";
    $result = $this->_zendDb->fetchAll($sql,array($this->cldatac->user,$this->cldatac->pass));

    if(count($result) == 1){
        return true;
    } else {
        $this->clbec->nb_error = 'Usuario o contraseña inválidos';
        $this->clbec->cd_error = 1;
        return false;
    }
}

/**
 * Check if client exists in DB
 *
 * @return boolean
 */
private function clientExists()
{
    $sql  = "SELECT * FROM cliente " .
            "WHERE ( cliente_pnombre = ? " .
            "AND cliente_snombre = ? " .
            "AND cliente_apaterno = ? " .
            "AND cliente_amaterno = ? ) " .
            "OR ( cliente_email = ? ) " . 
            "LIMIT 1";
    $result = $this->_zendDb->fetchAll(
        $sql,
        array(
            $this->cldatac->nombre,
            $this->cldatac->snombre,
            $this->cldatac->apaterno,
            $this->cldatac->amaterno,
            $this->cldatac->email,
        )
    );

    if (count($result) == 1){
        return true;
    } else {
        return false;
    }
}

/**
 * getBec
 * 
 * Returns Client BEC from an existent client
 *
 * @return string
 */
private function getBec()
{
    $sql  = "SELECT cliente_bec FROM cliente " .
            "WHERE ( cliente_pnombre = ? " .
            "AND cliente_snombre = ? " .
            "AND cliente_apaterno = ? " .
            "AND cliente_amaterno = ? ) " .
            "OR ( cliente_email = ? ) " . 
            "LIMIT 1";
    $bec = $this->_zendDb->fetchOne(
        $sql,
        array(
            $this->cldatac->nombre,
            $this->cldatac->snombre,
            $this->cldatac->apaterno,
            $this->cldatac->amaterno,
            $this->cldatac->email,
        )
    );

    return $bec;
}

/**
 * @return Webservice_Cldatac
 */
private function getCldatac ()
{
    return $this->cldatac;
}
/**
 * @param Webservice_Cldatac $cldatac
 */
private function setCldatac (Webservice_Cldatac $cldatac)
{
    $this->cldatac = $cldatac;
}
/**
 * @return Webservice_Clbec
 */
private function getClbec ()
{
    return $this->clbec;
}
/**
 * @param Webservice_Clbec $clbec
 */
private function setClbec (Webservice_Clbec $clbec)
{
    $this->clbec = $clbec;
}

}

Which makes use of Webservice_Clbec and Webservice_Cldatac to be translated as complexTypes by Autodiscovery. WSDL seems ok actually pretty much the same as one generated by nusoap server class. Finally, when I instantiate Zend_Soap_Client or SoapClient and call the method fetchBec the following exception is thrown:

Fatal error: Uncaught SoapFault exception: [VersionMismatch] Wrong Version in C:\Users\sivemax01\Zend\workspaces\DefaultWorkspace\library\Zend\Soap\Client.php:887 Stack trace: #0 [internal function]: SoapClient->__call('fetchBec', Array) #1 C:\Users\sivemax01\Zend\workspaces\DefaultWorkspace\library\Zend\Soap\Client.php(887): Zend_Soap_Client_Common->fetchBec(Array) #2 [internal function]: Zend_Soap_Client->__call(Array, Array) #3 C:\Users\sivemax01\Zend\workspaces\DefaultWorkspace\lmp\html\client.php(70): Zend_Soap_Client->fetchBec('fetchBec', Array) #4 {main} thrown in C:\Users\sivemax01\Zend\workspaces\DefaultWorkspace\library\Zend\Soap\Client.php on line 887

I am using xampp 1.6.8 on Windows Vista which comes with php 5.2.6. I tried disabling php_soap.dll but useless, I also tested this code on a centos server using php 5.2.1 but still similar error.

I even tried a simpler class with no db adapters, no complex types:

<?php class myclass {

public function method($input)

{ return $input }

}

Obviously adding phpdoc but still same error, I guess this means something is wrong with my server configuration but I don't know what. Any Ideas?

Erick Martinez

** Update

I´ve been trying to use SoapServer and SoapClient in the same way (Zend_Soap_Server and Zend_Soap_Client rely on them as I understand) with excactly the same error: Wrong Version, so it seem is not a Zend_Framework Issue but rather a php one, still I can't figure out what is causing the problem or if anyone has succeeded implementing the SoapServer, under which configuration.

When executed over CENTOS php 5.2.5 I get some DTD fault (SOAP can't manage DTD or something) , hmm...

Weird thing is I have been working with nusoap and works like a charm, even when I use SoapClient to instanciate some methods. I wish this could be fixed I kind of liked the Zend_Soap API, it seems more modular.

Comment

I see what you say, thing is I generate what it seems to be a correct WSDL then I pass the uri of the WSDL as the first parameter to the Zend_Soap_Server or else I save the generated WSDL (I can even use an IDE to edit it diagramatically) but when I instantiate Zend_Soap_Client and , if I specify the uri of the WSDL the fault message is presented or if I specify the uri of the server (which I don't understand what's the use if you are working id wsdl mode and soapclient constructor takes wsdl uri as first parameter and makes no use of the uri/location parameter) a fault message with "Can't read WSDL file " is presented. Maybe I am missing something...

-- Update 2008/11/23

I fixed this, you were right, I was using this wrong: I had separated files one for WSDL and one for the server so I guess this was the problem (that and a bad SQL...). You are right may be Auto discover should include server, maybe you could use it by default and deactivate it with a construct parameter. Anyway it would be useful to have this information on the official documentation.

Thank you

Comments

I think you are using it wrong.

Zend_Soap_AutoDiscover is NOT a server. It only generates the WSDL File. What you have to do is specifiy the WSDL with AutoDiscover somewhere and then use Zend_Soap_Server and give the WSDL uri as the first parameter. The AutoDiscover has to generate the correct location of the Zend_Soap_Server.

I have written a tutorial that describes the steps to get Soap Server + AutoDiscover to work at server.php and server.php?wsdl repsectivly:

http://www.whitewashing.de/blog/articles/65

Maybe Zend_Soap_AutoDiscover should be integrated with Zend_Soap_Server in a Tiein component, since it somehow happens frequently, that people misunderstand the AutoDiscover component a little bit. Perhaps the manual is unclear on this issue.

I've just found this bug after having the same issue - I'd suggest it was a documentation bug more than anything else.

It would be nice if it was possible to inject a SOAP endpoint URL into the generated WSDL somehow?

this is possible using the setUri function of both autodiscover and wsdl classes.

I a version to come, AutoDiscover and Soap Server should be integrated more tightly but now i just added a note into the manual which will be inserted into 1.7.1 release manual.