ZF-10753: Add SOA design to Zend_Soap AutoDiscovery
Description
SOA design is based on reporting error through SOAP Fault. Of course SOAP Fault is already supported by current SOAP Server and Zend_Soap_Wsdl (even it's still with bug, see linked issue).
But currently it's impossible to use it through AutoDiscovery system. Here is a possible design I implemented successfully:
/**
* @xsd element
* @xsd sequence
*/
class wsGetUserInfosException extends SoapFault
{
/**
* @var int $code
*/
public $code;
/**
* @var string $string
*/
public $message;
/**
* @var object $trace
*/
public $trace;
public function __construct($params=array(0, ""), $faultactor=null, $headerfault=null) {
parent::__construct('Client', 'wsGetUserInfos: Fatal error', $faultactor, $this, __CLASS__, $headerfault);
$this->code=$params[0];
$this->message=$params[1];
$this->trace=arrayToObject($this->getTrace());
}
}
It's directly using improvment of parent task. So it's required dependecy. For now we just declare a new element in the WSDL, we also need to declare our new custom SoapFault:
class wsGetUserInfos
{
/**
* @param string $name
* @return wsGetUserInfosResult
* @xsd fault wsGetUserInfosException
*/
public function getUserInfos($name) {
if ($name=='')
throw new wsGetUserInfosException(array(1, "Parameter name missing !"));
$details=new detailsBrotherList();
$details->addBrotherDetail(new brotherDetail("Jean", 1, 10));
$details->addBrotherDetail(new brotherDetail("Jack", 2, 20));
$details->addBrotherDetail(new brotherDetail("Martin", 3, 30));
return new wsGetUserInfosResult($name, "Dupont", 33, $details);
}
}
And so the modified part of AutoDiscovery to handle the new @xsd fault:
/**
* Add a function to the WSDL document.
*
* @param $function Zend_Server_Reflection_Function_Abstract function to add
* @param $wsdl Zend_Soap_Wsdl WSDL document
* @param $port object wsdl:portType
* @param $binding object wsdl:binding
* @return void
*/
protected function _addFunctionToWsdl($function, $wsdl, $port, $binding)
{
$uri = $this->getUri();
// We only support one prototype: the one with the maximum number of arguments
$prototype = null;
$maxNumArgumentsOfPrototype = -1;
foreach ($function->getPrototypes() as $tmpPrototype) {
$numParams = count($tmpPrototype->getParameters());
if ($numParams > $maxNumArgumentsOfPrototype) {
$maxNumArgumentsOfPrototype = $numParams;
$prototype = $tmpPrototype;
}
}
if ($prototype === null) {
require_once "Zend/Soap/AutoDiscover/Exception.php";
throw new Zend_Soap_AutoDiscover_Exception("No prototypes could be found for the '" . $function->getName() . "' function");
}
// Add the input message (parameters)
$args = array();
if ($this->_bindingStyle['style'] == 'document') {
// Document style: wrap all parameters in a sequence element
$sequence = array();
foreach ($prototype->getParameters() as $param) {
$sequenceElement = array(
'name' => $param->getName(),
'type' => $wsdl->getType($param->getType())
);
if ($param->isOptional()) {
$sequenceElement['nillable'] = 'true';
}
$sequence[] = $sequenceElement;
}
$element = array(
'name' => $function->getName(),
'sequence' => $sequence
);
// Add the wrapper element part, which must be named 'parameters'
$args['parameters'] = array('element' => $wsdl->addElement($element));
} else {
// RPC style: add each parameter as a typed part
foreach ($prototype->getParameters() as $param) {
$args[$param->getName()] = array('type' => $wsdl->getType($param->getType()));
}
}
$wsdl->addMessage($function->getName() . 'In', $args);
$isOneWayMessage = false;
if($prototype->getReturnType() == "void") {
$isOneWayMessage = true;
}
if($isOneWayMessage == false) {
// Add the output message (return value)
$args = array();
if ($this->_bindingStyle['style'] == 'document') {
// Document style: wrap the return value in a sequence element
$sequence = array();
if ($prototype->getReturnType() != "void") {
$sequence[] = array(
'name' => $function->getName() . 'Result',
'type' => $wsdl->getType($prototype->getReturnType())
);
}
$element = array(
'name' => $function->getName() . 'Response',
'sequence' => $sequence
);
// Add the wrapper element part, which must be named 'parameters'
$args['parameters'] = array('element' => $wsdl->addElement($element));
} else if ($prototype->getReturnType() != "void") {
// RPC style: add the return value as a typed part
$args['return'] = array('type' => $wsdl->getType($prototype->getReturnType()));
}
$wsdl->addMessage($function->getName() . 'Out', $args);
}
$isUsingCustomFault = false;
if (preg_match_all('/@xsd\s+fault\s+([^\s]+)/m', $function->getDocComment(), $matches)) {
$classNameCustomFault = $matches[1][0];
if (class_exists($classNameCustomFault)) {
$isUsingCustomFault = true;
$class = new ReflectionClass($classNameCustomFault);
}
}
if ($isUsingCustomFault) {
if (preg_match_all('/@xsd\s+element\s+(.*)/m', $class->getDocComment(), $matches)) {
$classNameTypeStrategy = 'element';
} else if (preg_match_all('/@xsd\s+complexType\s+(.*)/m', $class->getDocComment(), $matches)) {
$classNameTypeStrategy = 'type';
}
}
if ($isUsingCustomFault) {
$wsdl->addMessage($classNameCustomFault, array('fault' => array($classNameTypeStrategy => $wsdl->getType($classNameCustomFault))));
}
// Add the portType operation
if($isOneWayMessage == false) {
$portOperation = $wsdl->addPortOperation($port, $function->getName(), 'tns:' . $function->getName() . 'In', 'tns:' . $function->getName() . 'Out', $isUsingCustomFault ? $wsdl->getType($classNameCustomFault) : false);
} else {
$portOperation = $wsdl->addPortOperation($port, $function->getName(), 'tns:' . $function->getName() . 'In', false, $isUsingCustomFault ? $wsdl->getType($classNameCustomFault) : false);
}
$desc = $function->getDescription();
if (strlen($desc) > 0) {
$wsdl->addDocumentation($portOperation, $desc);
}
// When using the RPC style, make sure the operation style includes a 'namespace' attribute (WS-I Basic Profile 1.1 R2717)
if ($this->_bindingStyle['style'] == 'rpc' && !isset($this->_operationBodyStyle['namespace'])) {
$this->_operationBodyStyle['namespace'] = ''.$uri;
}
// Add the binding operation
$operation = $wsdl->addBindingOperation($binding, $function->getName(), $this->_operationBodyStyle, $this->_operationBodyStyle, $isUsingCustomFault ? array('name' => $classNameCustomFault) : false);
$wsdl->addSoapOperation($operation, $uri . '#' .$function->getName());
// Add the function name to the list
$this->_functions[] = $function->getName();
}
Comments
No comments to display