With WSF/PHP Data Service library, you can convert a SQL query to a Data Service very easily in few steps.

  1. Decide your SQL query first, For the query you may require some input parameters, and you have to decide what should be returned by the query, Say your query is
    $sql_query = "SELECT name, age, email FROM users where country = ?";

    Then the “country” is our input parameter and the name, age, email are our return values.

  2. Define the input Format. For the above query it will be something like,
    $inputFormat = array("country" => "INT");
  3. Define the output format. We are giving the name “Users” for the out most wrapper element and the name “user” for the wrapper element of each user. Here is how we define it,
    $outputFormat = array("resultElement" => "users",
                    "rowElement" => "user",
                    "elements" => array(
                                "name" => "name",
                                "age" => "age",
                                "email" => "email"));

    For this output format, our expected result payload would be like,

    <users>
      <user>
         <name>xxx</name>
         <age>23</age>
         <email>xxx@xxx.xx</email>
      </user>
      <user>
         <name>yyy</name>
         <age>23</age>
         <email>yyy@yyy.yy</email>
      </user>
      ....
    </users>
  4. Define your operation using the sql query, input and output formats.
    $operations = array("getUsersByCountry" => array(
                                  "inputFormat" => $inputFormat,
                                  "sql" => $sql_query,
                                  "outputFormat" => $outputFormat));
  5. Define your database configuration in an array like this,
    $config = array(
        "db" => "mysql", // your db engine
        "username" => "myname", // name & password for the db server
        "password" => "mypasswd",
        "dbname" => "db", // the db
        "dbhost" => "localhost");
  6. Create a DataService instance using the database configuration and the operations we just created. And call DataServices reply method.
    $ds_service = new DataService(array(
                   "config" => $config,
                   "operations" => $operations));
    
    $ds_service->reply();

That is it. You just exposed your query as a web service. The PHP script URL will be the endpoint URL for the web service.

  1. WS-Addressing Action is used by web services to dispatch the operation for an incoming request SOAP message. It is one way of dispatching operations in WSF/PHP and it base Apach Axis2/C, other ways are SOAP action based dispatching which covers in my early blog “The Use of SOAP Action with WSF/PHP“,  Body based dispatching and URI based dispatching.
  2. In WSF/PHP client, we can enable the addressing by setting the “action” field in the request WSMessage instance and setting “useWSA”=> TRUE in the WSClient instance. Here is an example.
    $requestPayloadString = <<<XML
    <ns1:echoString xmlns:ns1="http://wso2.org/wsfphp/samples">
       <text>Hello World!</text>
    </ns1:echoString>
    XML;
    
    $client = new WSClient(array("to" => "http://localhost/samples/echo_service.php",
                                 "useWSA" => TRUE));
    
    $requestMessage = new WSMessage($requestPayloadString,
    	    array("action" => "http://localhost/samples/echo_service/echoString"));
    
    $responseMessage = $client->request($requestMessage);
    
    printf("Response = %s <br>", htmlspecialchars($responseMessage->str));

    If you doesn’t set “useWSA” => TRUE explicitly, the client will send the action as a SOAP action rather than a WSA Action. The above is the same example I used in demonstrating SOAP action with just the addition of “useWSA” option.

  3. You can select WS-Addressing version among “submission” which .NET support by default and “1.0″, just by mention it in the “useWSA” field.
    I.e.

    “useWSA” => “submission” Then the namespaces are having the values defined in the submission specification of WS-Addressing.
    “useWSA” => 1.0 Then the namespace are having the values defined in the WS-Addressing 1.0 spec.
  4. In the option “actions” in the WSService, we can provide the action to operation map, so the service will direct the SOAP messages to the correct operation by looking at the WS-Addressing action of the SOAP message.
    Here is a sample code that use action to dispatch the operation.

    function echoStringFunc($inMessage) {
        // logic of echoString operation
    }
    
    function echoIntFunc($inMessage) {
        // logic of echoInt operation
    }
    
    // we will take echoString and echoInt as tow operations
    $operations = array("echoString" => "echoStringFunction",
                        "echoInt" => "echoIntFunction");
    
    // soap action to operation map
    $actions = array("http://localhost/samples/echo_service/echoString" => "echoString",
                     "http://localhost/samples/echo_service/echoInt" => "echoInt");
    
    // creating the service with the operations and actions set
    $service = new WSService(array("operations" => $operations,
                                   "actions" => $actions));
    
    $service->reply();

    Note that this code is same as the code I post in a previous blog “The Use of SOAP Action with WSF/PHP” which uses SOAP action to dispatch the operation. In fact WSService will adjust to dispatch SOAP messages, so if there is a SOAP action in the message, it will be dispatched using that, and if it contains WS-Addressing action, it will used to do dispatching without the need of writing a single additional line of code. Note that this same code for both “submission” and “1.0″ WS-Addressing versions.

  5. In a WSDL 1.1, WS-Addressing action can be declared in the message element in the portType section.
        <wsdl:portType name="echoPortType">
            <wsdl:operation name="echoString">
                <wsdl:input message="ns0:echoStringRequest" wsaw:Action="http://localhost/samples/echo_service/echoString"/>
                <wsdl:output message="ns0:echoStringResponse" wsaw:Action="http://localhost/samples/echo_service/echoStringResponse"/>
            </wsdl:operation>
            <wsdl:operation name="echoInt">
                <wsdl:input message="ns0:echoIntRequest" wsaw:Action="http://localhost/samples/echo_service/echoInt"/>
                <wsdl:output message="ns0:echoIntResponse" wsaw:Action="http://localhost/samples/echo_service/echoIntResponse"/>
            </wsdl:operation>
        </wsdl:portType>

    When you use WSF/PHP in wsdl mode, it will pick the action declared in the WSDL and set it in the SOAP message, if you have enabled the addressing for the WSClient by setting non-null value for “useWSA” option.

