Wednesday, January 27, 2010

Remote services using REST


In this example, we implement a REST client and a REST server.

Components used in this example
Implementation of the client

class MyRestClient
{
Processing the user request
  • We get the data and the action.
  • We parse the data to get a scalar or an array.
  • We set the server name. It is the same as the client plus one parameter to indicate it is the server.
  • We get the list of remote methods. We remove the system methods.
  • We process the request.
  • If there is a XML node named response, we get the response that is a scalar.
  • Otherwise we extract the array from the XML node.
  • We add the response details if needed.
  • If we catch an exception, we return the error message.

    public function process()
    {
        
// We get the data and the action.
        
list($data$action$response) = $this->_getParameters();

        
$methods = array();
        
        try {
            
// We parse the data to get a scalar or an array.
            
$parsed $this->_parseData($data);
            
            
// We set the server name.
            // It is the same as the client plus one parameter to indicate it is the server.
            
$server "http://{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}?server=";
            
$client = new Zend_Rest_Client($server);

            
// We get the list of remote methods. We remove the system methods.
            
$get $client->getFunctions()->get();
            
$methods $this->_toArray($get->getFunctions);
            
$methods preg_grep('~^(array|scalar)_\w+$~'$methods);
            
sort($methods);
            
            
// We process the request.
            
if ($action) {
                
$get $client->$action($parsed)->get();
                
                if (
$get->getStatus()) {
                    if (isset(
$get->response)) {
                        
// If there is a XML node named response, 
                        // we get the response that is a scalar. 
                        
$result['GET'] = (string)$get;
                    } else {
                        
// Otherwise we extract the array from the XML node.
                        
$result['GET'] = $this->_toArray($get->$action);
                    }
                } else {
                    
$result['ERROR'] = (string)$get;                        
                }

                
// We add the response details if needed.
                
$response and $result['RESPONSE'] = $get;
                
            } else {
                
$result 'Enter some data and select an action.';
            }                        
            
        } catch (
Exception $e) {
            
// If we catch an exception, we return the error message.
            
$result '[' __CLASS__ '] ' $e->getMessage();
        }

        return array(
$data$action$response$methods$result);
    }
Extraction of the parameters from the GET request

    private function _getParameters()
    {
        
$data = isset($_GET['data'])? $_GET['data'] : '';
        
$action = isset($_GET['action'])? $_GET['action'] : null;
        
$response = empty($_GET)? : !empty($_GET['response']);

        return array(
$data$action$response);
    }
Extraction of the data into a scalar or an array.

    private function _parseData($data)
    {        
        if (
strpos($data"\n") === false) {
            return 
$data;
            
        } else {
            foreach(
explode("\n"$data) as $line) {
                
$parts explode('='$line2);
                if (
count($parts) == 1) {
                    
$parsed[] = trim($parts[0]);
                } else {
                    
$parsed[trim($parts[0])] = trim($parts[1]);
                }
            }
            return 
$parsed;
        }
    }
Extraction of the array from the XML node
  • We ignore the status that is returned as an array element.
  • We explore nodes recursively.
  • We replace the keys generated by ZF Server with the corresponding index.
  • Or we keep the key as it is.

    private function _toArray($simpleXml)
    {
        
$array = array();
        
        foreach((array)
$simpleXml as $key => $value) {
            
// We ignore the status that is returned as an array element.
            
if (!($key == 'status' and strtolower($value) == 'success')) {
                
// We explore nodes recursively.
                
$value instanceof SimpleXMLElement and $value $this->_toArray($value);
            
                if (
preg_match('~^key_(\d+)$~'$key$match)) {
                    
// We replace the keys generated by ZF Server with the corresponding index.
                    
$array[$match[1]] = $value;
                } else {
                    
// Or we keep the key as it is.
                    
$array[$key] = $value;
                }
            } 
        }
        
        return 
$array;
    }

}

Tuesday, January 26, 2010

Remote services with XML-RPC


In this example, we implement an XML-RPC client and an XML-RPC server. We also use namespaces, the XMLRPC data types, and the server proxy.

Components used in this example
Implementation of the client
  • We define the XMLRPC types.

