<?php /** * @author Robin J. Rogge <rrogge@bigpoint.net> * @link http://www.pradosoft.com/ * @copyright 2010 Bigpoint GmbH * @license http://www.pradosoft.com/license/ * @since 3.2 * @package System.Util */ /** * TRpcClient class * * Note: When using setIsNotification(true), *every* following request is also * considered to be a notification until you use setIsNotification(false). * * Usage: * * First, you can use the factory: * <pre> * $_rpcClient = TRpcClient::create('xml', 'http://host/server'); * $_result = $_rpcClient->remoteMethodName($param, $otherParam); * </pre> * * or as oneliner: * <pre> * $_result = TRpcClient::create('json', 'http://host/server')->remoteMethod($param, ...); * </pre> * * Second, you can also use the specific implementation directly: * <pre> * $_rpcClient = new TXmlRpcClient('http://host/server'); * $_result = $_rpcClient->remoteMethod($param, ...); * </pre> * * or as oneliner: * <pre> * $_result = TXmlRpcClient('http://host/server')->hello(); * </pre> * * @author Robin J. Rogge <rrogge@bigpoint.net> * @version $Id$ * @package System.Util * @since 3.2 */ class TRpcClient extends TApplicationComponent { /** * @var string url of the RPC server */ private $_serverUrl; /** * @var boolean whether the request is a notification and therefore should not care about the result (default: false) */ private $_isNotification = false; // magics /** * @param string url to RPC server * @param boolean whether requests are considered to be notifications (completely ignoring the response) (default: false) */ public function __construct($serverUrl, $isNotification = false) { $this->_serverUrl = $serverUrl; $this->_isNotification = TPropertyValue::ensureBoolean($isNotification); } // methods /** * Creates an instance of the requested RPC client type * @return TRpcClient instance * @throws TApplicationException if an unsupported RPC client type was specified */ public static function create($type, $serverUrl, $isNotification = false) { if(($_handler = constant('TRpcClientTypesEnumerable::'.strtoupper($type))) === null) throw new TApplicationException('rpcclient_unsupported_handler'); return new $_handler($serverUrl, $isNotification); } /** * Creates a stream context resource * @param mixed $content * @param string $contentType mime type */ protected function createStreamContext($content, $contentType) { return stream_context_create(array( 'http' => array( 'method' => 'POST', 'header' => "Content-Type: {$contentType}", 'content' => $content ) )); } /** * Performs the actual request * @param string RPC server URL * @param array payload data * @param string request mime type */ protected function performRequest($serverUrl, $payload, $mimeType) { if(($_response = @file_get_contents($serverUrl, false, $this->createStreamContext($payload, $mimeType))) === false) throw new TRpcClientRequestException('Request failed ("'.$http_response_header[0].'")'); return $_response; } // getter/setter /** * @return boolean whether requests are considered to be notifications (completely ignoring the response) */ public function getIsNotification() { return $this->_isNotification; } /** * @param string boolean whether the requests are considered to be notifications (completely ignoring the response) (default: false) */ public function setIsNotification($bool) { $this->_isNotification = TPropertyValue::ensureBoolean($bool); } /** * @return string url of the RPC server */ public function getServerUrl() { return $this->_serverUrl; } /** * @param string url of the RPC server */ public function setServerUrl($value) { $this->_serverUrl = $value; } } /** * TRpcClientTypesEnumerable class * * @author Robin J. Rogge <rrogge@bigpoint.net> * @version $Id$ * @package System.Util * @since 3.2 */ class TRpcClientTypesEnumerable extends TEnumerable { const JSON = 'TJsonRpcClient'; const XML = 'TXmlRpcClient'; } /** * TRpcClientRequestException class * * This Exception is fired if the RPC request fails because of transport problems e.g. when * there is no RPC server responding on the given remote host. * * @author Robin J. Rogge <rrogge@bigpoint.net> * @version $Id$ * @package System.Util * @since 3.2 */ class TRpcClientRequestException extends TApplicationException { } /** * TRpcClientResponseException class * * This Exception is fired when the * * @author Robin J. Rogge <rrogge@bigpoint.net> * @version $Id$ * @package System.Util * @since 3.2 */ class TRpcClientResponseException extends TApplicationException { /** * @param string error message * @param integer error code (optional) */ public function __construct($errorMessage, $errorCode = null) { $this->setErrorCode($errorCode); parent::__construct($errorMessage); } } /** * TJsonRpcClient class * * Note: When using setIsNotification(true), *every* following request is also * considered to be a notification until you use setIsNotification(false). * * Usage: * <pre> * $_rpcClient = new TJsonRpcClient('http://host/server'); * $_result = $_rpcClient->remoteMethod($param, $otherParam); * // or * $_result = TJsonRpcClient::create('http://host/server')->remoteMethod($param, $otherParam); * </pre> * * @author Robin J. Rogge <rrogge@bigpoint.net> * @version $Id$ * @package System.Util * @since 3.2 */ class TJsonRpcClient extends TRpcClient { // magics /** * @param string RPC method name * @param array RPC method parameters * @return mixed RPC request result * @throws TRpcClientRequestException if the client fails to connect to the server * @throws TRpcClientResponseException if the response represents an RPC fault */ public function __call($method, $parameters) { // send request $_response = $this->performRequest($this->getServerUrl(), $this->encodeRequest($method, $parameters), 'application/json'); // skip response handling if the request was just a notification request if($this->isNotification) return true; // decode response if(($_response = json_decode($_response, true)) === null) throw new TRpcClientResponseException('Empty response received'); // handle error response if(!is_null($_response['error'])) throw new TRpcClientResponseException($_response['error']); return $_response['result']; } // methods /** * @param string method name * @param array method parameters */ public function encodeRequest($method, $parameters) { static $_requestId; $_requestId = ($_requestId === null) ? 1 : $_requestId + 1; return json_encode(array( 'method' => $method, 'params' => $parameters, 'id' => $this->isNotification ? null : $_requestId )); } /** * Creates an instance of TJsonRpcClient * @param string url of the rpc server * @param boolean whether the requests are considered to be notifications (completely ignoring the response) (default: false) */ public static function create($type, $serverUrl, $isNotification = false) { return new self($serverUrl, $isNotification); } } /** * TXmlRpcClient class * * Note: When using setIsNotification(true), *every* following request is also * considered to be a notification until you use setIsNotification(false). * * Usage: * <pre> * $_rpcClient = new TXmlRpcClient('http://remotehost/rpcserver'); * $_rpcClient->remoteMethod($param, $otherParam); * </pre> * * @author Robin J. Rogge <rrogge@bigpoint.net> * @version $Id$ * @package System.Util * @since 3.2 */ class TXmlRpcClient extends TRpcClient { // magics /** * @param string RPC method name * @param array RPC method parameters * @return mixed RPC request result * @throws TRpcClientRequestException if the client fails to connect to the server * @throws TRpcClientResponseException if the response represents an RPC fault */ public function __call($method, $parameters) { // send request $_response = $this->performRequest($this->getServerUrl(), $this->encodeRequest($method, $parameters), 'text/xml'); // skip response handling if the request was just a notification request if($this->isNotification) return true; // decode response if(($_response = xmlrpc_decode($_response)) === null) throw new TRpcClientResponseException('Empty response received'); // handle error response if(xmlrpc_is_fault($_response)) throw new TRpcClientResponseException($_response['faultString'], $_response['faultCode']); return $_response; } // methods /** * @param string method name * @param array method parameters */ public function encodeRequest($method, $parameters) { return xmlrpc_encode_request($method, $parameters); } /** * Creates an instance of TXmlRpcClient * @param string url of the rpc server * @param boolean whether the requests are considered to be notifications (completely ignoring the response) (default: false) */ public static function create($type, $serverUrl, $isNotification = false) { return new self($serverUrl, $isNotification); } }