In SOAP 1.1, the SOAP action is mentioned as a compulsory HTTP header. In practice it was used to identify the operation for a given request which we identify by the term ‘dispatching operations’.
As of SOAP 1.2, the SOAP action field became optional, and it was part of the content type header.

In WSF/PHP, you can use the “action” property of WSMessage to set the SOAP action in a request SOAP message.

$requestPayloadString = <<<XML
<ns1:echoString xmlns:ns1="http://wso2.org/wsfphp/samples">
   <text>Hello World!</text>
</ns1:echoString>
XML;

$client = new WSClient(array( "to" => "http://localhost/samples/echo_service.php" ));

$requestMessage = new WSMessage($requestPayloadString,
	    array("action" => "http://localhost/samples/echo_service/echoString"));

$responseMessage = $client->request($requestMessage);

printf("Response = %s <br>", htmlspecialchars($responseMessage->str));

WSF/PHP send SOAP 1.2 request by default. So the SOAP message HTTP headers for the above code would be like this,

POST /samples/echo_service.php HTTP/1.1
User-Agent: Axis2C/1.5.0
Content-Length: 223
Content-Type: application/soap+xml;charset=UTF-8;action="http://localhost/samples/echo_service/echoString"
Host: 127.0.0.1

Note that the SOAP  Action is sent in the Content-Type inside the optional action parameter.

If you set it to use SOAP 1.1 explicitly in the WSClient like this,

$client = new WSClient(array( "to" => "http://localhost/samples/echo_service.php",
                              "useSOAP" => 1.1 ));

It would send the SOAP Action as a separate HTTP header,

POST /samples/echo_service.php HTTP/1.1
User-Agent: Axis2C/1.5.0
SOAPAction: "http://localhost/samples/echo_service/echoString"
Content-Length: 225
Content-Type: text/xml;charset=UTF-8
Host: 127.0.0.1

If you are using WSF/PHP in the server side, you don’t need to worry about the SOAP version as it will support both SOAP 1.1 and SOAP 1.2 automatically.
Anyway you need to declared the actions to operations map, so WSF/PHP will be able to dispatch the SOAP request and direct the message to the correct operation.

function echoStringFunc($inMessage) {
    // logic of echoString operation
}

function echoIntFunc($inMessage) {
    // logic of echoInt operation
}

// we will take echoString and echoInt as tow operations
$operations = array("echoString" => "echoStringFunction",
                    "echoInt" => "echoIntFunction");

