This chapter describes:
- PHP SOAP Extension Functions for Server Applications
- HelloServer.php - First SOAP Server Application
- HelloServer12.php - SOAP 1.2 Server Application
- HelloServerWsdl.php - SOAP 1.2 Server Application in WSDL Mode
PHP SOAP Extension Functions for Server Applications
If you read the SOAP Extension reference page, you will see that SOAP Extension support SOAP server applications with a class called SoapServer, which offers the following functions:
- SoapServer->__construct() - construct a new SoapServer object
- SoapServer->addFunction() - Adds one or several functions those will handle SOAP requests
- SoapServer->setClass() - Sets class which will handle SOAP requests
- SoapServer->handle() - Handles a SOAP request
- SoapServer->getFunctions() - Returns list of defined functions
- ...
SoapServer->__construct() allows you to construct a new SoapServer object with the following syntax:
__construct ( mixed wsdl [, array options] );
where "wsdl" specifies the URL of the WSDL document, and "options" specifies a list of options:
'uri' => "...", # the name space of the SOAP service 'soap_version' => SOAP_1_1 |SOAP_1_2, 'actor' => "...", # the actor 'encoding' => "...", # the encoding name 'classmap' => "...", # a map of WSDL types to PHP classes ...
Note that SoapServer object can be constructed in two modes, WSDL mode and non-WSDL mode:
__construct( "..."[, array options] ); # WSDL mode, many options
# are provided by the WSDL document
__construct( null, array options ); # non-WSDL mode,
# 'uri' is a required option.
SoapServer->addFunction() allows you to add one or more functions to handle SOAP requests. The functions to be added are functions defined in the current PHP program file.
$obj->addFunction("func"); # adds one function
$obj->addFunction(array("func1",...)); # adds many functions
$obj->addFunction(SOAP_FUNCTIONS_ALL); # adds all functions
SoapServer->setClass() allows you to all methods in the specified class to handle SOAP requests.
$obj->setClass("cls");
SoapServer->handle() allows you to return the execution back the SOAP Extension to handle the incoming SOAP request.
$obj->handle();
SoapServer->getFunctions() allows you to get a list of all functions that have been assigned to handle SOAP requests.
array $a = $obj->getFunctions();
HelloServer.php - First SOAP Server Application
Okay. Now let's build our first SOAP server. I want the first server to perform a very simple function of return a greeting string based on the input name. Here is my version called, HelloServer.php:
<?php # HelloServer.php
# Copyright (c) 2005 by Dr. Herong Yang, http://www.herongyang.com/
#
function hello($someone) {
return "Hello " . $someone . "!";
}
$server = new SoapServer(null,
array('uri' => "urn://www.herong.home/res"));
$server->addFunction("hello");
$server->handle();
?>
The sever application is ready. Note that:
- This SOAP server application can not be used as a standalone SOAP server. It needs a HTTP server to receive SOAP requests, and a PHP runtime to act as a CGI to feed SOAP requests.
- The "uri" value is just an unique identification, used as the namespace for the response message.
The HTTP server I will be using is the MS IIS (Internet Information Services). It has already configured correctly to interface with PHP CGI. For details, see my other book: "Herong's Tutorial Notes on PHP". All I have to do is to move my server application to IIS document directory:
>copy HelloServer.php \Inetpub\wwwrootTo test my server application, I wrote this client application, HelloClient.php:
<?php # HelloClient.php # Copyright (c) 2005 by Dr. Herong Yang # $client = new SoapClient(null, array( 'location' => "http://localhost/HelloServer.php", 'uri' => "urn://www.herong.home/req", 'trace' => 1 )); $return = $client->__soapCall("hello",array("world")); echo("\nReturning value of __soapCall() call: ".$return); echo("\nDumping request headers:\n" .$client->__getLastRequestHeaders()); echo("\nDumping request:\n".$client->__getLastRequest()); echo("\nDumping response headers:\n" .$client->__getLastResponseHeaders()); echo("\nDumping response:\n".$client->__getLastResponse()); ?>Check your IIS server to make sure it is running. Then run HelloClient.php. You will get:
Returning value of __soapCall() call: Hello world! Dumping request headers: POST /HelloServer.php HTTP/1.1 Host: localhost Connection: Keep-Alive User-Agent: PHP SOAP 0.1 Content-Type: text/xml; charset=utf-8 SOAPAction: "urn://www.herong.home/req#hello" Content-Length: 499 Dumping request: <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn://www.herong.home/req" 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:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body><ns1:hello> <param0 xsi:type="xsd:string">world</param0> </ns1:hello></SOAP-ENV:Body> </SOAP-ENV:Envelope> Dumping response headers: HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Connection: close Content-Type: text/xml; charset=utf-8 X-Powered-By: PHP/5.0.4 Content-Length: 522 Dumping response: <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn://www.herong.home/res" 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:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body><ns1:helloResponse> <return xsi:type="xsd:string">Hello world!</return> </ns1:helloResponse></SOAP-ENV:Body> </SOAP-ENV:Envelope>Very exciting, right? Both server and client work nicely.
If you are interested in how the execution was carried out on the server, I have a simplified execution flow diagram on the server side:
IIS PHP (SOAP Extension) HelloServer.php HTTP request | -------------->| SOAP message | | CGI | PHP CGI API |------->|------------------------>| | addFunction() | SOAP Extension API | handle() |<------------------------| | | SOAP Extension API |------------------------>| SOAP Extension API | hello() |<------------------------| CGI | |<-------| HTTP response | <--------------| SOAP message |HelloServer12.php - SOAP 1.2 Server Application
So far, we have tried only with SOAP 1.1. Can we do some tests with SOAP 1.2? Sure. Here is my hello server modified for SOAP 1.2, HelloServer12.php:
<?php # HelloServer12.php # Copyright (c) 2005 by Dr. Herong Yang # function hello($someone) { return "Hello " . $someone . "! - SOAP 1.2"; } $server = new SoapServer(null, array( 'uri' => "urn://www.herong.home/res", 'soap_version' => SOAP_1_2)); $server->addFunction("hello"); $server->handle(); ?>Here is my hello client modified for SOAP 1.2, HelloClient12.php:
<?php # HelloClient12.php # Copyright (c) 2005 by Dr. Herong Yang # $client = new SoapClient(null, array( 'location' => "http://localhost/HelloServer12.php", 'uri' => "urn://www.herong.home/req", 'soap_version' => SOAP_1_2, 'trace' => 1 )); $return = $client->__soapCall("hello",array("world")); echo("\nReturning value of __soapCall() call: ".$return); echo("\nDumping request headers:\n" .$client->__getLastRequestHeaders()); echo("\nDumping request:\n".$client->__getLastRequest()); echo("\nDumping response headers:\n" .$client->__getLastResponseHeaders()); echo("\nDumping response:\n".$client->__getLastResponse()); ?>Remember to move HelloServer12.php to IIS document directory. Then run HelloClient12.php. You will get:
Returning value of __soapCall() call: Hello world! - SOAP 1.2 Dumping request headers: POST /HelloServer12.php HTTP/1.1 Host: localhost Connection: Keep-Alive User-Agent: PHP SOAP 0.1 Content-Type: application/soap+xml; charset=utf-8; action="urn://www.herong.home/req#hello" Content-Length: 458 Dumping request: <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="urn://www.herong.home/req" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2003/05/soap-encoding"> <env:Body> <ns1:hello env:encodingStyle="http://www.w3.org/2003/05/soap-encoding"> <param0 xsi:type="xsd:string">world</param0> </ns1:hello> </env:Body> </env:Envelope> Dumping response headers: HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Connection: close Content-Type: application/soap+xml; charset=utf-8 X-Powered-By: PHP/5.0.4 Content-Length: 570 Dumping response: <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="urn://www.herong.home/res" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2003/05/soap-encoding"> <env:Body xmlns:rpc="http://www.w3.org/2003/05/soap-rpc"> <ns1:helloResponse env:encodingStyle="http://www.w3.org/2003/05/soap-encoding"> <rpc:result>return</rpc:result> <return xsi:type="xsd:string">Hello world! - SOAP 1.2</return> </ns1:helloResponse> </env:Body> </env:Envelope>Now the output is more interesting:
- SOAP 1.2 HTTP Binding rules are applied. See the HTTP request header line: Content-Type. It has a value of "application/soap+xml", and parameter of "action=...".
- SOAP 1.2 Message rules are applied. See the envelope namespace value in both request message and response message: xmlns:env="http://www.w3.org/2003/05/soap-envelope.
- SOAP 1.2 RPC Presentation rules are applied. See the "result" sub element in the response element of the response message. But SOAP Extension does not put the returning value directly in this sub element. It inserts a second sub element in the response element, and puts the returning value in this second element. The value in the required "result" element is just a reference to the second element. This seems to be violating the SOAP 1.2 RPC Presentation specifications.
HelloServerWsdl.php - SOAP 1.2 Server Application in WSDL Mode
Now let's move forward one step further: creating a SOAP 1.2 server application in WSDL mode. Here is the same hello server modified to work in WSDL mode, HelloServerWdsl.php:
<?php # HelloServerWsdl.php
# Copyright (c) 2005 by Dr. Herong Yang
#
function hello($someone) {
return "Hello " . $someone . "! - With WSDL";
}
ini_set("soap.wsdl_cache_enabled", "0");
$server = new SoapServer("http://localhost/Hello.wsdl",
array('soap_version' => SOAP_1_2));
$server->addFunction("hello");
$server->handle();
?>
Nothing special in the program. The server object is now created with the location of the WSDL document.
Here is the WSDL document, Hello.wsdl
<?xml version="1.0"?>
<definitions name="MyDefinition"
targetNamespace="urn:myTargetNamespace"
xmlns:tns="urn:myTns"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="myRequest">
<part name="reqParam" type="xsd:string"/>
</message>
<message name="myResponse">
<part name="resParam" type="xsd:string"/>
</message>
<portType name="MyPortType">
<operation name="hello">
<input message="tns:myRequest"/>
<output message="tns:myResponse"/>
</operation>
</portType>
<binding name="MyBinding" type="tns:MyPortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="hello">
<soap:operation soapAction=""/>
<input>
<soap:body use="encoded"
namespace="urn:myInputNamespace"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded"
namespace="urn:myOutputNamespace"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="MyService">
<documentation>Returns a greeting string.
</documentation>
<port name="MyPort" binding="tns:MyBinding">
<soap:address
location="http://localhost/HelloServerWsdl.php"/>
</port>
</service>
</definitions>
The WSDL document looks complicated. But it actually very simple to understand, as long as you remember the following points:
- Read the document backward. The goal of this document is to define a "service" with two pieces of information: "binding" definition, and "url" where to reach the server. The "binding" is then defined with "type", "style", "transportation" and "operation". And so on.
- The values of "name" attributes in most of the elements are identifiers local to this document only. You can use any strings. Some of them will be used on the SOAP messages.
- The values of "namespace" attributes can also be any strings. They are just used to distinguish name spaces.
To install HelloServerWsdl.php to my IIS server, copy these two files to the IIS document directory:
>copy HelloServerWsdl.php \inetpub\wwwroot >copy Hello.wsdl \inetpub\wwwroot
Of course, we need to modify out hello client program as, HelloClientWsdl.php:
<?php # HelloClientWsdl.php
# Copyright (c) 2005 by Dr. Herong Yang
#
$client = new SoapClient("http://localhost/Hello.wsdl",
array('soap_version' => SOAP_1_2,'trace' => 1 ));
echo("\nDumping client object functions:\n");
var_dump($client->__getFunctions());
$return = $client->__soapCall("hello",array("world"));
echo("\nReturning value of __soapCall() call: ".$return);
# $return = $client->hello("world");
# echo("\nReturning value: ".$return);
echo("\nDumping request headers:\n"
.$client->__getLastRequestHeaders());
echo("\nDumping request:\n".$client->__getLastRequest());
echo("\nDumping response headers:\n"
.$client->__getLastResponseHeaders());
echo("\nDumping response:\n".$client->__getLastResponse());
?>
When you are ready to test, run HelloClientWsdl.php. You will get:
Dumping client object functions:
array(1) {
[0]=>
string(30) "string hello(string $reqParam)"
}
Returning value of __soapCall() call: Hello world! - With WSDL
Dumping request headers:
POST /HelloServerWsdl.php HTTP/1.1
Host: localhost
Connection: Keep-Alive
User-Agent: PHP SOAP 0.1
Content-Type: application/soap+xml; charset=utf-8; action=""
Content-Length: 457
Dumping request:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope
xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:ns1="urn:myInputNamespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
<env:Body>
<ns1:hello
env:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<reqParam xsi:type="xsd:string">world</reqParam>
</ns1:hello>
</env:Body>
</env:Envelope>
Dumping response headers:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Connection: close
Content-Type: application/soap+xml; charset=utf-8
X-Powered-By: PHP/5.0.4
Content-Length: 573
Dumping response:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope
xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:ns1="urn:myOutputNamespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
<env:Body xmlns:rpc="http://www.w3.org/2003/05/soap-rpc">
<ns1:helloResponse
env:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<rpc:result>resParam</rpc:result>
<resParam xsi:type="xsd:string">Hello world! - With WSDL</resParam>
</ns1:helloResponse>
</env:Body>
</env:Envelope>
Anything interesting here? Yes. The some names defined in the WSDL document did show up in the SOAP request message and response message, like: "urn:myInputNamespace", "reqParam", "urn:myOutputNamespace", etc.
Conclusion
SOAP Extension is easy to use for setting up SOAP services quickly.
SOAP Extension does support SOAP 1.2 specification.
WSDL document is actually easy to understand.
Exercise: Write a temperature conversion SOAP service.
discuss this topic to forum
