Automatic WSDL Generation in PHP 5
Downloads for the software described here are available on the downloads page.
About WSDL
WSDL (Web Services Description Language) is part of the XML Web Services technology platform. WSDL files describe to web service clients what services are available at a particular endpoint (server), and how the request and response messages should be (de)serialised. Find out more by reading the Web Services Description Language (WSDL) 1.1 W3C Note.
Typically, WSDL files are automatically generated from web service code using reflection, and are imported into clients at design-time. Visual Studio, for example, uses WSDL files to create proxy classes allowing the developer to call remote web services as simple method calls on a local object, making the process seamless (as if you were just calling a local method).
The two main platforms used for web services in the real world - J2EE and .NET - can both generate WSDL files easily from code because they are strongly typed languages. This is important because XML web services typically pass strongly typed data between client and server.
WSDL generation in PHP
PHP is a weakly typed language (meaning it is not necessary to declare a variable's content type when the variable itself is defined, and casts between eg. numbers and strings happen automatically depending on the context in which a variable is used), therefore reflection to generate WSDL files is impossible. This is a serious limitation for the applicability of PHP as a web services platform as it prevents PHP web services from having easy interoperability with clients written in other languages.
There have been several attempts to write WSDL auto-generation code for PHP. They typically work by having the developer provide additional data about the types of each argument and return value to each web service defined, for example in comments or arrays.
All of the published solutions are rather limited. Perhaps the best attempt I found is David Giffin's wsdl-writer-0.3, available at www.giffin.org. It operates by parsing comments immediately before a web service method to determine the types of its arguments. Here is a simple example:
class SimpleService {
/**
* Add method
*
* @param int $a First integer
* @param int $b Second integer
* @return int Result of addition
*/
public function Add($a, $b)
{
return $a + $b;
}
}
Some notes on syntax:
- The comment parsing is very strict. You must not leave any empty lines between the closing
*/and the method declaration. - Object properties must be declared using the exact syntax
/** @var int Some integer value */on the line immediately preceding the property declaration. - The types specified after
@paramand@returnmust be validXSDtypes orcomplexTypes you have created yourself.
wsdl-writer does a very good job of generating WSDL for simple requests but has some major limitations as it stands. David unfortunately does not maintain wsdl-writer so I have decided to publish my modifications here.
WSDL Writer 0.3-katy.1
The following issues are addressed in this release:
Interoperability improvements:
- PHP web services can now be imported into Visual Studio via their WSDL files (schema namespace fix)
- When creating array types, the
wsdl:arrayTypeattribute will now correctly use thexsdnamespace for arrays of primitive types andtns(the target namespace) for arrays ofcomplexTypes(previouslytnswas used for everything, which causes interoperability failures with .NET and Java) - Object
complexTypedefinitions with array properties will now be correctly serialised as eg.type="tns:ArrayOfstring"instead oftype="tns:string[]"(this problem caused interoperability failures with .NET and Java)
Feature additions:
- Specify
"@param base64Binary $file"or"@return base64Binary"(case-sensitive) to trigger automatic encoding and decoding of binary arguments (such as images) at the client and server.
- This is a significant enhancement which enables PHP web services to process eg. authentication or payment credentials supplied in headers, without having to modify every method of your web services to deal with authentication or other metadata that should preferably be supplied in headers.
- To create a SOAP header processor in a PHP SOAP server, define methods in your web service class with the same names as the header elements you wish to process. The header methods will be called in document order followed by the body method, using a single instance of the class (the same instance will persist over each method call).
- For automatic WSDL generation, specify that a method processes a SOAP header by including
@internal soapheaderbefore the method definition. To specify that a particular web service method requires certain SOAP headers, include@internal soaprequires FirstHeader SecondHeader ....(space-delimited list of header method names). - You can also create derived classes to facilitate encapsulation of SOAP header processing. This is desirable to avoid code repitition when you have several web service methods which use the same set of headers. To implement this, define a base class which processes your headers, and derive from it to create a web service class with your web service methods.
- Limitations: The WSDL specification says that
<soap:header>binding elements don't have to use the same<message>as those used by the<operation>being bound. My implementation only allows the same to be used. This should have no effect on your services in practice. - Limitations: .NET generates SOAP headers whose element names are the type names of their arguments. This means each header must include (and optionally return) precisely one argument; additionally the argument must be a
complexTypefor the web service to import properly into Visual Studio. This also means that the PHP 5 method name which processes the header must be the same as the type name of its argument, eg.public function LoginObject($loginobject)where$loginobjectis of typeLoginObject. PHP 5 doesn't have the element naming, argument count or complexType restrictions, but in order to produce interoperable WSDL and eliminate the possibility of duplicate<part>names in a single<message>(where the arguments to a header and body method have the same name), my implementation co-erces part names representing headers to be the name of the type used as the argument, not the name of the argument itself. Therefore, if you use my implementation to generate WSDL, you must follow the .NET limitations, even if your client is PHP. Again, in practice, this should only be a minor inconvenience. - Access from .NET: In .NET languages such as C# and VB.NET, SOAP headers are available as read/write properties of the web service proxy class generated when your WSDL is imported into Visual Studio.
<types> section of the WSDL file. This effectively means you can now pass and return types of arbitrary complexity among your web services - a very powerful enhancement.
- Background: The original release of wsdl-writer-0.3 only scans the top level of types used (ie. the arguments and return parameters of each method). If any such argument or return parameter has properties which are themselves
complexTypes or arrays ofcomplexTypes (such as objects which have other objects or arrays of objects as public member variables), these are also needed in the<types>section to fully define the parent type. - Example: If you have a web service with returns an array of Foo, and Foo has properties of class types Bar and Baz, and Bar has a property of type array of string, the following types will be defined in the WSDL file:
ArrayOfFoo,Foo,Bar,BazandArrayOfstring(the original release code would only includeFooand its type definition would be empty).
Other bug fixes:
- Command-line WSDL generation now works correctly
- Multiple
complexTypeclass definitions in the same file which have some properties with the same name could cause the<types>section of the WSDL file to be generated incorrectly. This has been fixed. - Constructors and PHP magic methods will be excluded from the generated WSDL
- SOAP servers using SOAP sessions may have a constructor in their web service classes to initialise new sessions to default values, so this exclusion is important
- Strict Standards warnings in PHP 5 fixed
Processing SOAP headers in a PHP SOAP server
Here is an example of a web service class which processes headers and a body method:
// To be supplied as a SOAP header element - remember that individual headers must
// consist of exactly one complexType argument, so basic xsd types must be boxed in a class
class WrappedString
{
/** @var string Data */
public $data;
public function __construct($s)
{
$this->data = $s;
}
}
// Login credentials to be supplied as a SOAP header
class LoginObject
{
/** @var int Login */
public $login;
/** @var int Password */
public $password;
public function __construct($l, $p)
{
$this->login = $l;
$this->password = $p;
}
}
// Arbitrary object returned by the test web service method
// to prove that the header data was stored in the same class instance
class TestObject
{
/** @var string Result string */
public $result;
/** @var int Demo session key */
public $sessionkey;
public function __construct($r, $k)
{
$this->result = $r;
$this->sessionkey = $k;
}
}
class MethodWithHeaders
{
public $sessionKey;
/**
* HeaderA SOAP header
*
* To process a SOAP header, create a function with the same name
as the header's complexType.
* Returned values will be sent back to the client as a SOAP response header.
* This example returns the string in a response header, converted to uppercase.
*
* @param WrappedString $string Input string
* @return WrappedString Capitalised string
* @internal soapheader
*/
public function WrappedString($string)
{
return new WrappedString(strtoupper($string->data));
}
/**
* HeaderB SOAP header
*
* You don't have to return a value - you can store the details sent
* in the header for later use.
*
* @param LoginObject $loginobject Username and password
* @internal soapheader
*/
public function LoginObject($loginobject)
{
$this->sessionKey = $loginobject->login * $loginobject->password;
}
/**
* Some test web service
*
* Body function - shows that a single MethodWithHeaders object
* instantiation is used for all the headers and the body method,
* because $this->sessionKey hasn't been declared static.
*
* @param string $string Input string
* @return TestObject Input string and demo session key based on
header fields
* @internal soaprequires WrappedString LoginObject
*/
public function testfunction($string)
{
return new TestObject($string, $this->sessionKey);
}
}
NOTE: Ironically the type hinting introduced into PHP 5.1 causes PHP SOAP servers to fail, so you should not use type hinting in your web service method or header prototypes.
Supplying SOAP headers and accessing SOAP response headers in a PHP web service client
The following code calls the server example above. Some items in [square brackets] require substitution as appropriate.
// Create SOAP client using only WSDL
$headersClient = new SoapClient("[Location of WSDL file]");
// Create headers (use same element name and type name
// for .NET interoperability - not required for PHP)
$headerA = new SoapHeader('[Namespace URI of WrappedString]',
'WrappedString', new WrappedString('some test string'));
$headerB = new SoapHeader('[Namespace URI of LoginObject]',
'LoginObject', new LoginObject(14, 3));
$headersClient->__setSoapHeaders(array($headerA, $headerB));
// Standard call which passes input headers
// but doesn't offer access to output headers
$headersClient->testfunction("Echo me!"));
// Call which allows access to output headers
// (input headers persist until changed)
$headersClient->__soapCall('testfunction', array("Echo me again!"),
null, null, $outputHeaders);
print_r($outputHeaders);
Note you need to include definitions of WrappedString or LoginObject in the client code.
Final notes
The changes to wsdl-writer and examples above have been tested against PHP 5.1.1 and Visual Studio .NET 2003 (.NET Framework 1.1). For general usage examples please refer to the examples supplied with the source code.
I hope you find this code useful!
Please send feedback using the contact page or use the comment form below.
Printer-friendly version- 12073 reads
buy wow gold cheap wow power
buy wow gold
cheap wow power leveling
my wow gold
cheapest wow power leveling
BUY wow gold
cheap wow power leveling
CHEAP rs gold
good wow power leveling
MY lotro gold
CHEAPEST aion gold
buy wow gold
cheap wow gold
CHEAPEST wow gold
hyrtueu
ftwtwtw
buy cialis online
buy levitra online
buy viagra
buy levitra
buy cialis
Twins movie download
To the author of a blog … I
To the author of a blog … I Read your blog rather recently. That it would be desirable to note … (do not think that with what that I reproach or I try to give advice) laconic enough design, anything superfluous I would tell))) your subjects are close to me, and it pleases. But why do not write the opinion on the events occurring in the world, in respect of events international for example?? I understand, that “news suffice”, but sometimes it would be desirable to learn opinion of the usual person, so to say - an independent sight, to compare it to the opinion. And so … Write even more often, even more, and even more interestingly. Thanks!
tummy tuck
PHP deserialization problem
Hi Katy,
You are really good in PHP as I saw your solution on the link http://markmail.org/message/npkr47g65wddofmr#query:%22No%20Deserializer%...
I am also facing the same problem of "No deserializer found for 'ArrayOfmyComplexType'" on consuming web service.
And your beautifull codes are appreciated by every one; but I am facing following problems.
1. I am not able to use that code in my PHP. Can you help me in that by giving an example.
2. I have genrated classes and classmap by using the link of wso; and my question is whether it is generating proper classes to consume services or not?
Hope you can guide me in right direction.
Thank you
Saurabh
Web developer
www.nriol.com
Bangalore (India)
Fatal error: Class 'WrappedString' not found
Hy there,
When try run your example i get the follow error in client "Fatal error: Class 'WrappedString' not found". What do you mean with this "
Note you need to include definitions of WrappedString or LoginObject in the client code."
Thanks for your work
I'm cofisued is there a
I'm cofisued
is there a online WDSL to PHP Generation?
reply
I am new to spring webservices and I tried to find the similar error but didn't find any thing. Is targetnamespace and name space required for spring to generate wsdl ? My schema doesn't have target name space.
caderea parului
Code commenting for PHP services or class
Hey katy,
I was trying to make create WSDL from PHP script , I am in confusion what kind of code commenting style will help me to get a good WSDL .
Any Example will help me out !!
Thanks in Advance
Arrays and Complex Types
Hi Katy,
Life saving class.
Having trouble returning arrays using SOAP Server with an automatically generated WSDL, as string "Array" is returned rather than the array itself
in the comments I have put
* @return array
is this right? Is there a way to define complex types in the comments or is this something that needs to be added by hand? I find very little on the web regarding returning anything more than a single value through SOAP.
Any pointers would be extremely helpful.
Headers in .NET
Hi Katy,
Great stuff here, I'm just having one problem. If I define a SoapHeader in PHP and a function which requires that header, like LoginObject and testfunction, .NET will generate a method that requires you to pass the LoginObject to the testfunction. So if I were to call the testfunction in .NET and use the headers in the LoginObject I'm required to do:
MethodWithHeaders service = new MethodWithHeaders();
LoginObject lo = new LoginObject();
lo.username = "la";
lo.password = "la";
service.LoginObjectValue = lo;
service.testfunction("testString", lo);
If I have to pass the header object to every function it defeats the purpose I would think. I could just as easily modify the function definitions instead of use the header. Is this the way it is supposed to work?
Regards,
Bas
reply
I know PHP5 has a cool new Web Services extension for using web services, but what about exposing a PHP script or method as a web service? is there a good online tutorial explaining how to do this?
az merchant accounts
Headers Not Processed From PHP and Java Clients
Thnaks for posting this code!
Can you think of any reason why PHP and Java clients are not generating the headers correctly, but .NET clients are. I noticed that the Java client (soapui) is not putting the namespace prefix on the object in the header.
Creating WSDL For your Example
hi katy,
I have read above example. i think that is exactly what i want. but i have problem to create wsdl for the above server side code.
Please also put some instuctions how to create wsdl for above example using your php2wsdl class. i think that will be more helpfull to us.
Hope you will reply. :)
Thanks.
Fantastic!
Thanks Katy!
This was just what I was looking for. I had downloaded David Kingma's wsHelper and found it great to use but it had some downsides in that I didn't want the overhead of it being able to handle multiple web services on the fly. I hacked it up a a bit and had it running as I wanted, but when I came to include authentication into my web service I hit a wall. I looked at contributing to his script to include WSDL headers, but in reality decided I didn't have enough time with a deadline looming.
At that point I started my search again and stumbled across this page. This is exactly what I was looking for, so I thought I'd just drop a message to quickly say thank you.
php-webservice
Hi Katy,
Thank you for sharing such a wonderful article. its very useful to me. I am working in a php web service. I need a help in that. Could you please guide me how we can pass login credentials in the php web service to check the login.
Please advice.
Thanks & Regards,
Venkat
Post new comment