// soap action to operation map
$actions = array("http://localhost/samples/echo_service/echoString" => "echoString",
                 "http://localhost/samples/echo_service/echoInt" => "echoInt");

// creating the service with the operations and actions set
$service = new WSService(array("operations" => $operations,
                               "actions" => $actions));

$service->reply();

So whenever a SOAP request message hit this script, it will check the SOAPAction HTTP header if it is a SOAP 1.1 message, or the action parameter in the Content-Type HTTP header if it is 1.2, then try to find the function mapped with that action from the actions map.
Anyway if the SOAP action is not set in the request message WSF/PHP will try to dispatch the message with other methods like body based dispatching, WS-Addressing based dispatching which are transport protocol independent dispatching mechanisms.

Anyway since most of the cases SOAP is used on top of HTTP, it is really common SOAP messages are dispatched using the SOAP action which is no doubt a very easy and efficient way of dispatching.

Database plays a big role in any day-today application. It is a major component from accounting, web portal, CMS, SaaS applications, search engines to all enterprise applications. In traditional MVC(Model-View-Controller) applications we talk about the Model component which represent the database.. And in 3-tier architectural pattern it is the ‘Data layer’ which represent the database and provide data to the ‘Logic Layer’ as per request.

As SOA evolves, database is becoming just one part of the ‘Data Layer’ or the ‘Model’ component. There are many data sources, data providers which are mostly deployed as web services which you can ask for data as you do with traditional databases. This gives you the advantages of the use of web services like interoperability, security and reliability and other WS-* support. And it helps you to get rid of the headache of installing different drivers for different databases and most importantly it removes the tight binding of your application to the database. So with adaption of SOA, the ‘Data Layer’ has been replaced by the ‘Service Consuming Layer’.

The story of the ‘Data Provider’ also changed with the SOA adaption. The new data sources are designed with the SOA in mind. And the legacy systems are wrapped by a service layer to make it more easier to consume. We use the term ‘Data Services’ for the data sources deployed as web services.

There are many public data services available as ‘REST’ which is a lighter way of providing services. The other way is the use of WS-* features like WS-Security, WS-Reliable Messaging to deploy the data services which are mostly adopted by the enterprise.

Today there are many tools around, that helps you to develop data services from existing databases. WSO2 provides an open source data service framework that allows you to build data services from simple xml configuration files without the need of writing a single line of code. And WSF/PHP also packed with a data services library that allow you to write a simple PHP script to build a data service.

PHP is one of the favorite language to write database back-ended web applications. Specially considering the fact that there are thousands of servers powered by the LAMP (Linux  + Apache + MySQL +PHP) stack, PHP data service library would comes useful to build around a web service around these existing data sources and make them SOA-aware.

November 19th, 2008WS-SecurityPolicy With PHP

WS-SecurityPolicy specification defines standards for defining security policies for your web service. WSF/PHP allows you to declare your security policies according to these standards.

You can take one of following approaches to associate policies to your web service or client.

  • PHP Array to represent your policies
  • Policy file compliant with WS-Security Policy.
  • Declaring policies inline with the WSDL.

Declaring Policies with a PHP Array

This is a WSF/PHP specific API to declare policies for a web service. You don’t need to learn WS-Security Policy to write policies with this approach. You can set whether you want to use encryption, signing or usernameToken in a PHP array and create a WSPolicy object using it.

// here is the security array to declare your policies in simple manner
$sec_array = array("encrypt" => TRUE,
 "algorithmSuite" => "Basic256Rsa15",
 "securityTokenReference" => "IssuerSerial");

// creating WSPolicy instance using the policy array
$policy = new WSPolicy(array("security"=> $sec_array));

You can use this policy object to create a service along with a WSSecurityToken which contain the user parameters like the server private key and the client certificate.

$sec_token = new WSSecurityToken(array(
 "privateKey" => $server_pvt_key,
 "receiverCertificate" => $client_pub_key));

$svr = new WSService(array("actions" => $actions,
 "operations" => $operations,
 "policy" => $policy,
 "securityToken" => $sec_token)); // here is the policy object you just created

$svr->reply();

