Thats very strange. Everything looks right.
Well here is some of my code that works to return an array even from a zero argument function call.
This is a function that is in the base class (ServiceBase) for all of my SOAP Server classes.
/**
* Builds a proper SOAP_Value object for sending by the PEAR::SOAP Server.
*
* Takes in an associative array and returns a properly formed
* SOAP_Value object. Walks through each member of the given $value to see
* if it is a simple SOAP type, a user defined type, or an array. Then
* depending on what the variable type is builds the appropriate SOAP_Value
* object. This method is recursive so it handles user defined types that
* have fields of other user defined types.
*
* @param string $name The name of the variable to be created. (i.e.
* the name of an out variable in a SOAP API, or
* the name of a field in a user type).
* @param mixed $value An associative array that maps to a user
* defined variable type or a simple value that
* matches a SOAP predefined variable type.
* @param string $type The type of the SOAP_Value to create.
* @return SOAP_Value
*/
function buildSOAP($name, $value, $type) {
$retVal = NULL;
if (isset($this->__typedef[$type])) {
foreach($value as $subItemName => $subItemValue) {
if ($subItemValue != NULL) {
if (is_array($this->__typedef[$type][0])) {
$arrayItemName = key($this->__typedef[$type][0]);
$arrayItemType =
$this->stripNamespace(
$this->__typedef[$type][0][$arrayItemName]);
$retVal[] = $this->buildSOAP($arrayItemName,
$subItemValue,
$arrayItemType);
} else {
$subItemType = $this->stripNamespace(
$this->__typedef[$type][$subItemName]);
if (!is_numeric($subItemName) &&
isset($this->__typedef[$type][$subItemName])) {
if ($retVal != NULL) {
if (is_array($retVal)) {
$retVal[] = $this->buildSOAP($subItemName,
$subItemValue,
$subItemType);
} else {
$temp = $retVal;
$retVal = array();
$retVal[] = $temp;
$retVal[] = $this->buildSOAP($subItemName,
$subItemValue,
$subItemType);
}
} else {
$retVal = $this->buildSOAP($subItemName,
$subItemValue,
$subItemType);
}
}
}
}
}
if ($retVal != NULL) {
$retVal = new SOAP_Value($name,
'{' . $this->namespace . '}' . $type,
$retVal);
}
} else {
$retVal = new SOAP_Value($name,
$type,
$value);
}
return $retVal;
}
Here is a Server class that works:
require_once('../Library/ServiceBase.php');
require_once('../Library/webservice.php');
class IWSService extends ServiceBase {
function IWSService() {
parent::ServiceBase('IWS', IWS_USERNAME, IWS_PASSWORD);
$this->addMap('GetAvailableServices',
array('in' => array(),
'out' => array('ServiceList' => '{'
. $this->namespace
. '}ServiceList')));
$this->addTypeDef('Service',
array('Name' => 'string',
'URL' => 'string'));
$this->addTypeDef('ServiceList',
array(array('Service' => '{' . $this->namespace . '}Service')));
}
function GetAvailableServices() {
$services = array();
// Protocol
if ($_SERVER['HTTPS'] == 'on') {
$protocol = 'https://';
} else {
$protocol = 'http://';
}
// Port
if ($_SERVER['SERVER_PORT'] != 80) {
$port = ':' . $_SERVER['SERVER_PORT'];
} else {
$port = '';
}
// Default IWS Service
$url = $protocol . $_SERVER['SERVER_ADDR'] . $port
. substr($_SERVER['REQUEST_URI'], 0,
strrpos($_SERVER['REQUEST_URI'], '/') ) . '/';
$services[] = array('Name' => 'IWS',
'URL' => $url);
$dh = opendir('.');
while (($file = readdir($dh)) !== false) {
if ($file != '.' && $file != '..') {
if (is_dir($file)) {
$url = $protocol . $_SERVER['SERVER_ADDR'] . $port
. substr($_SERVER['REQUEST_URI'], 0,
strrpos($_SERVER['REQUEST_URI'], '/') )
. '/' . $file . '/';
if (($client = WebService::ServiceProxy($url . '?wsdl'))
!= false) {
if (is_object($client)) {
$name = $client->Identify();
$services[] = array('Name' => $name,
'URL' => $url);
}
}
}
}
}
return $this->buildSOAP('ServiceList',
$services,
'ServiceList');
}
}
In this setup I buid a SOAP_Value object that I return, this was another issue I found with PEAR::SOAP in that for complex types it didn't always do a good job of converting my types into valid objects that would be interpreted by C#.
Let me know if this helps or not.