There have been a few use cases recently within InterSystems where we've needed to connect to Caché-based web services from PHP. The first of these was actually the Developer Community itself, which uses web services as part of Single Sign-On with other InterSystems sites/applications. The following example demonstrates how to connect to a Caché-based web service (particularly, the web service in the SAMPLES namespace) from PHP, using password authentication.
(Note: This assumes password authentication is enabled for /csp/samples.)
<?php
// Standard SOAP header for username/password
// From http://stackoverflow.com/questions/13465168/php-namespaces-in-soapheader-child-nodes
class WSSESecurityHeader extends SoapHeader {
public function __construct($username, $password)
{
$wsseNamespace = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
$security = new SoapVar(
array(new SoapVar(
array(
new SoapVar($username, XSD_STRING, null, null, 'Username', $wsseNamespace),
new SoapVar($password, XSD_STRING, null, null, 'Password', $wsseNamespace)
),
SOAP_ENC_OBJECT,
null,
null,
'UsernameToken',
$wsseNamespace
)),
SOAP_ENC_OBJECT
);
parent::SoapHeader($wsseNamespace, 'Security', $security, false);
}
}
// Key parameters for connecting to the web service:
$location = "http://localhost:57772/csp/samples/SOAP.Demo.cls";
$uri = "http://tempuri.org";
$username = "_SYSTEM";
$password = "SYS";
// If the soap client is to be constructed from the WSDL, or if the WSDL is to be used to generate
// PHP classes (using various tools that exist for that purpose), ?WSDL=1 and the username/password
// need to be tacked on to the end of the URL:
$wsdl = $location . "?WSDL=1&CacheUserName=" . $username . "&CachePassword=" . $password;
// Ideally, we'd then just be able to do something like:
// $client = new SoapClient($wsdl, array("trace"=>1));
// In many cases, that will be sufficient.
// However, the WSDL for SOAP.Demo in the SAMPLES namespace references additional schemas
// defined in other classes - for example:
// <s:import namespace="http://tempuri.org/QueryByName_DataSet" schemaLocation="http://localhost:57772/csp/samples/SOAP.Demo.QueryByName.DS.cls?XSD"/>
// There doesn't seem to be any way to make PHP's WSDL consumption use the CSP session cookie or continue
// to pass along the credentials in the URL when retrieving these, so it fails. The workaround is to create
// the SoapClient without the WSDL, using location/uri instead.
// Create SoapClient Object
$client = new SoapClient(null, array(
"trace" => 1,
"location" => $location,
"uri" => $uri,
"style" => SOAP_DOCUMENT,
"use" => SOAP_LITERAL));
// Add security header.
$client->__setSoapHeaders(new WSSESecurityHeader($username, $password));
// Get a person by ID - we'll let this come from the URL but default to ID 1 so something's always shown.
$id = 1;
if (isset($_GET["id"])) {
$id = $_GET["id"];
}
// Object with parameter names and values for the SOAP call
$request = new stdClass();
$request->id = $id;
// This goes in an associative array with key FindPersonSoapIn (see WSDL)
$params = array();
$params['FindPersonSoapIn']=$request;
// In the array of options for the soap call, soapaction must be specified since there's no WSDL to refer to.
// PHP provides a default value of <uri>#<method>, but that's incorrect.
$options = array('soapaction'=>$uri.'/SOAP.Demo.FindPerson');
// Actually call the web service
$result = $client->__soapCall("FindPerson",$params,$options);
// Just dump out the response to the page.
var_dump($result);
?>