You can invoke this service just by writing a simple web service client. There also you need to provide the policies declared in the service, so the client can build his request to validate with server policies. You will be using a similar WSPolicy object to set these policies at the client side too, as show in the below code segment.

 $sec_token = new WSSecurityToken(array(
   "privateKey" => $pvt_key,
   "receiverCertificate" => $rec_cert));

 $client = new WSClient(array("useWSA" => TRUE,
    "policy" => $policy, /* the policy object */
    "securityToken" => $sec_token));

 $resMessage = $client->request($reqMessage);

Declaring Policies with a Policy File

You can set your policies in the server or client side using a policy file compliant with WS-Security Policy specification. You have to take this approach if your policy requirements are too complicated, like you want to sign only some parts of the message or you want to encrypt some soap headers.

Similar to the above method, here too you will use the WSPolicy object to set your policies. But unlike the above where you give the policies as a PHP array , here you can just give the policy file as an argument to the WSPolicy constructor.

// creating the WSPolicy instance from a policy file
$policy_xml = file_get_contents("policy.xml");
$policy = new WSPolicy($policy_xml);

Here is an example of a complete policy file written according to the WS-Security Policy standards. And you can find a quick guide on WS-Security Policy from the article Understanding WS-Security Policy Language written by Nandana, a key leader of Apache Rampart project.

Declaring Policies inline in a WSDL

We use WSDL to describe our web services. WSDL has the information about the service endpoint, the transport protocols (e.g. http), messaging protocols (e.g. SOAP) and the message schemas and many others about the service. You can attach your policies inside a WSDL.

Here is an example of a WSDL with inline policies. The difference in this approach is you can set your policies separately for each messages or each operations or each endpoints of your service. The following segment of a WSDL shows how you refer to different policies which are declared in the early part of the WSDL.

     <wsdl:binding name="CalendarSOAP12Binding" type="ns1:CalendarPortType">
       <!-- Endpoint policies are declared here.
          these are common to all messages transferring
          through this protocols (i.e. SOAP12, http)-->
        <wsp:PolicyReference URI="#transport_binding_policy"/>
        <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <wsdl:operation name="login">
           <soap12:operation soapAction="urn:login" style="document"/>
           <wsdl:input>
              <!-- policy specific to the 'login' operation -->
              <wsp:PolicyReference URI="#username_token_policy"/>
              <soap12:body use="literal"/>
           </wsdl:input>
           <wsdl:output>
              <soap12:body use="literal"/>
           </wsdl:output>
         </wsdl:operation>
         <wsdl:operation name="register">
            <!-- no specific policies are set for the 'register' operation
            <soap12:operation soapAction="urn:register" style="document"/>
            <wsdl:input>
              <soap12:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
               <soap12:body use="literal"/>
            </wsdl:output>
         </wsdl:operation>
           ....
       </wsdl:binding>

This is the binding section of a WSDL where we bind messaging protocol and transport protocols with a service endpoint. Here we have “login” and “register” operations. Note that we are referring to “transport_binding_policy” from the parent level of each operation elements. That means these policies are common to all the operation in that binding. And inside the “login” operation we are referring to “username_token_policy”, so in order to invoke this operation, you have to send username token headers. And “register” doesn’t require any operation specific policies allowing users to register without any prior authentications.

You can select any of the above mentioned approach to define policies of your web service or to invoke a web service that support WS-Policy. If your policy requirements are simple, it will be easy to use the array based approach. If your policy requirements are complex or you have a good understanding of WS-Policy and WS-Security Policy you can rely on the policy file based approach or defining policy inline with WSDL. And the former 2 methods will give you a nice separation of the logic code and security configurations. The selection is yours:)

Non-Repudiation and Integrity are two main security issues addressed by signing a message. If you are writing a web service or a service consumer in PHP you can use the WSF/PHP toolkit to sign messages.

Here is how you can sign a SOAP request message.

// loading the keys
$my_cert = ws_get_cert_from_file("client_certificate.cert");
$my_key = ws_get_key_from_file("client_private_key.pem");
$rec_cert = ws_get_cert_from_file("server_certificate.cert");

