PHP and Soap Headers
When I first began experimenting with PHP-SOAP, the one thing I noticed immediately was the distinct lack of documentation on php.net for SOAP. Functions and features go undocumented (eg __SetSoapHeaders) and those that are found include little relevant information beyond language syntax. The following blog post(s) cover some of the concepts that I found difficult to find any information on.
This post concerns itself with how PHP-SOAP handles SOAP Headers as a client and a server. SOAP Headers provide a standardized mechanism for web services to include function modifiers that do not correspond to directly to the function being called. The most common use of headers are for authentication purposes (eg username/password or token). The authentication data could technically be included as just another function parameter but doing so clutters the function semantics.
The complete code including WSDL, client, and server can be found here. You will need to manually change the soap:address in the WSDL to your own server.
If you look at ed.wsdl, you should be able to tell the following about the service:
- it is Document/Literal
-- the binding is defined with a style of document
-- for each operation in the binding, inputs/outputs have use defined as literal
- it defines a single function named "hello"
--accepts a complex element named "hello" that contains a single attribute "firstname"
--accepts a SOAP Header consisting of a single complex element named "helloHeader" that contains a single attribute "lastname"
--returns a complex element named helloResponse that contains a single attribute "goodbye"
As you can see, the service is fairly light-weight. In PHP, the complex elements will simply become associative arrays or stdClass objects.
The important piece to note is how the SOAP Headers are defined. A <header /> element from the soap namespace must be defined within the binding element for the operation. This informs the client that a header may be sent. The message and part of the header indicate the structure of the header. The message must point to a <message /> element. The part should point to a <part /> within that message.
The reason why the header object must be explicitly defined as a message element as opposed to simply a schema element is because the headers are processed as a separate function call. A SOAP Header should only be used for function modifiers, not arguments. As the header could be defined for multiple functions, it makes sense that the header processing be done without regard to the function being called.
If you look at ed_server.php, you will notice the class Ed{} has two functions defined: hello and helloHeader. When a client includes the header with a call to hello, helloHeader() will be the first call after service(). The value from the header is stored as a private class variable and will be used later in the local hello() call. The result is an object with a single element "goodbye" consisting of the firstname and possibly lastname that were submitted.
Lastly, PHP-SOAP is somewhat immature in terms of SOAP Headers when used as a client. In other toolkits such as .NET and AXIS, headers that are defined as complex elements are automatically (de)serialized to/from the appropriate custom class. In PHP-SOAP, there is only one generic header class that must be explicitly told what definition to use. You do this by specifying the namespace, object name, and values in the SoapHeader constructor. For really complex headers (eg one element is an array of other complex elements), the values simply become multi-dimensional arrays.
Hopefully, this post provides a bit of insight into how you can incorporate headers into your web service with PHP-SOAP. My next post will discuss SOAP faults and how other toolkits interpret your faults.
Ed
Nice tutorial, Ed. Should be helpful for people getting started with PHP + Soap.
Posted by: Aamer | April 11, 2007 at 07:09 PM
Do you know some trick to add custom attributes to the soap headers?
Posted by: hug | August 10, 2007 at 07:05 AM
I do not know of a way to add custom attributes to a soap header in PHP-SOAP. If all else fails, you could try NuSOAP which will allow you to specify a complete header string containing anything you'd like.
Posted by: Ed | August 29, 2007 at 11:25 AM
Hi guys, looks like it's been a a while since you posted on this tech blog. May I suggest adding code samples to the API section on the Bronto site? Our use case for the API is to pass along information from Bronto to Google Analytics using GA's urchinTracker as the data receiver. Among other things I'd like to generate "fake page views" in GA profiles we would set up for our newsletters to track newsletter opens in GA. The reason I need to go through the API is that most email clients won't execute GA's javascript. Right now we're only tracking newsletter click-throughs in GA.
We're also interested in extracting data that Bronto doesn't present in reports such as contact subscribes/unsubscribes/bounces per day.
Posted by: Olivier Travers | October 22, 2007 at 06:07 PM
Hey Ed.
This is bl00dy fantastic.
Exactly what I was looking for - simple, function and self-explanatory.
It's ridiculous exactly how little good documentation and how much bad/useless documentation about exactly what you've done here.
Cheers,
Chris
Posted by: Chris | November 23, 2007 at 07:49 AM
Thx for your Tutorial.
As Aamer said:
It was really helpful getting started with PHP and SOAP, especially with using customized SOAP-Headers.
Thx again,
Lars
Posted by: Lars | February 20, 2008 at 06:25 AM
Thanks for this tutorial, with this simple example.
I've a question : how do you deal with errors, fault, that you could throw thru soap response ? I don't find any reference to that ...
Thanks in advance,
Matthieu
Posted by: Plantex | February 21, 2008 at 09:11 AM
In response to those asking about custom attributes in Soap headers. The only way I've found is to catch the xml on the way out and rewrite the xml using preg_replace or something similar.
You need to create a subclass of SoapClient and override the __doRequest method. Have a look for "Dirty Secrets of SOAP" in google to find an exact explanation.
Posted by: juamei | March 05, 2008 at 11:32 AM
Hi,
Thanks for the great help, I found it very useful for understanding how to get headers from a soapserver class.
I was wondering if you would know how to also return the same header in the response?
I would like a client to "tag" a request with a unique ID in the header, and I would like the response to the request to contain the identical "tag" in the header back to the client.
Thanks!
Posted by: Ateil | March 21, 2008 at 12:18 AM
Hi,
Thanks for the great help, I found it very useful for understanding how to get headers from a soapserver class.
I was wondering if you would know how to also return the same header in the response?
I would like a client to "tag" a request with a unique ID in the header, and I would like the response to the request to contain the identical "tag" in the header back to the client.
Thanks!
Posted by: Ateil | March 21, 2008 at 12:19 AM
Hi,
Thanks for the great help, I found it very useful for understanding how to get headers from a soapserver class.
I was wondering if you would know how to also return the same header in the response?
I would like a client to "tag" a request with a unique ID in the header, and I would like the response to the request to contain the identical "tag" in the header back to the client.
Thanks!
Posted by: Ateil | March 21, 2008 at 12:19 AM
Hi,
Thanks for the great help, I found it very useful for understanding how to get headers from a soapserver class.
I was wondering if you would know how to also return the same header in the response?
I would like a client to "tag" a request with a unique ID in the header, and I would like the response to the request to contain the identical "tag" in the header back to the client.
Thanks!
Posted by: Ateil | March 21, 2008 at 12:19 AM
I think this is a great tutorial. I will forward this to my friends for study.
Posted by: Ter | April 23, 2008 at 11:20 PM
very helpful your example
Posted by: Matthias Mueller | May 29, 2008 at 05:49 AM
Thank you so much, man.
Posted by: Ananda Putra | September 16, 2008 at 04:43 AM
Hi,
I am venkat. I tried and tired of creating a web service using php and nusoap. I have to call a complex data type and have to pass the login and password details as hardcode value. The result has to return the users details. I dont know how do it.
Please help, it would be a great help for me.
Thanks
Venkat
Posted by: venkat | November 10, 2008 at 04:58 AM