Overview

Packages

  • None
  • Rpc

Classes

  • Horde_Rpc_Jsonrpc
  • Horde_Rpc_Soap_Caller
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Copyright 2007-2012 Horde LLC (http://www.horde.org/)
  4:  *
  5:  * See the enclosed file COPYING for license information (LGPL). If you
  6:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  7:  *
  8:  * @author   Joey Hewitt <joey@joeyhewitt.com>
  9:  * @author   Jan Schneider <jan@horde.org>
 10:  * @category Horde
 11:  * @package  Rpc
 12:  */
 13: 
 14: /**
 15:  * The Horde_RPC_json-rpc class provides a JSON-RPC 1.1 implementation of the
 16:  * Horde RPC system.
 17:  *
 18:  * - Only POST requests are supported.
 19:  * - Named and positional parameters are accepted but the Horde registry only
 20:  *   works with positional parameters.
 21:  * - Service Descriptions are not supported yet.
 22:  *
 23:  * @link     http://json-rpc.org
 24:  * @category Horde
 25:  */
 26: class Horde_Rpc_Jsonrpc extends Horde_Rpc
 27: {
 28:     /**
 29:      * Returns the Content-Type of the response.
 30:      *
 31:      * @return string  The MIME Content-Type of the RPC response.
 32:      */
 33:     function getResponseContentType()
 34:     {
 35:         return 'application/json';
 36:     }
 37: 
 38:     /**
 39:      * Sends an RPC request to the server and returns the result.
 40:      *
 41:      * @param string  The raw request string.
 42:      *
 43:      * @return string  The JSON encoded response from the server.
 44:      */
 45:     function getResponse($request)
 46:     {
 47:         $request = Horde_Serialize::unserialize($request, Horde_Serialize::JSON);
 48: 
 49:         if (!is_object($request)) {
 50:             return $this->_raiseError('Request must be a JSON object', $request);
 51:         }
 52: 
 53:         // Validate the request.
 54:         if (empty($request->method)) {
 55:             return $this->_raiseError('Request didn\'t specify a method name.', $request);
 56:         }
 57: 
 58:         // Convert objects to associative arrays.
 59:         if (empty($request->params)) {
 60:             $params = array();
 61:         } else {
 62:             $params = $this->_objectsToArrays($request->params);
 63:             if (!is_array($params)) {
 64:                 return $this->_raiseError('Parameters must be JSON objects or arrays.', $request);
 65:             }
 66:         }
 67: 
 68:         // Check the method name.
 69:         $method = str_replace('.', '/', $request->method);
 70:         if (!$GLOBALS['registry']->hasMethod($method)) {
 71:             return $this->_raiseError('Method "' . $request->method . '" is not defined', $request);
 72:         }
 73: 
 74:         // Call the method.
 75:         try {
 76:             $result = $GLOBALS['registry']->call($method, $params);
 77:         } catch (Horde_Exception $e) {
 78:             return $this->_raiseError($e, $request);
 79:         }
 80: 
 81:         // Return result.
 82:         $response = array('version' => '1.1', 'result' => $result);
 83:         if (isset($request->id)) {
 84:             $response['id'] = $request->id;
 85:         }
 86: 
 87:         return Horde_Serialize::serialize($response, Horde_Serialize::JSON);
 88:     }
 89: 
 90:     /**
 91:      * Returns a specially crafted PEAR_Error object containing a JSON-RPC
 92:      * response in the error message.
 93:      *
 94:      * @param string|PEAR_Error $error  The error message or object.
 95:      * @param stdClass $request         The original request object.
 96:      *
 97:      * @return PEAR_Error  An error object suitable for a JSON-RPC 1.1
 98:      *                     conform error result.
 99:      */
100:     function _raiseError($error, $request)
101:     {
102:         $code = $userinfo = null;
103:         if (is_a($error, 'PEAR_Error')) {
104:             $code = $error->getCode();
105:             $userinfo = $error->getUserInfo();
106:             $error = $error->getMessage();
107:         }
108:         $error = array('name' => 'JSONRPCError',
109:                        'code' => $code ? $code : 999,
110:                        'message' => $error);
111:         if ($userinfo) {
112:             $error['error'] = $userinfo;
113:         }
114:         $response = array('version' => '1.1', 'error' => $error);
115:         if (isset($request->id)) {
116:             $response['id'] = $request->id;
117:         }
118: 
119:         return PEAR::raiseError(Horde_Serialize::serialize($response, Horde_Serialize::JSON));
120:     }
121: 
122:     /**
123:      * Builds an JSON-RPC request and sends it to the server.
124:      *
125:      * This statically called method is actually the JSON-RPC client.
126:      *
127:      * @param string|Horde_Url $url  The path to the JSON-RPC server on the
128:      *                               called host.
129:      * @param string $method         The method to call.
130:      * @param Horde_Http_Client $client
131:      * @param array $params          A hash containing any necessary parameters
132:      *                               for the method call.
133:      *
134:      * @return mixed  The returned result from the method.
135:      * @throws Horde_Rpc_Exception
136:      */
137:     public static function request($url, $method, $client, $params = null)
138:     {
139:         $headers = array(
140:             'User-Agent' => 'Horde RPC client',
141:             'Accept' => 'application/json',
142:             'Content-Type' => 'application/json');
143: 
144:         $data = array('version' => '1.1', 'method' => $method);
145:         if (!empty($params)) {
146:             $data['params'] = $params;
147:         }
148:         $data = Horde_Serialize::serialize($data, Horde_Serialize::JSON);
149:         try {
150:             $result = $client->post($url, $data, $headers);
151:         } catch (Horde_Http_Client_Exception $e) {
152:             throw new Horde_Rpc_Exception($e->getMessage());
153:         }
154:         if ($result->code == 500) {
155:             $response = Horde_Serialize::unserialize($result->getBody(), Horde_Serialize::JSON);
156:             if (is_a($response, 'stdClass') &&
157:                 isset($response->error) &&
158:                 is_a($response->error, 'stdClass') &&
159:                 isset($response->error->name) &&
160:                 $response->error->name == 'JSONRPCError') {
161:                 throw new Horde_Rpc_Exception($response->error->message);
162:                 /* @todo: Include more information if we have an Exception that can handle this.
163:                 return PEAR::raiseError($response->error->message,
164:                                         $response->error->code,
165:                                         null, null,
166:                                         isset($response->error->error) ? $response->error->error : null);
167:                 */
168:             }
169:             throw new Horde_Rpc_Exception($http->getResponseBody());
170:         } elseif ($result->code != 200) {
171:             throw new Horde_Rpc_Exception('Request couldn\'t be answered. Returned errorcode: "' . $result->code);
172:         }
173: 
174:         return Horde_Serialize::unserialize($result->getBody(), Horde_Serialize::JSON);
175:     }
176: 
177:     /**
178:      * Converts stdClass object to associative arrays.
179:      *
180:      * @param $data mixed  Any stdClass object, array, or scalar.
181:      *
182:      * @return mixed  stdClass objects are returned as asscociative arrays,
183:      *                scalars as-is, and arrays with their elements converted.
184:      */
185:     function _objectsToArrays($data)
186:     {
187:         if (is_a($data, 'stdClass')) {
188:             $data = get_object_vars($data);
189:         }
190:         if (is_array($data)) {
191:             foreach ($data as $key => $value) {
192:                 $data[$key] = $this->_objectsToArrays($value);
193:             }
194:         }
195:         return $data;
196:     }
197: 
198: }
199: 
API documentation generated by ApiGen