// preparing the policy array
$sec_array = array("sign"=>TRUE,
                   "algorithmSuite" => "Basic256Rsa15",
                   "securityTokenReference" => "IssuerSerial");

$policy = new WSPolicy(array("security"=>$sec_array));

// preparing the security_token
$sec_token = new WSSecurityToken(array("privateKey" => $my_key,
                                       "certificate" => $my_cert,
                                       "receiverCertificate" => $rec_cert));

// create the client using the policy and sec token + option ws-addressing
$client = new WSClient(array("useWSA" => TRUE,
                              "policy" => $policy,
                              "securityToken" => $sec_token));

// invoke the request				
$resMessage = $client->request($reqMessage);

Although I’ve not shown in the above code, you should always wrap the requesting code inside a try/catch block specially when there are security tokens in your message. Because whenever security test is failed the server will send a SOAP fault, and the client is expected to handle that.

Similarity you can write a service that accept only signing messages with the same policy and security tokens. But this time your security token contains the private key and the certificate of the server. And for service you create WSService instance (similarity to  WSClient) and feed these options.

// loading the keys and create the security token
$cert = ws_get_cert_from_file("server_certificate.cert");
$pvt_key = ws_get_key_from_file("server_private_key.pem")

$sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
                                       "certificate" => $cert));

// policy is declared similar to earlier example

$sec_array = array("sign" => TRUE,
                    "algorithmSuite" => "Basic256Rsa15",
                    "securityTokenReference" => "KeyIdentifier");

$policy = new WSPolicy(array("security"=>$sec_array));

// create a WSService instance and feed the options
$svr = new WSService(array("operations" => $operations,
                           "actions" => $actions,
                           "policy" => $policy,
                           "securityToken" => $sec_token));

$svr->reply();

With this code you will only be able to sign the payload of a SOAP request. Payload is what stays inside the soap body part which contain the actual data of your business. So in most of the case, what you want is integrity of your business data, so this will satisfy your need.

But there can be situation you want to sign SOAP headers which are outside the SOAP body. SOAP headers contain meta data of the message. For an example when you sign a message, the SOAP headers could carry the signature information and the certificates.

One example of the use of signing SOAP headers is replay detection. (I’ve written about replay detection on a early blog). in SOAP, replays can be detected using ‘message id’s which are transferred through the WS-addressing SOAP headers. But interceptors can change the message ids and send it as a unique message which will eventually cause to fail the replay detection. But if you sign the ‘message id’ (i.e. WS-Addressing headers) the intruders will not be able to do the trick since they can not recreate the signature for the changed content.

Anyway you can not set your requirement to sign the soap headers, just using a PHP array as it has done in the above examples. Rather you have to declare it using a policy file compliant to WS-SecurityPolicy specification. I.e instead of using a PHP array to create a WSPolicy instance like this,

$sec_array = array("sign"=>TRUE,
                    "algorithmSuite" => "Basic256Rsa15",
                    "securityTokenReference" => "IssuerSerial");

$policy = new WSPolicy(array("security"=>$sec_array));

You can create the WSPolicy instance directly using the policy file,

$policy_xml = file_get_contents("policy.xml");
$policy = new WSPolicy($policy_xml);

And inside the policy file, you can declare your requirements, policies. Here is how I say I want to sign all my WS-Addressing headers.

<sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
   <sp:Header Namespace="http://www.w3.org/2005/08/addressing"/>
</sp:SignedParts>

Note that here the namespace “http://www.w3.org/2005/08/addressing” is the namespace used in the Addressing headers. So I instruct to sign headers containing that namespace in order to sign the ‘message id’ inside the WS-Addressing headers. You can visit a comlete policy file with the declarations of signed part from here.

Replay attack is a common kind of attack, the hackers are using to break the security of a web service.

If you can intercept one soap message while it is transferring through the wire, you can replay that message to the server again and again. Since the original message may have already encrypted, signed and contain valid authentication credentials the replaying messages will also be able to pass all the security tests and fool the server and do enormous damages to the business. For an example think of replying a soap message that a client is conducting a payment with a e-commerce service. The service may tend to charge the client multiple times for each request which break the integrity of the business.

