Thursday, October 14, 2010

Zend_Soap_Client and PHP http client do not share same native code

I was told by a senior PHP consultant that he can not successfully make Zend_Soap_Client call over the SSL. He runs his code on Zend Server running on i Series. Then, I told him that, if I were him, I will try to write a normal HTTP client to access the WSDL file over SSL first. The reason for me to approach in this way because,

  • SOAP is built on the top HTTP. So, SSL should not be business of SOAP. In other words, SOAP client shall rely on the HTTP client implement.
  • I THOUGHT that Zend_Soap_Client uses same native code as PHP http client.

However, I was wrong. He tested and found that he was able to do file_get_contents and retrieve https:// content from the server. Then, I started to trace the source code of Zend_Soap_Client. I found,
  1. Zend_Soap_Client is a wrapper around PHP SOAP extension.
  2. PHP SOAP is a thin layer. It quickly goes to native code at file called soap.php.
  3. Native c code of soap.php has its own logical to deal with proxy and SSL. Click Here to see C source code.
So, I was wrong. I can not regard that Zend_Soap_Client can support SSL just because PHP Http client can support SSL. They probably uses different version of native http client code. From this discussion, I get deeper understanding about architecture of PHP. I will probably write a article to compare Java and PHP.

BTW, PHP 5.3 version of Zend Server 5.0.2 on iSeries does not support Zend_Soap_Client to access web service over the SSL. However, Zend_Soap_Client work with SSL when the Zend Server was rolled back to version 5.0.1 . Zend_Soap_Client coming with Zend server 5.0.4 can be used to access Web service over one-way SSL. I have not tried mutual SSL with Zend_Soap_Client yet. But, it sounds not easy.

Also, to test Zend_Soap_Client access over one-way SSL can be as simple as below,

try {
 $soap_url = 'https://your.host/webservice?wsdl'; 
 
 $soap_client = new SoapClient ( $soap_url);
 
 var_dump ( $soap_client -> __getFunctions () );
 
} catch ( Exception $e ) {
 print_r ( $e );
 exit ();
}

Friday, October 8, 2010

A simple tutorial about creating Zend Framework SOAP Web service server in Zend Studio 7.2

I was studying how to create Web Service in PHP recently. I used Axis1.0, Axis2.0 to create Web service before. I used NuSoap to make a web service to call Sync4J admin service too. I like Axis' wsdl2java and java2wsdl tool set very much. In PHP, there is similar tool too. However, this time, I decide to implemented Web service under Zend Framework. After trying, I found it is quiet easy to create a simple Web service in ZF although I found that Zend_Soap_Server is not sophisticated enough to make solution for Enterprise application. For example, it does not have build in support for WS security head. It is actually a wrapper around PHP Soap extension. You can find discussion from Zend wiki by click here.

I will show steps of creating ZF based Web Service in Zend studio as below. Also, I put download link of whole ZS project at the end. We can create this simple demo project as below,
  1. Create a new ZF project in Zend Studio and name it as jiaWS.
  2. Add autoloaderNamespaces[] = "Jia_" in application.ini to allow Zend Autoloader load Jia_* classes.
  3. Creating a action class DummyController under directory /application/controllers. 
  4. creating a function soapAction() in DummyController .
  5. creating a function wsdlAction() in DummyController. 
  6. Creating a Jia_Hello class with sayHello() function under /library/Jia directory.
  7. Creating a Jia_DummyException class under /library/Jia directory for Soap fault processing.
It is done! You need to pay attention on how to specify web service URI in the code if you use different project name and function names. See key source code as below,

Jia_Hello class.
/**
 * This class contains function which will be used by Web service caller.
 * All business logics will be implented or called in these functions.
 * 
 * @author Yiyu Jia
 *
 */
class Jia_Hello {

 /**
  * This function simply return a String when it is called.
  * Note :  the PHPDoc style comment below is very 
  * important as Zend_Soap_AutoDiscover use it to generate WSDL. 
  * 
  * @return string
  */
 public function sayHello(){
  return "hello";
 }
}

DummyController calss
/**
 * This class includes two functions. soapAction() is point called by
 * Web service client. wsdlAction() generates WSDL when it is called.
 * 
 * @author yiyu
 *
 */
class DummyController extends Zend_Controller_Action
{
 /**
  * SOAP action named as soap.
  */
 public function soapAction() {
  // disable layouts and renderers
  $this->getHelper ( 'viewRenderer' )->setNoRender ( true );
  
  // initialize server and set URI
  $server = new Zend_Soap_Server('http://localhost/jiaWS/public/index.php/dummy/wsdl');
  
  // set SOAP service class
  $server->setClass ( 'Jia_Hello' );
  
  // register exceptions for generating SOAP faults
  $server->registerFaultException ( array ('Jia_DummyException' ) );
  
  // handle request
  $server->handle ();
 
 }
 /**
  * function to generate WSDL.
  */
 public function wsdlAction() {
  
  //You can add Zend_Auth code here if you do not want 
  //everybody can access the WSDL file.
 
  // disable layouts and renderers
  $this->getHelper ( 'viewRenderer' )->setNoRender ( true );
  
  // initilizing zend autodiscover object.
  $wsdl = new Zend_Soap_AutoDiscover ();
  
  // register SOAP service class
  $wsdl->setClass ( 'Jia_Hello' );
  
  // set a SOAP action URI. here, SOAP action is 'soap' as defined above.
  $wsdl->setUri ( 'http://localhost/jiaWS/public/index.php/dummy/soap' );
  
  // handle request
  $wsdl->handle ();
 }

}


DummyClient.php
//This code is not a ZF MVC based
//So, we need to load Zend libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass ( 'Zend_Soap_Client' );

//setting options past into Zend_Soap_Client.
$options = array ('location' => 'http://localhost/jiaWS/public/index.php/dummy/soap', 'uri' => 'http://localhost/jiaWS/public/index.php/dummy/wsdl' );

try {
 //Initilizing client object
 $client = new Zend_Soap_Client ( null, $options );
 //calling the Web service function.  
 $result = $client->sayHello ();
 print_r ( $result );
} catch ( SoapFault $exp ) { //catching exception and print out.
 die ( 'ERROR: [' . $exp->faultcode . '] ' . $exp->faultstring );
} catch ( Exception $exp2 ) {
 die ( 'ERROR: ' . $exp2->getMessage () );
}

Source code as Zend Studio project can be downloaded click here

You can find a simple RESTful Web Service demo on my another post, creating RESTful service with Zend_Rest_Route and Zend_Rest_Controller.