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.
Related
I have soap request with body
<soap:Body>
<ProcessRequest>
<request xsi:type="GetNotification">
<node1>val1</node2>
<node2>val2</node2>
</request>
</ProcessRequest>
</soap:Body>
I am trying to pass request type GetNotification in soap client call in PHP but its not working.
in $args I am passing
$args = ['node1' => 'val1','node2'=>'node2'];
$response = $client->ProcessRequest(['request'=>$args])
how do i pass type GetNotification
You can use this tool:
https://github.com/wsdl2phpgenerator/wsdl2phpgenerator
You can give it the soap url and it will generate full list of classes for the service.
Using these classes it will be much easier to understand what and how you need to send you request.
I think this should be something like this. Because the xsi:type indicates that request value should be GetNotification. Keep in mind that i didn't test it and it's just a thought.
<?php
//using arrays
$response = $client->ProcessRequest(['request' => [
'GetNotification' => ['node1' => 'val1', 'node2' => 'val2']
]);
//using objects
class request
{
private $GetNotification;
public function __construct(GetNotification $getNotification)
{
$this->GetNotification = $getNotification;
}
}
class GetNotification
{
private $node1;
private $node2;
public function __construct(string $node1, string $node2)
{
$this->node1 = $node1;
$this->node2 = $node2;
}
}
$response = $client->ProcessRequest(
new request(new GetNotification('val1', 'val2'))
);
I've implemented a zend soap server with the code below. For my purposes i need to be able to access the full XML message or at the very least its headers. However, the getLastRequest method on SoapServer returns empty, and all the super globals, such as $_GET, $_POST and whatnot, are also empty. Anyone has any ideas?
class SoapTest
{
protected $server;
public function __construct(\Zend\Soap\Server $server)
{
$this->server = $server;
}
/***
* #param string $requestIn
* #return string
*/
public function test($requestIn)
{
// access XML here
}
}
$serverUrl = "http://localhost/SoapTest.php";
$options = [
'uri' => $serverUrl,
];
$server = new Zend\Soap\Server(null, $options);
if (isset($_GET['wsdl'])) {
$soapAutoDiscover = new \Zend\Soap\AutoDiscover(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence());
$soapAutoDiscover->setBindingStyle(array('style' => 'document'));
$soapAutoDiscover->setOperationBodyStyle(array('use' => 'literal'));
$soapAutoDiscover->setClass(SoapTest::class);
$soapAutoDiscover->setUri($serverUrl);
header("Content-Type: text/xml");
echo $soapAutoDiscover->generate()->toXml();
} else {
$soap = new \Zend\Soap\Server($serverUrl . '?wsdl', array('cache_wsdl' => WSDL_CACHE_NONE));
$soap->setObject(new \Zend\Soap\Server\DocumentLiteralWrapper(new SoapTest($soap)));
$soap->handle();
}
Apparently, Zend Soap Server fills the $request property (which is returned in getLastRequest) AFTER the handle, so i cant access it in my method.
I can however access the XML by calling the following:
$request = file_get_contents('php://input');
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.
There are several weeks that I'm trying to create a soap server in php that at first serves a wsdl with authentication header on it and at second it accepts only authenticated users in every request. But I' ve only made it fully working only without authentication. Every search I 've made and every solution I 've found contains a SoapClient,Zend_Soap_Client,nu_soap_client (you name it) and either some kind of wrapper class around my class or only addition of username & password on the client.
But at my solution only the server is in php and client are various programs written in java etc, not in php. Here is my code for (I use zend here but the idea is the same on plain php) the wsdl generation and the server part:
use Zend\Soap\AutoDiscover as Zend_Soap_AutoDiscover;
use Zend\Soap\Server as Zend_Soap_Server;
if (isset($_GET['wsdl'])) {
$autodiscover = new Zend\Soap\AutoDiscover();
$autodiscover->setClass('MyClass');
$autodiscover->setUri('http://Myclass/path/');
$autodiscover->handle();
exit;
}
$server = new Zend_Soap_Server(null, array(
'uri' => 'http://Myclass/path/',
));
$server->setClass('Myclass');
$server->handle();
I also used piotrooo's wsdl generator and plain php soap library like this:
use WSDL\WSDLCreator;
// use WSDL\XML\Styles\DocumentLiteralWrapped;
if (isset($_GET['wsdl'])) {
$wsdl = new WSDL\WSDLCreator('Myclass', 'http://Myclass/path/');
$wsdl->setNamespace("http://Myclass/path/");
$wsdl->renderWSDL();
exit;
}
$server = new SoapServer(null, array(
'uri' => 'http://Myclass/path/',
// 'style' => SOAP_DOCUMENT,
// 'use' => SOAP_LITERAL,
));
$server->setClass('Myclass');
$server->handle();
And my class:
class Myclass
{
public function __construct()
{
/*some db stuff with doctrine*/
}
/**
* #param string $id
* #return object
*/
public function Id($id)
{
/*I'm using doctrine to fetch data from db and then return an object/or array*/
}
}
At last this is the auto generated wsdl:
<definitions name="Myclass" targetNamespace="http://Myclass/path/"><types><xsd:schema targetNamespace="http://Myclass/path/"/></types><portType name="MyclassPort"><operation name="Id"><documentation>Id</documentation><input message="tns:IdIn"/><output message="tns:IdOut"/></operation></portType><binding name="MyclassBinding" type="tns:MyclassPort"><soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/><operation name="Id"><soap:operation soapAction="http://Myclass/path/#Id"/><input><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://Myclass/path/"/></input><output><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://Myclass/path/"/></output></operation></binding><service name="MyclassService"><port name="MyclassPort" binding="tns:MyclassBinding"><soap:address location="http://Myclass/path/"/></port></service><message name="IdIn"><part name="id" type="xsd:string"/></message><message name="IdOut"><part name="return" type="xsd:struct"/></message></definitions>
Annotations vary on each generator.
I also tried nusoap but I was disappointed because of it's pure class method discovery. I must add that I'm testing usage of service with soapui (that's why I don't want php's SoapClient or equivalent examples).
Last I also must say that I tried solutions of adding authentication method inside my class and this worked BUT this didn't prevent unauthenticated user from accessing ws.
A little extra information. As of my research every answer I was found was about SOAPClient for example the $client->__addheader() function etc. Please tell me if I' m wrong or if this can't be done with PHP because I ll have to find someone else to do this for me with another programming language like Java etc.
Thanks in advance
Dimitris
You could have your clients send the authentication information in basically 3 places: SOAP Body, SOAP Headers, HTTP Headers. Of the three I think the proper one is SOAP Headers so I'll go with that for this example. (Note I'm using some custom auth header format, but I suggest you research about the WS-Security protocol.)
class Myclass
{
public function __construct()
{
/*some db stuff with doctrine*/
}
public function Auth($auth)
{
if (! $this->validateUser($auth->Username, $auth->Password)) {
throw new SoapFault('Client.Authentication', 'Invalid username or password');
}
}
/**
* #param string $id
* #return object
*/
public function Id($id)
{
/*...*/
}
private function validateUser($user, $password)
{
/*...*/
}
}
Now for the client: I think you in your server role don't have to worry about which particular clients will be consuming your service, simply because you can't consider them all. That's why there are standards, to abstract from particular implementations. So, as long as you follow the standard, you can trust any client will comply. And luckily, SOAP headers are part of the standard.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<Auth>
<Username>foo</Username>
<Password>bar</Password>
</Auth>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<Id>4</Id>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
That is the XML you are expecting to receive, regardless of the client.
Finally, the WSDL. From what I've read, neither Zend Autodiscover nor piotrooo's library support headers definition. Apparently Yii's CWebService does (with proper method annotations).
In the worst case scenario, you can write the WSDL yourself, or adapt it from the one generated with your library of choice.
Links:
http://www.yiiframework.com/doc/api/1.1/CWebService
http://www.ibm.com/developerworks/library/ws-tip-headers/
UPDATE:
class Auth
{
/**
* #soap
* #var string
*/
public $Username;
/**
* #soap
* #var string
*/
public $Password;
}
class MyClass
{
private $authenticated = false;
public function Auth($auth)
{
if ($this->validateUser($auth->Username, $auth->Password)) {
$this->authenticated = true;
}
}
/**
* #soap
* #header Auth $auth
* #param string $id
* #return object
*/
public function Id($id)
{
if (! $this->authenticated) {
throw new SoapFault('Client.Authentication', 'Invalid username or password');
}
//return $id;
}
private function validateUser($user, $password)
{
return $user == 'foo';
}
}
require __DIR__ . '/vendor/yiisoft/yii/framework/yii.php';
$URL = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'];
if (isset($_GET['wsdl'])) {
$gen = new CWsdlGenerator();
$wsdl = $gen->generateWsdl(MyClass::class, $URL);
header("Content-type: text/xml; charset=utf-8");
echo $wsdl;
}
else {
$server = new SoapServer($URL . '?wsdl');
$server->setClass(MyClass::class);
echo $server->handle();
}
This is indeed working as expected with the sample XML I provided before.
(You just need to change <Id>4</Id> for <Id><id>4</id></Id>).
class WSSoapClient extends SoapClient {
private $username;
private $password;
/*Generates de WSSecurity header*/
private function wssecurity_header() {
/* The timestamp. The computer must be on time or the server you are
* connecting may reject the password digest for security.
*/
$timestamp = gmdate('Y-m-d\TH:i:s\Z');
/* A random word. The use of rand() may repeat the word if the server is
* very loaded.
*/
$nonce = mt_rand();
/* This is the right way to create the password digest. Using the
* password directly may work also, but it's not secure to transmit it
* without encryption. And anyway, at least with axis+wss4j, the nonce
* and timestamp are mandatory anyway.
*/
$passdigest = base64_encode(
pack('H*',
sha1(
pack('H*', $nonce) . pack('a*',$timestamp).
pack('a*',$this->password))));
$auth='
<wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">
<wsse:UsernameToken wsu:Id=\"UsernameToken-2\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">
<wsse:Username>'.$username.'</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">'.$password.'</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
<wsa:Action>http://www.kbb.com/2011/01/25/VehicleInformationService/IVehicleInformationService/GetYears</wsa:Action>
';
/* XSD_ANYXML (or 147) is the code to add xml directly into a SoapVar.
* Using other codes such as SOAP_ENC, it's really difficult to set the
* correct namespace for the variables, so the axis server rejects the
* xml.
*/
$authvalues = new SoapVar($auth,XSD_ANYXML);
$header = new SoapHeader("http://docs.oasis-open.org/wss/2004/01/oasis-".
"200401-wss-wssecurity-secext-1.0.xsd", "Security", $authvalues,
true);
return $header;
}
/* It's necessary to call it if you want to set a different user and
* password
*/
public function __setUsernameToken($username, $password) {
$this->username = $username;
$this->password = $password;
}
/* Overwrites the original method adding the security header. As you can
* see, if you want to add more headers, the method needs to be modifyed
*/
public function __soapCall($function_name, $arguments, $options=null,
$input_headers=null, $output_headers=null) {
$result = parent::__soapCall($function_name, $arguments, $options,
$this->wssecurity_header());
return $result;
}
}
I am trying to use this but I am getting the following error:
Fatal error: Uncaught SoapFault exception: [HTTP] Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'
Please tell me how can I set the content type using SOAP object.
If somebody needs answer for the same question, the answer is simple. You need to use SOAP version 1.2 to pass Content-Type: application/soap+xml
$soapClient = new SoapClient('http://example.com/wsdl.wsdl',array(
'soap_version' => SOAP_1_2,
));
But, you must be careful, because it also adds action: youraction to Content-Type. For example:
Content-Type: application/soap+xml; charset=utf-8; action="http://example.com/path/to/your/action"