So it is no doubt when ever you thinking of designing an enterprise web service application, you should give attention to ‘Replay Detection’ more seriously.

One solution is you can handle the replay detection inside the business logic itself. If you do this for the above mentioned e-commerce like services, you will keep all the session ids and make sure only one payment is possible for one session. But this may need some really careful design of the application logic.

The other solution is to let your web service framework to handle the ‘Replay Detection’. That will clearly separate the security aspects from the business aspects of your service. And it will give you more flexibility in configuring your security requirements. And the other advantage is it will detect ‘Replay Attacks’ well before hitting the business logic, making the web service perform well.

WSF/PHP allows you to detect replay attacks using WS-Addressing and WS-Username token headers. WS-Addressing headers contains a message id which can be considered as unique to a soap message and ws-security headers contains created time of the message which can be used to calculate the age of the message and derive its validity.

WSF/PHP provide web service developer a callback with the ‘message id’ and the ‘message created time’  per each message. In the callback you can store this message id and created time in a database, and check them against all the incoming soap messages. If it found duplicate entries, you can consider it as a replay attack.

Here is an example draft of the above scenario written using WSF/PHP.

/* replay detection callback */
function replay_detect_callback($msg_id, $time_created) {
    /* Here is the pseudo code of the logic

    query for the $msg_id and $time_created for the database.
    if already exist
      return FALSE;
    else
      Insert message id and time created to the database
    return TRUE */
}

$security_token = new WSSecurityToken(array( 
                      "replayDetectionCallback" => "replay_detect_callback",
                      "enableReplayDetect" => TRUE,
                      /* Other tokens */ ));

$svr = new WSService(array("securityToken" => $security_token,
                      /* Other options*/ ));

$svr->reply()

So if you use this code in the service you can happily detect any replays of an old message and avoid possible damages. But remember this security check will work only if the replaying message contain the same message id and the created time as the original one. It is possible that an intruder not only intercept the message, but also change these fields and replay it in to your server. Such replays will not be detected from this code.

The solution is to sign each SOAP requests. If a client sign a message with his private key, the server can confirm that the message is not altered while it is on the wire. So if intruder replay a signed message, either it will fail the replay detection test (if it is replaying without changing the message id and created time) or it will fail validating the signature (if the message id and the created time is altered).

So if you implement a replay detection test with a signature test, you can eliminate all the replay attacks to your service (at least theoretically :) .

November 16th, 2008RESTful URL Mapping in WSF/PHP

In a RESTful design, choose of URLs for resources are really important. The URL uniquely represents a resource. Service consumers can change some parts in the URL to access different other resources. So it is clear that the URL consists of some constant parts which describe the resource group or catalog in general and some variable parts which have different and unique values for different resources.

As an example look at the following URL patterns

  • students/{name} – The constant ‘students’ represent the students group in general and the variable ‘name’ is used to identify each student individually.
  • students/{name}/marks/{subject} – The constants ‘students’ and ‘marks’ shows that this resource is a marks of some students, The two variables ‘name’ and ‘subjects’ addresses which student and marks of which subject is presented in the URL.

You can have a look at some of the uses of such mappings from RESTful School demo.

WSF/PHP allows you to create RESTful Web Services and further more RESTful Data Services in PHP.

In a RESTful Data Service you expose a database query as a web service. There you can write a prepared statement and feed arguments for the statement through the variable parameters of the URL. For an example take the RESTfulSchool Demo Code.

To retrieve a particular student, we can use the following prepared statement and the URL pattern ( This URL Pattern+  HTTP ‘GET’ method is matched to execute this query. )

$sql = "SELECT * FROM Students where StudentName = ?"

$get_students_with_name_url = array("HTTPMethod" => "GET",
                 "RESTLocation" => "students/{name}");

So you can execute this prepared statement with the subject name ‘John’ using the following URL.

http://labs.wso2.org/wsf/php/solutions/RESTFulSchool/school_service.php/students/john

If your service is not exposing the database directly, then you have to choose the general web service API rather than the data service specific API. In there you will be able to write your business logic for publishing student information in a PHP function and expose it as a web service.

