I am attempting to create this output in PHP:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://webservices.company.co.uk/AddressMatching" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://webservices.company.co.uk/ServiceBase/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns1:Body>
<ns0:GetAvailableAddresses>
<ns0:request>
<ns2:UserCredentials>
<ns2:AgentID>123</ns2:AgentID>
<ns2:Password>PASSword</ns2:Password>
<ns2:Username>user#company.com</ns2:Username>
</ns2:UserCredentials>
<ns0:Address>
<ns0:PostCode>NG42DJ</ns0:PostCode>
</ns0:Address>
</ns0:request>
</ns0:GetAvailableAddresses>
</ns1:Body>
</SOAP-ENV:Envelope>
This is fairly easy in Python, the class I'm using (suds) simply reads the wsdl file, and uses the namespaces correctly:
from suds.client import Client
client = Client('/the/path/to/AddressMatchingService.wsdl')
creds = { 'AgentID': '123', 'Username': 'user#company.com', 'Password': 'PASSword' }
address = client.factory.create('Address')
address.PostCode = "NG42DJ"
address_request = client.factory.create('AvailableAddressesRequest')
address_request.UserCredentials = creds
address_request.Address = address
request = client.service.GetAvailableAddresses(address_request)
print request
There is no need to reference anything to do with namespaces, it simply works by reading the wsdl file and figuring it out. As you can see in the original XML above, the variables have namespaces and inherit where required, also note that the Body is in the ns1 namespace.
The closest I have got in PHP, is using a WSDL-to-PHP converter which generates a ton of classes based on functions within the file, but it seems to lose all sense of namespacing by doing this. The only way of working with it I can see so far is to modify the generated class files like this:
// some code omitted...
use SoapVar; // added by me
// this is declared in AddressMatchingService namespace
class Credentials
{
public $AgentID = null;
public $Username = null;
public $Password = null;
public function __construct($AgentID, $Username, $Password)
{
//$this->AgentID = $AgentID;
$this->AgentID = new SoapVar($AgentID, null, null, null, null, "http://webservices.company.co.uk/ServiceBase/");
//$this->Username = $Username;
$this->Username = new SoapVar($Username, null, null, null, null, "http://webservices.company.co.uk/ServiceBase/");
//$this->Password = $Password;
$this->Password = new SoapVar($Password, null, null, null, null, "http://webservices.company.co.uk/ServiceBase/");
}
}
$wsdl = base_path() . '/resources/wsdl/AddressMatchingService.wsdl';
$soap = new SoapClient($wsdl, array('trace'=>1));
$creds = new AddressMatchingService\Credentials('123', 'user#company.com','PASSword');
$address = new AddressMatchingService\Address('NG42DJ');
$request = array('request' => $request);
$request = new SoapVar($request, SOAP_ENC_OBJECT);
$response = $soap->__soapCall("GetAvailableAddresses", array('request' => $request));
Which gets me close-ish:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://webservices.company.co.uk/ServiceBase/" xmlns:ns2="http://webservices.company.co.uk/AddressMatching">
<SOAP-ENV:Body>
<ns2:GetAvailableAddresses>
<request>
<Address>
<PostCode>NG42DJ</PostCode>
</Address>
<ns1:UserCredentials>
<ns1:AgentID>123</ns1:AgentID>
<ns1:Password>PASSword</ns1:Password>
<ns1:Username>user#company.co.uk</ns1:Username>
</ns1:UserCredentials>
<UPRN/>
</request>
</ns2:GetAvailableAddresses>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
But if it's so easy in Python, I thought I might be doing something wrong. Specifying the namespace in each class seems a pain, and I still don't know if it's possible to change the namespace of the Body from 'SOAP-ENV' to ns1. I would like to avoid handcrafting XML if I could avoid it, as there's so many functions and variables in the WSDL, I'd ideally like to make it work as efficiently as the Python suds library does!
SOAP-ENV or ns1 are not the namespaces, but aliases for them. The actual namespaces are the values in the xmlns:* attributes. In your first example, they resolve both to the same namespace.
SOAP-ENV:Envelope -> {http://schemas.xmlsoap.org/soap/envelope/}Envelope
SOAP-ENV:Header -> {http://schemas.xmlsoap.org/soap/envelope/}Header
ns1:Body -> {http://schemas.xmlsoap.org/soap/envelope/}Body
The alias does not change the meaning. Having different aliases for the same namespace could be considered bad, because it lowers the readability.
Second, just because the default API only has some calls with lots of specific arguments does not mean that you have to use them directly. Think about using helper methods or loops to avoid repeating yourself:
class Credentials
{
private namespaceUri = "http://webservices.company.co.uk/ServiceBase/";
public $AgentID = null;
public $Username = null;
public $Password = null;
public function __construct($AgentID, $Username, $Password)
{
$this->AgentID = $this->createSoapValue($AgentID);
$this->Username = $this->createSoapValue($Username);
$this->Password = $this->createSoapValue($Password);
}
private function createSoapValue($value) {
return new new SoapVar($value, null, null, null, null, $this->namespaceUri);
}
}
Last check the request element. I think you forgot the namespace for it.
Related
I am trying to get request with this structure:
<SOAP-ENV:Body>
<ns1:getCreditReportTypes>
<reportTypeRequest>
<reportParams xsi:type="ns1:personCreditReportParams">
<personId>4</personId>
<consentConfirmed>true</consentConfirmed>
</reportParams>
</reportTypeRequest>
</ns1:getCreditReportTypes>
</SOAP-ENV:Body>
Here is my php-code:
$obj = new \stdClass();
$obj->personId = 4;
$obj->consentConfirmed = true;
$data = new \SoapVar($obj, SOAP_ENC_OBJECT, "personCreditReportParams", $namespace, "reportParams");
$res = $this->client->getCreditReportTypes(new \SoapParam($data,"reportTypeRequest"));
However, php generates invalid xml:
<SOAP-ENV:Body>
<ns1:getCreditReportTypes xsi:type="ns1:personCreditReportParams">
<consentConfirmed>true</consentConfirmed>
<personId>4</personId>
</ns1:getCreditReportTypes>
</SOAP-ENV:Body>
How can I make a valid XML with object-way?
You should definitively use a WSDL to php generator such as PackageGenerator.
It'll ease you the request construction, the response handling.
For those who'll get the same problem.
My solution is to use nusoap (https://github.com/yaim/nusoap-php7). This library allows you to make complicated requests, including SWA (SOAP with Attachments).
Here is working code for my question:
$person = array("personId"=>$id, "consentConfirmed"=>$confirmed);
$data = array(
"reportParams"=>new soapval("reportParams", "personCreditReportParams", $person, false, $namespace)
);
$result = $client->call("getCreditReportTypes", $data, $namespace);
P.S. I've tried some generators and no one could make correct request, although classes were generated correctly.
I am looking for a way to change the namespace: "ns1" into "ret", tested the below XML using SoapUI with the namespace set to "ret" and the request was successful. I've "googled", and searched the answers from other related questions here in SO, but no luck. So, I am kind of desperate to find the answer...
Here is the XML that is generated to be sent to request:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://retailexpress.com.au/">
<SOAP-ENV:Header>
<ns1:ClientHeader>
<ns1:ClientID>Random-hash-clientID</ns1:ClientID>
<ns1:UserName>Username</ns1:UserName>
<ns1:Password>Password</ns1:Password>
</ns1:ClientHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:CustomerGetBulkDetails>
<ns1:LastUpdated>2000-01-01T00:00:00.000Z</ns1:LastUpdated>
<ns1:OnlyCustomersWithEmails>1</ns1:OnlyCustomersWithEmails>
</ns1:CustomerGetBulkDetails>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Though, it seems a bit odd that we have to request with the same namespace (ret), but that's how it is...
Here is the PHP code used to generate the above:
$rexHost = '<domain of retail express>';
$rexApi = '<URI of retail express API / wsdl path>';
$rexUser = 'Username';
$rexPassword = 'Password';
$rexApiClient = 'Random-hash-clientID';
$rexApiHost = 'http://retailexpress.com.au/';
$client = new SoapClient($rexHost.$rexApi, array('trace' => true));
$auth = new stdClass();
$auth->ClientID = $rexApiClient;
$auth->UserName = $rexUser;
$auth->Password = $rexPassword;
$header = new SoapHeader($rexApiHost, 'ClientHeader', $auth, false);
$client->__setSoapHeaders($header);
$lastUpdate = '2000-01-01T00:00:00.000Z'; //hardcoded for test
$params = array();
$params[] = new SoapVar($lastUpdate, XSD_DATETIME, null, null, 'LastUpdated', $rexApiHost);
$params[] = new SoapVar(1, XSD_INTEGER, null, null, 'OnlyCustomersWithEmails', $rexApiHost);
try {
$users = null;
return $users = $client->CustomerGetBulkDetails( new SoapVar($params, SOAP_ENC_OBJECT));
} catch (Exception $e) {
Log::info($e->getMessage());
Log::info($client->__getLastRequest()); //laravel logger, where I got the generated SOAP XML request
return false;
}
ns1 is not the namespace but an alias for it. http://retailexpress.com.au/ is the namespace. The namespace definition xmlns:ns1="http://retailexpress.com.au/" defines an alias for the current element and its descendants. A namespace has to be unique and stable. Using a definition and aliases allows for complex URI used as an namespace and short, readable aliases for the serialization.
The following three examples are all resolved to and element CustomerGetBulkDetails in the namespace http://retailexpress.com.au/ by an XML parser:
<ns1:CustomerGetBulkDetails xmlns:ns1="http://retailexpress.com.au/"/>
<ret:CustomerGetBulkDetails xmlns:ret="http://retailexpress.com.au/"/>
<CustomerGetBulkDetails xmlns="http://retailexpress.com.au/"/>
In other words, if the XML/SOAP implementation work correctly it does not matter which alias - ns1 or ret - is used for the namespace.
I am new to WebServices in General, and so far I have developed a Web Service SoapServer using Zend/Soap. I have been able to use it just fine, the problem is, that i want the client to be able to send the data as and array .
So far this is what i have done:
soap_server.php
<?php
/*
* url_helper used to check the client Access Ip adrress and Port, in case is from dev or prod enviorement. etc.
* returns the portion of the URL dynamicly in case its accesded from a public or local location.
*
*/
function url_helper(){
$s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
$sp = strtolower($_SERVER["SERVER_PROTOCOL"]);
$protocol = substr($sp, 0, strpos($sp, "/")) . $s;
$port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
return $protocol . "://" . $_SERVER['SERVER_NAME'] . $port;
}
if(($_SERVER['PHP_AUTH_USER'] == "test") AND ($_SERVER['PHP_AUTH_PW'] == "secret")){
//autoload from composer, loads all the required files for ZendSoap
include("vendor/autoload.php");
$serviceURL = url_helper().'/soap_server.php';
//The class for the WebService
class soap_server{
/**
*
* #param string $str1
* #param string $str2
* #param string $str3
* #return stdClass
*/
public function TEST($str1,$str2,$str3) {
// do some work here he
$response = new stdClass();
$response->message = "vars = ($str1,$str2,$str3)";
$response->success = true;
return $response;
}
}
// Generate WSDL relevant to code
if (isset($_GET['wsdl'])){
$autodiscover = new Zend\Soap\AutoDiscover();
$autodiscover->setClass('soap_server')
->setUri($serviceURL)
->setServiceName('soap_server');
$autodiscover->generate();
$autodiscover->handle();
//Soap Server
} else {
$server = new Zend\Soap\Server(null,array('uri' => $serviceURL.'?wsdl'));
$server->setClass('soap_server');
$server->handle();
}
}
else
{
//Send headers to cause a browser to request
//username and password from user
header("WWW-Authenticate: " .
"Basic realm=\"Protected Area\"");
header("HTTP/1.0 401 Unauthorized");
//Show failure text, which browsers usually
//show only after several failed attempts
print("This page is protected by HTTP " .
"Authentication.<br>\nUse <b>User</b> " .
"for the username, and <b>PW</b> " .
"for the password.<br>\n");
}
And the test client works fine when i send at as String, as intended and defined in the soapServer:
test_client.php
include("vendor/autoload.php");
$client = new Zend\Soap\Client("http://127.0.0.1/soap_server.php?wsdl",array('login'=>'test','password'=>'secret'));
$result1 = $client->TEST('Data1','OtherString','test');
print_r($result1);
All i need now if find a way so that the client can send me data in and Array like:
$data = array('str1'=>'Data1','str2'=>'OtherString','str3'=>'test');
But i don't know how to set this up with Zend Framework Autodiscovery and pair it with a working client. Have tried using type array instead of String, but with no success.
Thanks a lot for any help.
Sincerely,
Daniel
EDIT
So I have done further testing, and i do get to set up and Array or and stdClass in the docblock and it works in the following way:
The sample Zend Framework Soap Server:
$serviceURL = url_helper().'/test_ws.php';
//The class for the WebService
class test_ws{
/**
*
* #param string $str1
* #param array $myArray
* #param stdClass $myObject test
* #return stdClass
*/
public function TEST2($str1,$myArray,$myObject) {
// do some work here
$response = new stdClass();
$response->string = $str1;
$response->array = var_export($myArray,TRUE);
$response->stdClass = var_export($myObject,TRUE);
$response->object1 = $myObject->obj1;
$response->success = true;
return $response;
}
}
// Generate WSDL relevant to code
if (isset($_GET['wsdl'])){
$autodiscover = new Zend\Soap\AutoDiscover();
$autodiscover->setClass('test_ws')
->setUri($serviceURL)
->setServiceName('test_ws');
$autodiscover->generate();
$autodiscover->handle();
//Soap Server
} else {
$server = new Zend\Soap\Server(null,array('uri' => $serviceURL.'?wsdl'));
$server->setClass('test_ws');
$server->handle();
}
}
The Soap Call:
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:test="http://10.1.11.122/ws_kioskos/test_ws.php">
<soapenv:Header/>
<soapenv:Body>
<test:TEST2 soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<str1 xsi:type="xsd:string">String</str1>
<myArray xsi:type="soapenc:Array" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<!--You may enter ANY elements at this point-->
<elem1 xsi:type="xsd:string">pos1</elem1>
<elem2 xsi:type="xsd:string">pos2</elem2>
</myArray>
<myObject xsi:type="test:stdClass">
<obj1 xsi:type="xsd:string">HELO</obj1>
<obj2 xsi:type="xsd:string">WORLD</obj2>
</myObject>
</test:TEST2>
</soapenv:Body>
</soapenv:Envelope>
And the Soap Response:
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://10.1.11.122/ws_kioskos/test_ws.php?wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:TEST2Response>
<return xsi:type="SOAP-ENC:Struct">
<string xsi:type="xsd:string">String</string>
<array xsi:type="xsd:string">array (0 => 'pos1', 1 => 'pos2',)</array>
<stdClass xsi:type="xsd:string">stdClass::__set_state(array('obj1' => 'HELO', 'obj2' => 'WORLD',))</stdClass>
<object1 xsi:type="xsd:string">HELO</object1>
<success xsi:type="xsd:boolean">true</success>
</return>
</ns1:TEST2Response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
So with The stdClass I´m close to what i want to implement, as you can see with the:
<object1 xsi:type="xsd:string">HELO</object1> the only think missing is If there is a way to tell the Autodiscover Method what are the elements that are inside the stdClass, so that the client know how to interact with the WSDL, or is there another approach that would let me do such think.
I have read some about using Complex Data types, and using a ClassMap to define the WebService, but i could not make anything work with it, as i could not find good documentation for such implementation.
Once again,
Thanks a lot for any help.
Daniel
Working example
After some research, here is the solution that would be useful to anyone who builds SOAP server with zend-soap in Laravel.
Acme\Controllers\SoapController class:
...
public function wsdl()
{
$wsdl = new AutoDiscover(new ArrayOfTypeComplex());
$this->populateServer($wsdl);
return response($wsdl->toXml(), 200)
->header('Content-Type', 'application/wsdl+xml');
}
private function populateServer($server)
{
$server->setClass(MyService::class);
$server->setUri('http://host.com/soap/server');
}
public function server(Request $request)
{
$server = new Zend\Soap\ServerServer();
$this->populateServer($server);
$response = $server->handle();
return response($response, 200)->header('Content-Type', 'application/soap+xml');
}
...
Acme\Services\MyService class:
namespace Acme\Services;
class MyService
{
/**
* Does something.
* #param Acme\Types\ItemType[] $items
* #return \StdClass
*/
public function doSomething(array $items)
{
// ...
}
}
Acme\Types\ItemType class:
namespace Acme\Types;
class ItemType
{
/**
* #var string
*/
public $propertyName;
}
Please note two things:
Use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex strategy; and
Provide fully-qualified class name in a docblock.
Hope that helps.
I'm using PHP's SoapClient to consume a SOAP service but am receiving an error that the SOAP service cannot see my parameters.
<tns:GenericSearchResponse xmlns:tns="http://.../1.0">
<tns:Status>
<tns:StatusCode>1</tns:StatusCode>
<tns:StatusMessage>Invalid calling system</tns:StatusMessage>
</tns:Status>
</tns:GenericSearchResponse>
The XML PHP's SoapClient sends for the SOAP call:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://.../1.0">
<SOAP-ENV:Body>
<ns1:GenericSearchRequest>
<UniqueIdentifier>12345678</UniqueIdentifier>
<CallingSystem>WEB</CallingSystem>
</ns1:GenericSearchRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I used soap-ui initially, that works successfully when consuming the same WSDL. The XML soap-ui sends for the call:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://.../1.0">
<SOAP-ENV:Body>
<ns1:GenericSearchRequest>
<ns1:UniqueIdentifier>12345678</ns1:UniqueIdentifier>
<ns1:CallingSystem>WEB</ns1:CallingSystem>
</ns1:GenericSearchRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The difference being the UniqueIdentifier and CallingSystem parameters are prefixed with ns1 in the soap-ui request.
I've tried using passing SoapVar objects to the SoapClient call but this does not augment the parameter tags and prefix them with ns1.
I know that WEB is a valid CallingSystem value as the XSD specifies it, and it works when using soap-ui.
My current SoapClient code:
try {
$client = new SoapClient($wsdl, array('trace' => 1));
$query = new stdClass;
$query->UniqueIdentifier = $id;
$query->CallingSystem = 'WEB';
$response = $client->GenericUniqueIdentifierSearch($query);
} catch (SoapFault $ex) {
$this->view->error = $ex->getMessage();
...
}
I found this blog post but I was hoping there might be a cleaner implementation.
Update:
Used a solution from this question but is pretty clunky:
$xml = "<ns1:GenericSearchRequest>"
. "<ns1:UniqueIdentifier>$id</ns1:UniqueIdentifier>"
. "<ns1:CallingSystem>WEB</ns1:CallingSystem>"
. "</ns1:GenericSearchRequest>";
$query = new SoapVar($xml, XSD_ANYXML);
$response = $this->client->__SoapCall(
'GenericUniqueIdentifierSearch',
array($query)
);
The reasonable way I found to do this, is to use a combination of SoapVar and SoapParam.
Note, SoapVar has options to specify the namespace of each var.
So your code should be something like:
$wrapper = new StdClass;
$wrapper->UniqueIdentifier = new SoapVar($id, XSD_STRING, "string", "http://www.w3.org/2001/XMLSchema", "UniqueIdentifier", "ns1");
$wrapper->CallingSystem = new SoapVar("WEB", XSD_STRING, "string", "http://www.w3.org/2001/XMLSchema", "CallingSystem", "ns1");
$searchrequest = new SoapParam($wrapper, "GenericSearchRequest");
try{
$response = $this->client->GenericUniqueIdentifierSearch($searchrequest);
}catch(Exception $e){
die("Error calling method: ".$e->getMessage());
}
If you get an issue where the attributes and the method are getting different namespaces, try specifying the namespace for your SoapVar as the url as defined in your envelope (in your example: "http://.../1.0") like:
$wrapper->UniqueIdentifier = new SoapVar($id, XSD_STRING, "string", "http://www.w3.org/2001/XMLSchema", "UniqueIdentifier", "http://.../1.0");
See the Soap constants for a list of all XSD_* constants.
Use a SoapVar to namespace the GenericSearchRequest fields:
$xml = "<ns1:GenericSearchRequest>"
. "<ns1:UniqueIdentifier>$id</ns1:UniqueIdentifier>"
. "<ns1:CallingSystem>WEB</ns1:CallingSystem>"
. "</ns1:GenericSearchRequest>";
$query = new SoapVar($xml, XSD_ANYXML);
$response = $this->client->__SoapCall(
'GenericUniqueIdentifierSearch',
array($query)
);
Doing some SOAP calls to a 3rd party application. They provide this soap header as an example of what the application expects. How can I create a SOAP header like this in PHP?
<SOAP-ENV:Header>
<NS1:Security xsi:type="NS2:Security" xmlns:NS1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:NS2="urn:dkWSValueObjects">
<NS2:UsernameToken xsi:type="NS2:TUsernameToken">
<Username xsi:type="xsd:string">XXXX</Username>
<Password xsi:type="xsd:string">XXX</Password>
</NS2:UsernameToken>
</NS1:Security>
</SOAP-ENV:Header>
I do what i think is a correct call and keep getting in return that no headers were sent.
Here is a sample from my code.
class SOAPStruct
{
function __construct($user, $pass)
{
$this->Username = $user;
$this->Password = $pass;
}
}
$client = new SoapClient("http://www.example.com/service");
$auth = new SOAPStruct("username", "password");
$header = new SoapHeader("http://example.com/service", "TUsernameToken", $auth);
$client->__setSoapHeaders(array($header));
$client->__soapCall("GetSubscriptionGroupTypes", array(), NULL, $header)
And this is the SOAP header i get back. (its more but i stripped info away that might be sensitive)
<SOAP-ENV:Header>
<ns2:TUsernameToken>
<Username>username</Username>
<Password>password</Password>
</ns2:TUsernameToken>
</SOAP-ENV:Header>
SOAP header handling in PHP is actually not very flexible and I'd go as far as saying that especially the use two namespaces within the header will make it impossible to inject the header simply by using a SoapHeader-construct of some type.
I think the best way to handle this one is to shape the XML request yourself by overriding SoapClient::__doRequest() in a custom class that extends SoapClient.
class My_SoapClient extends SoapClient
{
public function __doRequest($request, $location, $action, $version, $one_way = 0)
{
$xmlRequest = new DOMDocument('1.0');
$xmlRequest->loadXML($request);
/*
* Do your processing using DOM
* e.g. insert security header and so on
*/
$request = $xmlRequest->saveXML();
return parent::__doRequest($request, $location, $action, $version, $one_way);
}
}
Please see SoapClient::__doRequest for further information and some examples.