Few months ago, I blogged about How you represent custom headers in a WSDL. In there I mentioned, WSF/PHP is going to support sending and handling custom SOAP headers with the 2.0 release which was released early September. Today I will write how how to use this new feature in your Web Service Client.
In order to do the comparison I will show you how you could send Custom SOAP headers before WSF/PHP 2.0.
Preparing Custom SOAP Headers Manually
Before 2.0 you have to prepare the custom SOAP headers manually. For that you could use the WSHeader object.
Say you want to create the following message for your WSDL. (I excluded the headers and payload namespaces for the clarity)
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"> <soapenv:Header> <Header1> <string>test1</string> <int>5</int> </Header1> <Header2> <int>6</int> <string>test2</string> </Header2> </soapenv:Header> <soapenv:Body> <echo>Hello</echo> </soapenv:Body> </soapenv:Envelope>
Here is how you prepare it using WSMessage and WSHeader objects.
// request payload $requestPayloadString = <<<XML <echo>Hello</echo> XML; // creation of two headers $header1 = new WSHeader(array( "name" => "Header1", "data" => array( new WSHeader(array("name" => "string", "data" => "test1")), new WSHeader(array("name" => "int", "data" => "5"))))); $header2 = new WSHeader(array( "name" => "Header2", "data" => array( new WSHeader(array("name" => "int", "data" => "6")), new WSHeader(array("name" => "string", "data" => "test2"))))); // you prepare the soap message = request payload + two input headers $request_msg = new WSMessage($requestPayloadString, array("inputHeaders" => array($header1, $header2))); // create the WSClient with the endpoint $client = new WSClient(array( "to" => "http://localhost/header_echo_service.php" )); // do the request with the WSMessage instance as the request argument $responseMessage = $client->request($request_msg); // print the response printf("Response = %s <br>", htmlspecialchars($responseMessage->str));
As you can see you are preparing hierarchical tree of WSHeaders to prepare the SOAP message. Anyway after 2.0 you don’t need to write this much of code to get the work done.
Preparing Custom SOAP Headers in WSDL Mode
If you have a WSDL it is so easy to start by generating the initial code from the WSDL2PHP tool. If I take the WSDL from my old post about custom headers in WSDL WSDL2PHP tool will generate the following set of classes.
class Header1 { /** * @var string */ public $string; /** * @var int */ public $int; } class Header2 { /** * @var int */ public $int; /** * @var string */ public $string; }
And you will get the following piece of code with the Note “TODO” where you need to fill the input fields and retrive the output values from the operation.
// create input object and set values //TODO: fill $input with (data type: string) data to match your business logic $header_in0 = new Header1(); // TODO: fill in the class fields of $header_in0 object which is of type Header1 to match your business logic $header_in1 = new Header2(); // TODO: fill in the class fields of $header_in1 object which is of type Header2 to match your business logic // call the operation $response = $proxy->echoString($input, $header_in0, $header_in1, &$header_out0, &$header_out1); //TODO: Implement business logic to consume $response, which is of type string // TODO: Implement business logic to consume $header_out0 object, which is of type class Header1 // TODO: Implement business logic to consume $header_out1 object, which is of type class Header2
After I follow the guidelines in the comment, My code looks so simple and little like this,
// create input object and set values $input = "Hello"; // I filled $input with a string $header_in0 = new Header1(); // I m filling the header1 $header_in0->string = "test1"; $header_in0->int = 5; $header_in1 = new Header2(); // now the header2; $header_in1->string = "test2"; $header_in1->int = 6; // call the operation $response = $proxy->echoString($input, $header_in0, $header_in1, &$header_out0, &$header_out1); // echoing the response payload which of type string echo $response; // echoing the $header_out0 object, which is of type class Header1 echo "output header0 contains {$header_out0->string} and {$header_out0->int}"; // echoing the $header_out1 object, which is of type class Header2 echo "output header1 contains {$header_out1->string} and {$header_out1->int}";
There the headers are PHP class objects. You fill the public variables of the classes and pass them to the ‘echoString’ operation. Although it looks like it call just a php function named ‘echoString’, it actually invoke the web service operation named ‘echoString’, with the request payload and custom SOAP headers. It returns the response payload value + the output headers to the references we passed as the last 2 arguments.
So with WSF/PHP 2.0 you can use WSDL mode and WSDL2PHP tool to send payload with custom headers with a little and quick code.
Hi. I’m having problem width SOAPy thing. Can You help me with this one? I have in requests something like this:
SOME VALUE HERE
SOME VALUE HERE
(…)
but i have to do it like this:
SOME VALUE
SOME VALUE
(…)
I tred to search some solution for having such requests, somone had the same problem but noone could help him. Albo there’s no answer for this in tutorials etc. Could You help me?
I can’t understand what has you typed. May be you need to escape html special characters with wildcards
Hi Dimuthu,
I’m currently using WSF/PHP 2.0 and have a problem related to the mustUnderstand option of WSHeader … hope you can lend me a hand.
I modified the provided add_soap_header_client.php so that it sets mustUnderstand to 1 (true), but now the called echo_service.php returns “Soap Fault: Header not understood”.
According to http://wso2.org/library/tutorials/understand-famous-did-not-understand-mustunderstand-header-s-error
one should provide a custom handler for the header and set a “processed” flag, but the article is geared to Axis2 and the samples do not directly apply to PHP.
So my question is: how do you write a server-side PHP script to handle request headers with the mustUnderstand option set to “true”?
Hi Massimo,
As you may know WSF/PHP is based on axis2/c. You get the WS-Security, WS-Reliable Messaging(RM) or WS-Addressing features in WSF/PHP because Axis2/C has these specifications implemented as Axis2/C handlers. What these handlers do is process the relevant headers for that handlers (for an example Security handlers processes Security headers and mark it as processed), This happens before invoking the users business logic. If the handlers failed to identify some ‘mustUnderstand’ header the invocation will fail before hitting the users business logic.
In WSF/PHP, we are only allowing you to write business logic in PHP. But doesn’t allow write Handlers in PHP. So whenever you set some custom handler to be ‘mustUnderstand’ true, then the default handlers in WSF/PHP will not understand it and fails before hitting your php logic.
This can be solved by allowing PHP users to write handlers in PHP. But this will surely complicate the current PHP API.
And the other solution (which I recommend) you can do is use a custom mustUnderstand attribute like ‘MyMustUnderstand’. I’m saying this because you are more likely writing a non-standard custom header. The whole point of using ‘mustUnderstand’ attribute is the interoperability with systems like .NET and other Java Frameworks. WSF/PHP already interop with these systems in terms of Security, RM. Your custom header will anyway not be there in other WS frameworks unless you implement it specifically.
In a case you actually writing a standard header that is not supported in WSF/PHP, then it is always better you write it in C as an Axis2/C handler.
This is just my thought. It is up to you to choose a good approach.
Thanks
Dimuthu
Hi Dimuthu,
Thank you for the quick and clear reply.
What I need to handle is a custom header indeed, so the “custom mustUnderstand” solution you advice is the way to go.
Truth to tell, the whole concept seems of little use when applied to custom headers … if you put them there, it’s because you need to handle them anyway, while you are entitled to silently ignore any “unknown” custom header you may receive, isn’t it?
Have a nice day,
Massimo
Yea, the service can ignore soap headers unless mustUnderstand=true is set according to the soap spec. Infact it is the requesters way of enforcing the receiver what should be processed in the request message header.
The most common case is the server enforce some rules by associating a policy (according to the ws-policy spec) to a service. In that case the server is expected to validate the request with the policy, probably what you too want to happen.
Thanks
Dimuthu