In such a cas,e your function is taking an argument which of type WSMessage. This structure hold an XML that contains values for all the variable parameters as in the users request URL. For an example for above REST Mapping, we can expect the following XML.

<getSubject>
    <name>Chemistry</name>
</getSubject>

And your function to expose as a service, would be look like this,

function getSubject($input) {
    /* retrieve the subject name from the
       $input xml using simple xml */

    $input_xml = new SimpleXMLElement($input->str);

    $subject_name = $input->name;

    /* write the logic to retrieve subject information
      for the $subject_name */

    ....
}

As many of other languages, XML schema too have data types. Basically it can be categorized in to two groups.

  1. Simple Types
  2. Complex Types

The different between these 2 types are so easy to identify. Say you have a schema element with a simple type like this.

<xs:element name="xx" type="someSimpleType"/>

Then a possible xml to validate against this schema is

<xx>Bingo</x>

Inside the ‘xxx’ you can only find texts, But in a case of a  schema element with complex types,

<xs:element name="xx" type="someComplexType"/>

The XML will be set of nested elements.

<xx>
  <yy>hi</yy>
  <zz>Nested elements</zz>
</xx>

Simply complex type contains elements with simple types or complex types similar to the class types in OOP languages which contain member variables with different types.

But unlike most of the other languages, in schema simple types are not always primitive types. There can be user derived simple types as well.

I will talk about each of these variations in schema types and their PHP representation of WSDL2PHP tool.

  • Primitive Types & Built-in Types
  • List Types
  • Union Types
  • Faceted Types

Primitive Types & Built-in Types

The XML schema specification talks about 19 primitive types. Some of the common of these simple types and the corresponding PHP types generated by WSDL2PHP tool are,

Schema Type PHP Type
string string
boolean boolean
decimal float
float float
double double
duration string
dateTime string (from above wsf/php 2.0 it is an integer – representing timestamp)

You may have noticed the famous types like ‘integer’, ‘byte’, ‘short’ are missing out of the above list. In fact these types exist, but they are not primitive types. They are derived from decimal (special cases when the fraction digits are 0 :) . Anyway still these types are considered built-in types in xml schema. Here is the complete list of built-in types (both primitive and non-primitive built-in types)

‘integer’, ‘byte’ and ‘short’ are mapped to PHP integer types by the WSDL2PHP tool.

To show an example how WSDL2PHP tool generating code for simple types, I will take the following schema Element.

<xs:element name="myName" type="xs:string"/>

And WSDL2PHP will generate a simple public variable (Assuming this is generating inside some class) for that element with a comment to describing it.

    /**
     * @var string
     */
     public $myName;

So if I specified some value for the variable (Taking the variable name of the parent class is $parent),

$parent->myName = "James";

I will get the following nice XML.

<myName>James</myName>

This is the same approach for all the other built-in types as well.

List Types

The list types are derived from list of simple types. It still a simple type because it list all the element in the list as a white-space separated text inside the parent element.

Here is how you may declare a list type in an xml schema.

<xs:simpleType name="mylist">
    <xs:list itemType="xs:int"/>
</xs:simpleType>

<!-- example element to have the above list value -->
<xs:element name="xCoordinates" type="tns:mylist"/>

Then the WSDL2PHP generated PHP code would be something like,

    /**
     * @var array of int
     */
    public $xCoordinates;

As the comment implies, you have to feed the values in an php array.

$parent->xCoordinates = array(1, 3, 8, 7, 9);

And Here is the XML you are getting,

<xCoordinates>1 3 8 7 9</xCoordinates>

Similarly you can have list of any simple types regardless whether it is built-in or derived.

Union Types

The union in schema is similar to the unions in ‘C’. In C variables of a union can have one (and not more than one) of the types declared inside the union type. Similarly in schema elements with union types can have one and only one simple type among the member types declared in the union. Here is an example of declaring union type,

<xs:simpleType name="myIntStringUnion">
    <xs:union memberTypes="xs:int xs:string"/>
</xs:simpleType>

<!-- example element to have the above union value -->
<xs:element name="garbage" type="tns:myIntStringUnion"/>

And the WSDl2PHP generated code,

    /**
     * @var int/string
     */
    public $garbage;

