Yes, it's possible.
What have you tried so far? Are you using a soap library or did you roll your own? Have you experienced any problems getting file transfer to work? If so what did you try to do and what happened when you tried to do it?
edit - Inspecting the HTTP traffic can be a useful way to debug SOAP servers. Also if the client you are using is flat-out incompatible with a server, you may have to find another client or "roll your own." I wrote a very simple client which works well with the SOAP servers generated by MS frameworks, it may work well with Java servers too.
If you do end up needing to create your own client, this may help get you started:
SimpleSoapClient.php
Extend this with your own class. See below...
<?php
/**
* Simple Soap Client
*
* Override this class to create a SOAP client.
*
* </pre>
**/
class SimpleSoapClient
{
protected $host;
protected $port;
protected $ns;
protected $url;
protected $act;
protected $debug;
protected function Post($method, $params)
{
return $this->_Post($method, $params);
}
protected function _Post($method, $params)
{
$namespaces = array();
foreach($params as $p)
{
if (isset($p->ns))
{
if ($namespaces[$p->ns])
$p->prefix = $namespaces[$p->ns];
else
$p->prefix = $namespaces[$p->ns] = 'ns'.count($namespaces);
}
}
if ($this->debug)
{
$cn = get_class($this);
echo "\n ====== Calling $cn::$method ====== \n\nParams: ";
print_r($params);
}
$host = $this->host;
$port = $this->port;
$ns = $this->ns;
$url = $this->url;
$act = $this->act;
$fp = fsockopen($host, $port, $errno, $errstr, 30);
if (!$fp)
die ("Oops: $errstr ($errno)<br />\n");
$xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"";
foreach($namespaces as $k=>$v)
$xml .= " xmlns:$v=\"$k\"";
$xml .= "><s:Body><$method xmlns=\"$ns\">";
foreach($params as $k=>$v)
$xml .= "<$k>$v</$k>";
$xml .= "</$method></s:Body></s:Envelope>";
$head = "POST $url HTTP/1.1\r\n"
. "Host: $host\r\n"
. "Content-Type: text/xml; charset=utf-8\r\n"
. "Content-Length: ".strlen($xml)."\r\n"
. "SOAPAction: \"$act$method\"\r\n"
. "Connection: Close\r\n\r\n";
if ($this->debug)
echo "\nRequest:\n\n$head$xml\n\n";
$s;
fwrite($fp, $head.$xml);
while (!feof($fp))
$s .= fgets($fp);
fclose($fp);
$s = trim(substr($s,strpos($s, "\r\n\r\n")));
if ($this->debug)
echo "Response:\n\n$s\n\n";
if (strstr($s,'<error_message>'))
die("\nError communicating with SOAP server.\n");
return($this->xml2assoc($s));
}
private function xml2assoc($xmlstring)
{
$xml;
if (is_object($xmlstring))
$xml = $xmlstring;
else
{
$xml = new XMLReader();
$xml->xml($xmlstring);
}
$tree = null;
while($xml->read())
{
switch ($xml->nodeType)
{
case XMLReader::END_ELEMENT: return $tree;
case XMLReader::ELEMENT:
$node = array('tag' => $xml->name,
'value' => $xml->isEmptyElement ? '' : $this->xml2assoc($xml));
if($xml->hasAttributes)
while($xml->moveToNextAttribute())
$node['attributes'][$xml->name] = $xml->value;
$tree[] = $node;
break;
case XMLReader::TEXT:
case XMLReader::CDATA:
$tree .= $xml->value;
}
}
// if ($this->debug) { echo "\nTREE:\n"; print_r($tree); }
return $tree;
}
public function DateFormat($date=null)
{
if (is_string($date))
$date = new DateTime($date);
return implode('-',array_slice(split('-',$date ? $date->format('c') : date('c')), 0, 3));
}
}
class SimpleSoapType
{
public $prefix;
public $type;
public $value;
public $ns;
function __construct($value)
{
$this->value = $value;
}
function __toString()
{
$t = (isset($this->prefix) ? $this->prefix.':' : '').$this->type;
$st = "<$t>"; $et = "</$t>";
if (is_array($this->value))
foreach ($this->value as $v)
$r .= $st.$v.$et;
else
$r = $st.$this->value.$et;
return $r;
}
protected function init() { throw('init is abstract'); }
}
?>
ExampleSoapClient.php
This is actually a production[1] soap client renamed to 'Example'.
<?php
require_once 'SimpleSoapClient.php';
/**
* Example Soap Client
**/
class ExampleSoapClient extends SimpleSoapClient
{
function __construct()
{
$this->host = 'connect.example.com';
$this->port = 80;
$this->ns = "https://{$this->host}/connect";
$this->url = "http://{$this->host}/svc/connect.svc";
$this->act = "{$this->ns}/IConnect/";
$this->debug = true;
}
protected function Post ($method, $params) {
$params['apiKey'] = 'abcdef1234567890';
return $this->_Post($method, $params);
}
private function returnMulti($d)
{
foreach($d[0]['value'][0]['value'][0]['value'][0]['value'] as $v)
$r[] = $v['value'];
return $r;
}
private function returnSingle($d)
{
$r = $d[0]['value'][0]['value'][0]['value'][0]['value'];
return $r;
}
private function returnMultiPairs($d)
{
$d = $this->returnMulti($d);
foreach ($d as $v)
$r[$v[0]['value']] = $v[1]['value'];
return $r;
}
/**
* Get Property Categories
*
*
**/
public function GetPropertyCategories()
{
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $this->returnMulti($d);
}
/**
* Get Property IDs (undocumented)
*
* @param dateTime $lastMod Last modified date
*
**/
public function GetPropertyIDs($lastMod)
{
$lastMod = $this->DateFormat($lastMod);
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $this->returnMulti($d);
}
/**
* Get Property (undocumented)
*
* @param string $propertyID Property ID
*
**/
public function GetProperty($propertyID)
{
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $this->returnSingle($d);
}
/**
* Get Property IDs by Category
*
* @param int $propertyCategory Property category to get IDs for
*
**/
public function GetPropertyIDsByCategory($propertyCategory)
{
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $this->returnMulti($d);
}
/**
* Get Rates
*
* @param int $propertyID Property ID to get rates for
* @param string $rateType Currently unused
* @param int $los Length of stay - 1 (daily), 7 (weekly), or 30 (monthly)
* @param string $startDate Beginning of period to retrieve data for
* @param string $endDate End of period to retrieve data for
* @param string $currency Currently 'USD' only
*
**/
public function GetRates($propertyID, $rateType, $los, $startDate, $endDate, $currency)
{
$startDate = $this->DateFormat($startDate);
$endDate = $this->DateFormat($endDate);
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $this->returnMultiPairs($d);
}
/**
* Get Availability
*
* @param int $propertyID Property ID to get availability for
* @param string $rateType Currently unused
* @param string $startDate Beginning of period to retrieve data for
* @param string $endDate End of period to retrieve data for
*
**/
public function GetAvailability($propertyID, $rateType, $startDate, $endDate)
{
$startDate = $this->DateFormat($startDate);
$endDate = $this->DateFormat($endDate);
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $this->returnMultiPairs($d);
}
/**
* Set Rates
*
* @param int $propertyID Property ID to set rates for
* @param string $rateType Currently unused
* @param int $los Length of stay - 1 (daily), 7 (weekly), or 30 (monthly)
* @param array $effDates Effective dates
* @param array $rates Rate for each date
* @param string $currency Currently 'USD' only
*
**/
public function SetRates($propertyID, $rateType, $los, $effDates, $rates, $currency)
{
if (!get_class($effDates) == 'msDateTime')
$effDates = new msDateTime($effDates);
if (!get_class($rates) == 'msDecimal')
$rates = new msDecimal($rates);
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $d;
}
/**
* Set Availability
*
* @param int $propertyID Property ID to set availability for
* @param array $effDates Effective dates
* @param array $numAvailabile Available units for each date [sic]
*
**/
public function SetAvailability($propertyID, $effDates, $numAvailabile) // notice spelling: numAvailabile
{
if (!get_class($effDates) == 'msDateTime')
$effDates = new msDateTime($effDates);
if (!get_class($numAvailabile) == 'msInt')
$numAvailabile = new msInt($numAvailabile);
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $d;
}
/**
* Set Rates and Availability
*
* @param int $propertyID Property ID to set rates and availability for
* @param string $rateType Currently unused
* @param int $los Length of stay - 1 (daily), 7 (weekly), or 30 (monthly)
* @param array $effDates Effective dates
* @param array $rates Rate for each date
* @param string $currency Currently 'USD' only
* @param array $numAvailabile Available units for each date [sic]
*
**/
public function SetRatesAndAvailability($propertyID, $rateType, $los, $effDates, $rates, $currency, $numAvailabile)
{
if (!get_class($effDates) == 'msDateTime')
$effDates = new msDateTime($effDates);
if (!get_class($rates) == 'msDecimal')
$rates = new msDecimal($rates);
if (!get_class($numAvailabile) == 'msInt')
$numAvailabile = new msInt($numAvailabile);
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $d;
}
/**
* Get Booking
*
* @param int $bookingID ID of Booking to retrieve
*
**/
public function GetBooking($bookingID)
{
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $this->returnSingle($d);
}
/**
* Make Booking
*
* @param bcBooking $booking Booking object
* @param bool $infoOnly If true, simulate booking without actually booking anything
*
**/
public function MakeBooking($booking, $infoOnly)
{
$d = $this->Post(__FUNCTION__, get_defined_vars());
return $d; // $this->returnMulti($d);
}
}
/**
* base soap type - MS array serialization
**/
class msSoapType extends SimpleSoapType
{
function __construct($value)
{
$this->ns = 'http://schemas.microsoft.com/2003/10/Serialization/Arrays';
parent::__construct($value);
}
}
/**
* dateTime soap type - MS array serialization
**/
class msDateTime extends msSoapType
{
function __construct($value)
{
$this->type = 'dateTime';
parent::__construct($value);
if (is_array($value))
foreach ($value as $k=>$v)
$this->value[$k] = SimpleSoapClient::DateFormat($v);
else
$this->value = SimpleSoapClient::DateFormat($value);
}
}
/**
* decimal soap type - MS array serialization
**/
class msDecimal extends msSoapType
{
function __construct($value)
{
$this->type = 'decimal';
parent::__construct($value);
}
}
/**
* int soap type - MS array serialization
**/
class msInt extends msSoapType
{
function __construct($value)
{
$this->type = 'int';
parent::__construct($value);
}
}
?>
[1] - May not look production quality, but I'm using this and others like it on some cron jobs and PHP sites and it's working well :)