class MyXmlRpcClient
{
    
// We define the XMLRPC types.
    
public static $types = array(
         
'AUTO_DETECT_TYPE',
         
'XMLRPC_TYPE_APACHEI8',
         
'XMLRPC_TYPE_APACHENIL',
         
'XMLRPC_TYPE_ARRAY',
         
'XMLRPC_TYPE_BASE64',
         
'XMLRPC_TYPE_BOOLEAN',
         
'XMLRPC_TYPE_DATETIME',
         
'XMLRPC_TYPE_DOUBLE',
         
'XMLRPC_TYPE_I4',
         
'XMLRPC_TYPE_I8',
         
'XMLRPC_TYPE_INTEGER',
         
'XMLRPC_TYPE_NIL',
         
'XMLRPC_TYPE_STRING',
         
'XMLRPC_TYPE_STRUCT',
         
// 'XML_STRING',
    
);
Processing the user request
  • We get the data, the action, the data type, etc...
  • We parse the data to get a scalar or an array.
  • We convert the data to a given XMLRPC type if requested.
  • We set the server name. It is the same as the client plus one parameter to indicate it is the server.
  • We get the list of remote methods. We remove the system methods.
  • We process the request.
  • We use the proxy server if requested.
  • We add the response details and the request details if needed.
  • If we catch an exception, we return the error message.

    public function process()
    {
        
// We get the data, the action, the data type, etc...
        
list($data$action$type$request$response$proxy) = $this->_getParameters();

        
$methods = array();
        
        try {
            
// We parse the data to get a scalar or an array.
            
$parsed $this->_parseData($data);
            
// We convert the data to a given XMLRPC type if requested.
            
$type and $parsed Zend_XmlRpc_Value::getXmlRpcValue($parsed
                
constant("Zend_XmlRpc_Value::$type"));
            
            
// We set the server name.
            // It is the same as the client plus one parameter to indicate it is the server.
            
$server "http://{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}?server=";
            
$client = new Zend_XmlRpc_Client($server);

            
// We get the list of remote methods. We remove the system methods.
            
$methods $client->getIntrospector()->listMethods();
            
$methods preg_grep('~^system~'$methodsPREG_GREP_INVERT);
            
sort($methods);
            
            
// We process the request.
            
switch($action) {
                case 
'listMethods':
                case 
'getSignatureForEachMethod':
                    
$result $client->getIntrospector()->$action();
                    break;
                    
                case 
'':
                    
$result 'Enter some data and select an action.';
                    break;
                    
                default:
                    
// We use the proxy server if requested.
                    
if ($proxy) {
                        list(
$service$method) = explode('.'$action);
                        
$server $client->getProxy($service);
                        
$result['PROXY'] = $server->$method($parsed);
                    } else {
                        
$result['CALL'] = $client->call($action, array($parsed));
                    }
                    
// We add the response details and the request details if needed.
                    
$response and $result['RESPONSE'] = $client->getLastResponse();
                    
$request and $result['REQUEST'] = $client->getLastRequest();
            }
                        
            
        } catch (
Exception $e) {
            
// If we catch an exception, we return the error message.
            
$result '[' __CLASS__ '] ' $e->getMessage();
        }

        return array(
$data$action$type$request$response$proxy$methods$result);
    }
Extraction of the parameters from the GET request

    private function _getParameters()
    {
        
$data = isset($_GET['data'])? $_GET['data'] : '';
        
$action = isset($_GET['action'])? $_GET['action'] : null;
        
$type = (isset($_GET['type']) and in_array($_GET['type'], self::$types))? 
            
$_GET['type'] : null;
        
$request = empty($_GET)? : !empty($_GET['request']);
        
$response = empty($_GET)? : !empty($_GET['response']);
        
$proxy = empty($_GET)? : !empty($_GET['proxy']);

        return array(
$data$action$type$request$response$proxy);
    }
Extraction of the data into a scalar or an array.

    private function _parseData($data)
    {        
        if (
strpos($data"\n") === false) {
            return 
$data;
            
        } else {
            foreach(
explode("\n"$data) as $line) {
                
$parts explode('='$line2);
                if (
count($parts) == 1) {
                    
$parsed[] = trim($parts[0]);
                } else {
                    
$parsed[trim($parts[0])] = trim($parts[1]);
                }
            }
            return 
$parsed;
        }
    }

}