So if you read the comment you can have an idea that this variable accept either ‘in’ or ‘string’ type without digging in to the WSDL.

Faceted Types

You can apply facets and restrict the value space of a simple type.

  • length
  • minLength
  • maxLength
  • pattern
  • enumeration
  • whiteSpace
  • maxInclusive
  • maxExclusive
  • minExclusive
  • minInclusive
  • totalDigits
  • fractionDigits

Some facets are specific to some built-in data types or types derived from these built-in datatypes. Here is a table of valid facets for each of the above mentioned catagory of types.

I have earlier blogged about how you work on faceted types in PHP in the post “Coding Schema Inheritance in PHP“. So Here I will list out a similar example  to demonstrate the PHP generated code by WSDL2PHP tool for facets.

Here is a schema with the ‘enumeration’ facet.

<!-- applying enumeration facet to the xs:string -->
<xs:simpleType name="mySubject">
    <xs:restriction base="xs:string">
         <xs:enumeration value="Maths"/>
         <xs:enumeration value="Physics"/>
         <xs:enumeration value="Chemistry"/>
    </xs:restriction>
</xs:simpleType>

<!-- example element to have the above faceted value -->
<xs:element name="subject" type="tns:mySubject"/>

And here is the WSDL2PHP generated code,

    /**
     * @var string
     *     NOTE: subject should follow the following restrictions
     *     You can have one of the following value
     *     Maths
     *     Physics
     *     Chemistry
     */
    public $subject;

It clearly says your variable ‘subject’ is only allowed to have the list of values mentioned in the ‘enumeration’.

So I just wrote about some variations of simple schema types and how they are represented in the WSDL2PHP generated code. As you may have observed, you don’t need to know any thing about the WSDL or the schema to write a PHP code around that, since WSDL2PHP gives you a generated code mentioning all the guidelines, restrictions about using them.

November 11th, 2008WSF/PHP Test Cases Explained

WSO2 WSF/PHP comes with a comprehensive set of test cases. It covers the most of the basic/concrete scenarios supported by WSF/PHP. You can find these test cases inside the “src/tests” directory of WSF/PHP source package. Or you can find the latest test-suite from the SVN location.

Here are some aspects covered in the test-suit.

Scenario Test Cases For Client Test Cases For Service
Basic Functionality echo_client*.phptmath_*.phpt samples/echo_service*.php
samples/math_service.php
Basic Schema Types BasicDataTypes/*.phpt services/BasicTypesDoclitBSvc/*.php
Complex Schema Types cmplxDataTypes/*.phpt services/ComplexDataTypesWSvc/*.php
services/ComplexDataTypesBSvc/*.php
WSDL/Schema Variations wsdl_mode/*.phpt services/wsdl_mode/*.php
WSDL Generation with Annotations wsdl_generation/*.phpt services/wsdl_generation/*.php
Reliable Messaging echo_rm*.phpt samples/echo_service_rm*.php
Security echo_encrypt_client*.phptecho_signing_client*.phpt

echo_timestamp_client*.phpt

echo_username_token_client*.phpt

encrypt_service*.phpsigning_service*.php

timestamp_service*.php

username_token_service*.php

MTOM mtom_*.phpt samples/mtom/*.php

(Note that Here ‘*’ is used as a wild card represent 0 or many characters)

Steps to Run Tests

  • First you need to install WSF/PHP correctly. Please read the WSF/PHP installation manual for that.
  • You have to have the ‘pear’ utility tool comes with PHP. And add this to the PATH environment variable.
  • Copy the samples and src/tests/samples/services directory (paths are relative to the root directory of the wsf/php package) to the web root directory.
  • Then go to the src/tests directory and execute the following command.
    pear run-tests -r

    This will execute all the test cases under the ‘tests’ directory and finally give a summery of the test results.

  • You can run individual test cases separately by providing the relative path to the test case from the ‘test’ directory. E.g. To run the echo_client.phpt test case, you may type
    pear run-tests samples/echo_client.phpt

If you like to add test cases for the WSF/PHP scenarios, follow this comprehensive guideline titled Writing Simple phpt Test Scripts For PHP Web Services.


© 2007 Dimuthu’s Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress