Showing posts with label Zend Framework. Show all posts
Showing posts with label Zend Framework. Show all posts

Friday, July 1, 2011

ExtJS 4 REST proxy and Zend framework Zend_Rest_Route are incompatible.

I noticed a very strange thing when I worked on Ext JS 4 v4.0.2 and Zend Framework. I find that every time when Ext JS grid sends new created Record to the server through its REST proxy, the POST method was teated as PUT method in Zend_Rest_Route. This cause troublesome because my code have to know if the incoming record a new one for insert or exsiting one for update. Otherwise, I have to use some UPSERT SQL statement to UPDATE or INSERT record into Database table. Unfortunately, I use DB2 for i5/OS V5R4, which does not have built-in UPSERT command and it even does nor support MERGE statement.

I do not want to walkaround this by doing SELECT first and judging if the code need to run INSERT or UPDATE statement. So, I traced the Ext JS 4 code and Zend Framework 1.11 code. I found that these two are not compatiable. I am not sure if we can say it is BUG as this wont happen if either side change their code. Or, both side can change the code to avoid the confilicaton if we say bugs are from both sides.

The key for this issue is that Ext JS 4 append record's id at the end of base URL, which points to RESTful service. Below is a psuedo POST URL create by Ext JS 4 REST proxy. I highlight the unexpected friend in red color.

http://hostname/yourRestServiceURI/yourRecordId/?dc=randomNum


We understand that adding record id as part of URI can allow server side script easily find it. But, why this design is implemented in a general SDK like Ext JS? Especially, I do not see good reason for a POST request to have it as record in POST are suppoe not to have been existing on server side. Therefore, the ID of the new record might not have been generated if it will be generated on the server side only.

In Ext JS 4 source code \src\data\proxyRest.js, we can see what happens,
/**
     * Specialized version of buildUrl that incorporates the {@link #appendId} and {@link #format} options into the
     * generated url. Override this to provide further customizations, but remember to call the superclass buildUrl
     * so that additional parameters like the cache buster string are appended
     */
    buildUrl: function(request) {
        var me        = this,
            operation = request.operation,
            records   = operation.records || [],
            record    = records[0],
            format    = me.format,
            url       = me.getUrl(request),
            id        = record ? record.getId() : operation.id;
        
        if (me.appendId && id) {
            if (!url.match(/\/$/)) {
                url += '/';
            }
            
            url += id; //Why add ID here even for POST? Yiyu Jia
        }
        
        if (format) {
            if (!url.match(/\.$/)) {
                url += '.';
            }
            
            url += format;
        }
        
        request.url = url;
        
        return me.callParent(arguments);
    }
}

Now, people will ask why adding id in URL cause the problem in Zend Framework? Well, let's trace Zend Framework code now.

On Zend Framework API document about Zend_Rest_Route, we can see Zend clearly described their design how RESTful URI should looks like,
Zend_Rest_Route Behavior
MethodURIModule_Controller::action
GET/product/ratings/Product_RatingsController::indexAction()
GET/product/ratings/:idProduct_RatingsController::getAction()
POST/product/ratingsProduct_RatingsController::postAction()
PUT/product/ratings/:idProduct_RatingsController::putAction()
DELETE/product/ratings/:idProduct_RatingsController::deleteAction()
POST/product/ratings/:id?_method=PUTProduct_RatingsController::putAction()
POST/product/ratings/:id?_method=DELETEProduct_RatingsController::deleteAction()

As we can see, Zend Framework does not expect any thing attached to method URI when PHP programmer intend to use POST method. In Zend_Rest_Route source code, we can see a case switch statement as below. You will see when the POST method will be "magically" routed to PUT method if pathElementCount is larger than zero.

switch( $values[$this->_actionKey] ){
                    case 'post':
                        if ($pathElementCount > 0) {
                            $values[$this->_actionKey] = 'put';
                        } else {
                            $values[$this->_actionKey] = 'post';
                        }
                        break;
                    case 'put':
                        $values[$this->_actionKey] = 'put';
                        break;
                }


But, where is the $pathElementCount set? In the same source code file, we can see below statement,

//Store path count for method mapping
$pathElementCount = count($path);

So, where is value of $path populated? Still, in the same source code, there is a function call match. Part of it is cited as below.

/**
     * Matches a user submitted request. Assigns and returns an array of variables
     * on a successful match.
     *
     * If a request object is registered, it uses its setModuleName(),
     * setControllerName(), and setActionName() accessors to set those values.
     * Always returns the values as an array.
     *
     * @param Zend_Controller_Request_Http $request Request used to match against this routing ruleset
     * @return array An array of assigned values or a false on a mismatch
     */
    public function match($request, $partial = false)
    {
        if (!$request instanceof Zend_Controller_Request_Http) {
            $request = $this->_front->getRequest();
        }
        $this->_request = $request;
        $this->_setRequestKeys();

        $path   = $request->getPathInfo();
        $params = $request->getParams();
        $values = array();
        $path   = trim($path, self::URI_DELIMITER);

Ok, now, it is not the end of code tracing yet. Where is the getPathInfo() function? It is in Zend_Controller_Request_Http. The following code citation will show us that getPathInfo() eeturns everything between the BaseUrl and QueryString and deos something special.

/**
     * Returns everything between the BaseUrl and QueryString.
     * This value is calculated instead of reading PATH_INFO
     * directly from $_SERVER due to cross-platform differences.
     *
     * @return string
     */
    public function getPathInfo()
    {
        if (empty($this->_pathInfo)) {
            $this->setPathInfo();
        }

        return $this->_pathInfo;
    }

/**
     * Set the PATH_INFO string
     *
     * @param string|null $pathInfo
     * @return Zend_Controller_Request_Http
     */
    public function setPathInfo($pathInfo = null)
    {
        if ($pathInfo === null) {
            $baseUrl = $this->getBaseUrl(); // this actually calls setBaseUrl() & setRequestUri()
            $baseUrlRaw = $this->getBaseUrl(false);
            $baseUrlEncoded = urlencode($baseUrlRaw);
        
            if (null === ($requestUri = $this->getRequestUri())) {
                return $this;
            }
        
            // Remove the query string from REQUEST_URI
            if ($pos = strpos($requestUri, '?')) {
                $requestUri = substr($requestUri, 0, $pos);
            }
            
            if (!empty($baseUrl) || !empty($baseUrlRaw)) {
                if (strpos($requestUri, $baseUrl) === 0) {
                    $pathInfo = substr($requestUri, strlen($baseUrl));
                } elseif (strpos($requestUri, $baseUrlRaw) === 0) {
                    $pathInfo = substr($requestUri, strlen($baseUrlRaw));
                } elseif (strpos($requestUri, $baseUrlEncoded) === 0) {
                    $pathInfo = substr($requestUri, strlen($baseUrlEncoded));
                } else {
                    $pathInfo = $requestUri;
                }
            } else {
                $pathInfo = $requestUri;
            }
        
        }

        $this->_pathInfo = (string) $pathInfo;
        return $this;
    }

So, what is the conclusion? The conclusion is that both Ext JS and Zend framework has their own design of RESTful URI pattern. Unfortunately, they are not compatible with each other. They are too nice to do more than developer expected. As libraries vendor, they maybe should do less sometimes in order to make their library as general as possible. Ext JS, as a pure client side JavaScript library, should not guide server side script designer how to implement RESTful Web service. Zend Framework, as a pure server side script library, should not make design for client side application either. In fact, RESTful Web service is a set of guide lines for developer to make scalable application over HTTP methods. But, it does not define how the URI pattern must be.

Saturday, December 18, 2010

Using Zend Controller Plugin to implement login module for RESTful Web Service

When I am designing my RESTful WS. I need to implement a login module to authenticate incoming request. Normally, in a Zend Framework based CMS project, login module is implemented as a login Action in controller. However, what I am making is a RESTful web service. I just do not want to waste CPU cycle to process any request which is not authorized to access my service. Also, RESTful web service has no requirement about MVC. In a RESTful web service, there is no even session, which is used to keep status of http request. RESTful Web service is stateless.

It is not necessary to implement my RESTful web service on the top of Zend Framework as RESTful Web service is just a simple map between CRUD operations and HTTP verbs. But, Zend Framework has handy classes like Zend_Rest_Controller and Zend_Rest_Route. Using these two classes, I can easily to implement RESTful Web service having URI template as http://hostname:port/restService/paraName1/paraValue1/paraName2/paraValue2 . In Zend Framework, there are two design patterns implemented. One is front controller design pattern. The other is MVC design pattern. Since RESTful web service is stateless and it does not need MVC pattern, implementing login module in a controller plugin module will be best seamless solution for RESTful Web service in Zend Framework. Only Zend Framework front controller pattern is involved in the processing of RESTful web service.

I regard my login module as a filter, which is well known concept in Java servlet. For my RESTful Web service, I put this filter in function void Zend_Controller_Plugin_Abstract::routeStartup (Zend_Controller_Request_Abstract $request) . Therefore, the authentication process happens even before the action dispatch will happen. Below is a simple version diagram to show the position of RESTful login module in Zend Framework dispatch process.

Sunday, December 12, 2010

Zend Framework resource plug in and avoid using Zend_Application_Resource_Multidb in wrong way

Personally, I do not think it has significant advantage to use configuration file (application.ini) from Zend Framework because 1) I do not see big difference between PHP script file and plain text configuration file in PHP enviroment. Probably, placing configuration directly in PHP script is better in terms of performance, easy maintenance and flexible. 2) the cost of following design of Zend Framework, for example resource plugin, is that programmer has to be very careful about each components' life cycle in the system. In other words, the programmer has to understand how the thing happens insight.

Here, I have an example. I was given an authentication library as resource plugin for Zend Framework. It asks for multidb resource configuration in application.ini. It has two databases resources. The second one gets the database connection confidential parameters by calling through the first one (default db).

After checking the source code of Zend Framework. We can see that bootstrap will initialize all database adapters from multidb resource specified in application.ini. So, here is the problem. What will happen to the second database connection in multidb when bootstrap tried to initialize it without properly database connection parameters? Fortunately, Zend Framework does not really call php database connection function ( in my case db2_(p)connect) when the DB adapter is initialized. Database connection function call is only called when php script tries to get connection from DB adapter.

However, it is really confusing to specify wrong connection parameters in application.ini as multidb plugin. Then, correcting parameters in some place else. Using multidb as resource plugin might be good practice when multiple database adapters are needed in multiple places in the code and all connection parameters are known. Otherwise, it is neat to create database connection when it is needed.

Simple should be always one of main considerations of software design. Especially, for an interpret language like PHP, this could be more important.

Tuesday, November 9, 2010

creating RESTful service with Zend_Rest_Route and Zend_Rest_Controller

After trying different ways to make RESTful WS in Zend Framework, I am aware that I can simply use Zend_Rest_Route and ZendRest_Controller to make "standard" RESTful WS in PHP. Especially, Zend_Rest_Route supports RESTful URI pattern like /parameterName/parameterValue/.... This is different from what I read from forums. I am using Zend Framework 1.10 in Zend Server 5.0.1 running on IBM i. Zend_Rest_Server is really not good choice to make RESTful service. Zend_Rest_Server needs to be further improved to meet the requirement of "standard" RESTful.

I tried Zend_Controller_Router_Route to implemented RESTful URI pattern. But, it is easier to use Zend_Rest_Route and Zend_Rest_Controller. Actually, Zend_Rest_Controller does nothing but defines several abstract methods, which map to relevant HTTP methods. But, our RESTful controller must extend from it. Below is the sample codes. In my environment, I can access it with this URL: http://localhost/jiaRESTfulWS/public/index/product/fish/number/100/

BootStrap class
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 //adding this function in Bootstrap class to initilize Zend_Rest_Route.
 protected function _initRestRoute() {
  //getting an instance of zend front controller.
  $frontController = Zend_Controller_Front::getInstance ();
  //initializing a Zend_Rest_Route
  $restRoute = new Zend_Rest_Route ( $frontController );
  //let all actions to use Zend_Rest_Route.
  $frontController->getRouter ()->addRoute ( 'default', $restRoute );
 }
 
}

IndexController.php
class IndexController extends Zend_Rest_Controller
{
 
 public function init() {
  $this->getHelper ( 'viewRenderer' )->setNoRender ( true ); 
 }
 
 /**
  * The index action handles index/list requests; it should respond with a
  * list of the requested resources.
  */
 public function indexAction() {
  //HTTP code 500 might not good choice here.
  $this->getResponse ()->setHttpResponseCode ( 500 );
  $this->getResponse ()->appendBody ( "no list/index allowed" );
  
 } 
 
 /**
  * The get action handles GET requests and receives an 'id' parameter; it
  * should respond with the server resource state of the resource identified
  * by the 'id' value.
  */
 public function getAction() {  
  
  //I will return result in XML format.
  $this->getResponse ()->setHeader ( 'Content-Type', 'text/xml' );
  
  //Note: the Request object here is not HttpRequest. It is Zend controller request. This is the key!
  if ($this->getRequest ()->getParam ( "product" ) != NULL and $this->getRequest ()->getParam ( "number") != NULL ) {   
   //Initializing a dummy object for return. 
   $return = new Jia_Return ();
   $return->setProducts ( $this->getRequest ()->getParam ( "product" ) );
   $return->setQuantity ( $this->getRequest ()->getParam ( "number" ) );
   //We prevent the product has been found.
   //So, we set HTTP code 200 here.  
   $this->getResponse ()->setHttpResponseCode ( 200 );
  }    
   else {
    $return= new Jia_ErrorCode('no parameters!');
    //prevent the product is not found.
    $this->getResponse ()->setHttpResponseCode ( 200 );
   }
  
  print $this->_handleStruct( $return );
 
 }
 
 /**
  * The post action handles POST requests; it should accept and digest a
  * POSTed resource representation and persist the resource state.
  */
 public function postAction() {
  
 }
 
 /**
  * The put action handles PUT requests and receives an 'id' parameter; it
  * should update the server resource state of the resource identified by
  * the 'id' value.
  */
 public function putAction() {
 
 }
 
 /**
  * The delete action handles DELETE requests and receives an 'id'
  * parameter; it should update the server resource state of the resource
  * identified by the 'id' value.
  */
 public function deleteAction() {
 
 }
 
  
 /**
  * Handle an array or object result
  *
  * @param array|object $struct Result Value
  * @return string XML Response
  */
 protected function _handleStruct($struct) {

  $dom = new DOMDocument ( '1.0', 'UTF-8' );
  
  $root = $dom->createElement ( "Jia" );
  $method = $root;
  
  $root->setAttribute ( 'generator', 'Yiyu Blog' );
  $root->setAttribute ( 'version', '1.0' );
  $dom->appendChild ( $root );
  
  $this->_structValue ( $struct, $dom, $method );
  
  $struct = ( array ) $struct;
  if (! isset ( $struct ['status'] )) {
   $status = $dom->createElement ( 'status', 'success' );
   $method->appendChild ( $status );
  }
  return $dom->saveXML ();
 }
 
 /**
  * Recursively iterate through a struct
  *
  * Recursively iterates through an associative array or object's properties
  * to build XML response.
  *
  * @param mixed $struct
  * @param DOMDocument $dom
  * @param DOMElement $parent
  * @return void
  */
 protected function _structValue($struct, DOMDocument $dom, DOMElement $parent) {
  $struct = ( array ) $struct;
  
  foreach ( $struct as $key => $value ) {
   if ($value === false) {
    $value = 0;
   } elseif ($value === true) {
    $value = 1;
   }
   
   if (ctype_digit ( ( string ) $key )) {
    $key = 'key_' . $key;
   }
   
   if (is_array ( $value ) || is_object ( $value )) {
    $element = $dom->createElement ( $key );
    $this->_structValue ( $value, $dom, $element );
   } else {
    $element = $dom->createElement ( $key );
    $element->appendChild ( $dom->createTextNode ( $value ) );
   }
   
   $parent->appendChild ( $element );
  }
 }
 
}
Return.php
/**
 * A dummy class as return objec.
 * 
 * @author Yiyu Jia
 *
 */
class Jia_Return {
 
 /**
  * 
  * @var unknown_type
  */
 public $products;
 public $quantity;
 /**
  * @return the $products
  */
 public function getProducts() {
  return $this->products;
 }

 /**
  * @param $products the $products to set
  */
 public function setProducts($products) {
  $this->products = $products;
 }
 /**
  * @return the $quantity
  */
 public function getQuantity() {
  return $this->quantity;
 }

 /**
  * @param $quantity the $quantity to set
  */
 public function setQuantity($quantity) {
  $this->quantity = $quantity;
 }
 
}

ErrorCode.php
/**
 * An dummy error code class.
 * 
 * @author Yiyu Jia
 *
 */
class Jia_ErrorCode { 
 
 public $errorCode;
 
 function __construct($errMsg){
  $this->errorCode = $errMsg;
 }  

}

ZendStudio 7.2 project file can be downloaded from here.

Friday, November 5, 2010

A simple tutorial about creating Zend_Rest_Server based RESTful service.

It seems that RESTful WS is attracting more people to consider as RPC solution. Compared with SOAP based Web Service, RESTful has its advantage and disadvantage. RESTful WS is declared to be more scalability as it exactly maps HTTP methods  POST, GET, PUT, DELETE as a CRUD (Create, Request, Update, Delete) operations. So, if we design our RESTful WS as stateless, it could be more scalable in a cluster farm. It can get benefit from Cache mechanism of HTTP (limited to HTTP GET method configuration on proxy server in most time). However, it could be problem or ask for cost if we do not want the result to be cached. Furthermore, we can not always assume client Web browser has cache enabled. So, RESTful is not always right choice. Especially, it is not good choice when we develop an enterprise application which ask for complex data structure delivered between sub systems and it does not have extremely large volume of the concurrent accessing.

I will compose another post to describe my comparison about SOAP WS and RESTful WS and JSON-RPC. Here, I will show a simple demo about how to create Zend_Rest_Server based RESTful WS in Zend Studio. Zend_Rest_Server is not perfect. For instances, it can not work together with Zend_Action_Router_Route. It also asked for calling function by put "method=functionName" in requesting URL after the "?". This make some guys think it is not RESTful WS as some person said it is not good design. But, I think it is not wrong design at least.  Below is source for simple ZF based RESTful WS.
  1. Create a new ZF project in Zend Studio and name it as jiaRESTfulWS.
  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 restAction() in DummyController .
  5. Creating a Jia_Hello class with sayHello($name) function under /library/Jia directory.
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. Or, you might want to add alias in your apache httpd configuration file to simplify the URL. On my machine, this demo RESTful WS can be accessed with following URL: http://localhost/jiaRESTfulWS/public/index.php/Dummy/rest?method=sayHello&name=jia . 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 {

 /**
  * 
  * @param string $name
  * @return string
  * 
  */
 public function sayHello($name){
  return "hello ".$name;
 }
}


DummyController class
/**
 * This class includes one function restAction(), wich has Zend_Rest_Server initialized.
 * However, you can only use URI with "?" like this "/rest?method=sayHello&name=jia". You 
 * can not have URI like "/rest/name/jia"
 * 
 * @author yiyu
 *
 */
class DummyController extends Zend_Controller_Action
{
 /**
  *  action named as rest.
  */
 public function restAction() {
  
  // disable layouts and renderers
  $this->getHelper ( 'viewRenderer' )->setNoRender ( true );
  
  // initialize REST server
  $server = new Zend_Rest_Server();
  // set REST service class
  $server->setClass ( 'Jia_Hello' ); 
    
  // handle request
  $server->handle ();
 
 }
 
}

Zend Studio 7.2 project file can be downloaded here. Also, I do not recommend to use Zend_Rest_Server for developing RESTful WS as I think it is old and uncompleted class. Zend_Rest_Route should be a better choice to make RESTful Web Service in PHP. Here is a simple tutorial about how to use Zend_Rest_Route and Zend_Rest_Controller to create RESTful Web Service. Creating RESTful service with Zend_Rest_Route and Zend_Rest_Controller

Saturday, October 30, 2010

A discussion about asynchronous programming in PHP and further

I read an article about asynchronous programming in PHP. It declares that lacking asynchronous programming is not limitation from PHP language. However, I think this is obviously the limitation of PHP.

Lacking of multi-thread features is the source of the limitation. PHP programmers have argued that they can use named pipe, node.js, libevent, Curl, Zend_Job, and other process control extensions to implement asynchronous functions in PHP. However, these methods are not core PHP feature. Therefore, there is no guarantee that these functions can be run on all platforms on which core PHP can run. For example, I wonder whether libevent can run on IBM i platform or not. Zend Server for IBM i supports Zend_Job. But it is not in Zend Server CE, which is free. Also, some extensions like PCNTL seems a solution based on multi process with shared memory. It is a inter-process implementation. Multithreading is a inner process implementation.

This limitation of PHP is because of the way of running PHP script too. PHP is known that it has less memory leak problem. This is not surprised as each PHP script is loaded into memory only when it need to be executed. After it is executed, it is flushed out of memory. Of course, PHP has no memory leak as it has no memory at all.This is because PHP inherits from perl as CGI programming language. Somebody will argue that PHP actually has optimizers and extensions like memcache. These make PHP looks like have some memory. However, optimizer is for caching operation code. memcache is for caching some data as a persistent storage. They can not make PHP to have real memory.

Since PHP has no memory, core PHP has no function like PipedInputStream, PipedOutputStream, and multi thread etc. As I presented in an old post, its static modifier means different life scope than other languages do. So, lacking of asynchronous programming feature is the limitation from PHP itself. It is confusing to discuss PHP lacks asynchronous feature and deny it is limitation from PHP language itself.

Also, in servlet world, people has been interested in changing "request per thread" architecture because of the challenge from multiplied Ajax calling from browser side. Leading by Jetty, both Jetty and Tomcat 7.0 has implemented asynchronous servlet API already. I will deeply investigate whether PHP is facing same challenge or not. Or, with Fastcgi, PHP does not need to worry about this at all.

Thursday, October 28, 2010

tutorial about setting up multiple breakpoints for different PHP script files in Zend Studio

After trying Zend Studio 7.2 and 8.0 Beta, I have to say that Zend Studio manual does not fully describe its features. Especially, I can not find info about how to properly set up multiple breakpoints in its manual .

What happened is, after initializing a ZF project with default settings in ZS, I find that I can only make debugging break point in the first PHP script, which is /public/index.php . Zend Debugger ignores other breakpoints set in other PHP scripts, where those complex logical are implemented. After studying, I find that we need to change the default debug configurations to make multiple breakpoints to work. The steps are as below,

1) right click your project in PHP Explorer window and select Properties for properties window.
2) Select  "PHP Include Path" --> "Libraries"
3) Remove the default libraries of Zend Framework, which belong to Zend Studio.
4) Add the Zend Framework directory under your local Zend server directory as library. Now you can see the screen like below,
5) Click menu "Run" --> "Debug Configurations..." . select or create your debug configuration settings.
6) Click "Configure..." button under the "server" tab for edit server window.
7) Click "path mapping" tab and create a path mapping entry, which has exactly same path for "server path" and "local path". You will see screen as below,
8) Click "Advanced" tab on "Debug Configurations" window and select radio button "Local copy if available. Otherwise, the server".

Now, you can see that Zend Debugger can stop at breakpoints in PHP scripts other than the first page scripts.

For better understand the debugger in Zend Studio, here is one link: click.

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.

Thursday, October 7, 2010

Zend_Soap does not have build-in support for WSS headers.

I am disappointed to find that Zend_Soap_Client does not support WSS tag. When I talked to some PHP programmers, I found that they only talk about HTTP authentication. It is true that we can protect our Web Service by putting HTTP authentication at the front of the Web service. We can further adding SSL on WS. But, all of these methods are belong to HTTP layer. In fact, Web Service standard defines security head tags for authentication and encryption purpose.

It will be disadvantage of ZF not to support WSS tags especially when other languages have supported it. In Java world, we have Axis2 and WSS4J. There are some extensions in PHP to support WSS. But I wish Zend will put WSS support in Zend_Soap soon. Otherwise, there will be doubt when we will apply Zend_Soap in enterprise application solution. I put my message in zend wiki. click here.

Wednesday, October 6, 2010

Be sure to check httpd "AllowOverride" setting for your Zend Framework app

Yesterday, I installed Zend Server CE ver 5.0.3 Windows x86 on my machine. Then, I used Zend Studio 7.2 to create a default ZF project. However, I find that I always got 404 error that means page is not found.

How can it be? Zend Studio actually put whole project files under Apache httpd's htdocs folder already. First thing I suspect is that I dont have .htaccess  file defined or it defines wrong rules? Then, I spend time on finding .htaccess file and checking its rules definition. However, it seems there is nothing wrong but my ZF application can not run!

Finally, I go to Apache httpd configuration folder and open the httpd.conf file and zend.conf file. Here, I found the problem. And it is so trivial and I feel I am stupid that I wasted time on checking .htaccess files. Zend server sets its apache httpd configuration with "AllowOverride None". Therefore, those .htaccess file I keep on checking is totally ignored by httpd! I changed it to be "AllowOverride All" and my ZF application works immediately. This is not a safe configuration. However, it is just a local web server for developing purpose. It is Ok.

I do not know whether this could be called as Zend Server's bug or not. Zend Server or Zend Studio should modify this setting for developers if they declare themselves as seamless integrated development environment for ZF developers. However, it is really not difficult for developers to correct this setting if they keep in mind of this. After getting this lesson, I think I will always remember to check this setting after I freshly install a Zend Server. 

If interested, official site to describe Apache httpd "AllowOverride" configuration is here .

Tuesday, April 6, 2010

We don't need MVC framework on the server side today

I got this thought several years ago when I evaluated Ajax libraries for one project about upgrading an old enterprise application written in PHP and Java. I recalled this because I interviewed with one employer who intends to select Zend Framework now (in 2010). Its implementation of MVC is one of reasons for them to choose Zend Framework. Zend framework could be good candidate for implementing enterprise application. However, I do not think a Rich Internet Application or a Single Page Application needs a MVC framework on the server side today.

Many software vendors talked about Ajax when Ajax was becoming popular. However, many of them talk about their own server side framework, which i personally do not like. I divide those Ajax frameworks into two classes. One is client side pure JavaScript library. The other is Ajax framework running on the server side to generate client side Ajax widget. Personally, I like the pure javascripts libraries for developing Single page enterprise application.

It is not necessary to adopt MVC framework on the server side if we are developing a Single page Web application because we will not generate View on the server side. Supposing we are using ExtJS or Dojo to developing a Single page Web application. All views (widgets) could be written in JavaScript. Browser can either download whole views at one time or dynamically download views on demand. Therefore, why do we still need a MVC framework on the server side? I believe that we only need a front control framework on the server side to supply data (modal) to render views downloaded in browser. With this design, we clearly divide view development and models developing. Also, it is possible to divide developing team into two group. One is good at JavaScript coding and will focus on JavaScript code. The other is good at PHP coding or Java coding and will focus on server side programming. Furthermore, we can avoid mixing HTML, Javascript, and PHP or Java code as much as possible. A designed protocol will link server side and client side applications. JSON could be a good candidate technique to be used for delivering data between browser and server.

Maybe, it could be good idea to implement an Javascript MVC framework in browser?