Revision: 20514
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
at November 17, 2009 12:45 by kerpunk
Initial Code
<?php
class Jsonrpc {
var $server;
var $client;
function Jsonrpc() {
}
function get_server() {
if(!isset($this->server)) {
$this->server = new JSON_RPC_Server();
}
return $this->server;
}
function get_client() {
if(!isset($this->client)) {
$this->client = new JSON_RPC_Client();
}
return $this->client;
}
}
class JSON_RPC_Message {
var $JSON_RPC_VERSION = '1.1';
var $JSON_RPC_ID; // for backwards compatibility
var $CONTENT_LENGTH_KEY = 'Content-Length';
var $CONTENT_TYPE_KEY = 'Content-Type';
var $CONNECTION_KEY = 'Connection';
var $content_length;
var $content_type = 'application/json';
var $connection = 'Close';
var $error_code = '';
var $error_message = '';
var $raw_data;
var $data_object;
var $parser;
var $VALUE_MAPPINGS = array();
function JSON_RPC_Message() {
$this->parser = new JSON_RPC_Parser();
$this->JSON_RPC_ID = 'ID_'.rand();
$this->VALUE_MAPPINGS = array(
$this->CONTENT_LENGTH_KEY => 'content_length',
$this->CONTENT_TYPE_KEY => 'content_type',
$this->CONNECTION_KEY => 'connection',
);
}
function create_header($key, $value) {
return "$key: $valuern";
}
function parse_header($header) {
if(preg_match('/(.+):s+(.+)/', $header, $matches)) {
return array($matches[1],$matches[2]);
}
return false;
}
}
class JSON_RPC_Request extends JSON_RPC_Message {
var $HOST_KEY = 'Host';
var $USER_AGENT_KEY = 'User-Agent';
var $ACCEPT_KEY = 'Accept';
var $host;
var $user_agent = 'CodeIgniter JSON RPC Client';
var $accept = 'application/json,application/javascript,text/javascript';
var $path;
var $remote_method;
var $request_method;
function JSON_RPC_Request() {
parent::JSON_RPC_Message();
$this->VALUE_MAPPINGS[$this->HOST_KEY] = 'host';
$this->VALUE_MAPPINGS[$this->USER_AGENT_KEY] = 'user_agent';
$this->VALUE_MAPPINGS[$this->ACCEPT_KEY] = 'accept';
}
function create_request() {
$req = '';
if($this->request_method == 'POST') {
$data = array();
$data['version'] = $this->JSON_RPC_VERSION;
$data['id'] = $this->JSON_RPC_ID;
$data['method'] = $this->remote_method;
if(isset($this->data_object)) {
$data['params'] = $this->data_object;
}
else
{
$data[ 'params' ] = array();
}
$data = $this->parser->encode($data);
$this->content_length = strlen($data);
$req .= "POST {$this->path} HTTP/1.1rn";
$req .= $this->create_header($this->USER_AGENT_KEY, $this->user_agent);
$req .= $this->create_header($this->HOST_KEY, $this->host);
$req .= $this->create_header($this->CONTENT_TYPE_KEY, $this->content_type);
$req .= $this->create_header($this->CONTENT_LENGTH_KEY, $this->content_length);
$req .= $this->create_header($this->ACCEPT_KEY, $this->accept);
$req .= $this->create_header($this->CONNECTION_KEY, $this->connection);
$req .= "rn";
$req .= $data;
} else {
$req .= 'GET ';
$req .= ($this->path == '/') ? '' : $this->path;
$req .= "/{$this->remote_method}?";
if(isset($this->data_object)) {
foreach($this->data_object as $param_key=>$param_value) {
if(!is_array($param_value) && !is_object($param_value)) {
$req .= "$param_key=$param_value&";
}
}
}
$req = substr($req, 0, -1); // rip off trailing ? or &
$req .= " HTTP/1.1rn";
$req .= $this->create_header($this->USER_AGENT_KEY, $this->user_agent);
$req .= $this->create_header($this->HOST_KEY, $this->host);
$req .= $this->create_header($this->ACCEPT_KEY, $this->accept);
$req .= $this->create_header($this->CONNECTION_KEY, $this->connection);
$req .= "rn";
}
$this->raw_data = $req;
return $req;
}
}
class JSON_RPC_Response extends JSON_RPC_Message {
var $ERROR_CODES = array(
'invalid_json' => 1,
'response_not_ok' => 2,
'response_malformed' => 3,
);
var $ERROR_MESSAGES = array(
'invalid_json' => 'The server responded with an invalid JSON object',
'response_not_ok' => '...',
'response_malformed' => 'The server responded with a malformed HTTP request'
);
var $SERVER_KEY = 'Server';
var $CACHE_CONTROL_KEY = 'Cache-Control';
var $server;
var $cache_control;
var $http_version;
var $response_code;
var $error_code = '';
var $error_message = '';
function JSON_RPC_Response($message = '', $error_code = '', $error_message = '') {
parent::JSON_RPC_Message();
$this->raw_data = $message;
$this->error_code = $error_code;
$this->error_message = $error_message;
$this->VALUE_MAPPINGS[$this->SERVER_KEY] = 'server';
$this->VALUE_MAPPINGS[$this->CACHE_CONTROL_KEY] = 'cache_control';
}
function has_errors() {
return (strlen($this->error_code) > 0);
}
function parse_response() {
if(strncmp($this->raw_data, 'HTTP', 4) == 0) {
preg_match('/^HTTP/([0-9.]+) (d+) /', $this->raw_data, $response);
$this->http_version = $response[1];
$this->response_code = $response[2];
if($this->response_code != '200') {
$this->error_code = $this->ERROR_CODES['response_not_ok'];
$this->error_message = substr($this->raw_data, 0, strpos($this->raw_data, "n")-1);
return false;
}
} else {
$this->error_code = $this->ERROR_CODES['response_malformed'];
$this->error_message = $this->ERROR_MESSAGES['response_malformed'];
return false;
}
$lines = explode("rn", $this->raw_data);
array_shift($lines); // remove first line, as it's not technically a header
while (($line = array_shift($lines))) {
if(strlen($line) < 1) { break; }
$header = $this->parse_header($line);
//echo $this->VALUE_MAPPINGS[$header[0]];
//$k = $this->VALUE_MAPPINGS[$header[0]];
//echo $this->$k;
if(isset($this->VALUE_MAPPINGS[$header[0]])) {
$k = $this->VALUE_MAPPINGS[$header[0]];
$this->$k = $header[1];
}
}
$data = implode("rn", $lines);
$this->data_object = $this->parser->decode($data);
if(!is_object($this->data_object) || is_null($this->data_object)) {
$this->error_code = $this->ERROR_CODES['invalid_json'];
$this->error_message = $this->ERROR_MESSAGES['invalid_json'];
return false;
}
return true;
}
}
class JSON_RPC_Server_Response extends JSON_RPC_Message {
var $SERVER_KEY = 'Server';
var $server = 'CodeIgniter JSON RPC Server';
var $id;
var $error;
var $ERROR_CODES = array(
'bad_call'=>array(
'code'=> 000,
'name'=>'Bad call',
'message'=> 'The procedure call is not valid.'
),
'parse_error'=>array(
'code'=> 000,
'name'=> 'Parse error',
'message'=> 'An error occurred on the server while parsing the JSON text comprising the procedure call.'
),
'procedure_not_found'=>array(
'code'=> 000,
'name'=> 'Procedure not found',
'message'=> 'The call is valid but the procedure identified by the call could not be located on the service.'
),
'service_error'=>array(
'code'=> 000,
'name'=> 'Service error',
'message'=> 'The call is valid, but a general error occurred during the procedure invocation.'
)
);
function JSON_RPC_Server_Response($data_object = null) {
parent::JSON_RPC_Message();
if($data_object != null) {
$this->data_object = $data_object;
}
}
function set_error($error) {
if(is_string($error)) {
$this->error = $this->ERROR_CODES[$error];
} else if(is_array($error)) {
$this->error = $error;
}
}
function create_server_response() {
$data = array();
$data['version'] = $this->JSON_RPC_VERSION;
if(isset($this->id)) { $data['id'] = $this->id; }
if(isset($this->error)) { $data['error'] = $this->error; }
else { $data['result'] = $this->data_object; }
$data = $this->parser->encode($data);
$this->content_length = strlen($data);
header("HTTP/1.1 200 OKrn");
header($this->create_header($this->SERVER_KEY, $this->server));
header($this->create_header($this->CONNECTION_KEY, $this->connection));
// header($this->create_header($this->CONTENT_TYPE_KEY, $this->content_type));
header($this->create_header($this->CONTENT_LENGTH_KEY, $this->content_length));
$this->raw_data = $data;
return $data;
}
}
class JSON_RPC_Server_Request extends JSON_RPC_Message {
var $ERROR_CODES = array(
'invalid_json' => 1
);
var $ERROR_MESSAGES = array(
'invalid_json' => 'The server responded with an invalid JSON object'
);
var $error_code = '';
var $error_message = '';
function JSON_RPC_Server_Request($message = '', $error_code = '', $error_message = '') {
parent::JSON_RPC_Message();
$this->raw_data = $message;
$this->error_code = $error_code;
$this->error_message = $error_message;
}
function has_errors() {
return (strlen($this->error_code) > 0);
}
function parse_response() {
$this->data_object = $this->parser->decode($this->raw_data);
if(!is_object($this->data_object) || is_null($this->data_object)) {
$this->error_code = $this->ERROR_CODES['invalid_json'];
$this->error_message = $this->ERROR_MESSAGES['invalid_json'];
return false;
}
return true;
}
}
class JSON_RPC_Client {
var $request;
var $response;
var $port = 80;
var $timeout = 5;
function JSON_RPC_Client() {
$this->request = new JSON_RPC_Request();
}
function server($url, $request_method = 'POST', $port = 80) {
if (substr($url, 0, 4) != "http") {
$url = "http://".$url;
}
$parts = parse_url($url);
$path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
if (isset($parts['query']) && $parts['query'] != '') {
$path .= '?'.$parts['query'];
}
$this->request->path = $path;
$this->request->host = $parts['host'];
$this->request->request_method = $request_method;
$this->port = $port;
}
function method($remote_method) {
$this->request->remote_method = $remote_method;
}
function request($request_parameters) {
$this->request->data_object = $request_parameters;
}
function timeout($timeout = 5) {
$this->timeout = $timeout;
}
function send_request() {
$request = $this->request->create_request();
$fp = @fsockopen($this->request->host, $this->port, $errno, $errstr, $this->timeout);
if(!$fp) {
$this->response = new JSON_RPC_Response('', $errno, $errstr);
return false;
}
fwrite($fp, $request);
$response_text = '';
while (!feof($fp)) {
$response_text .= fgets($fp, 128);
}
$this->response = new JSON_RPC_Response($response_text);
fclose($fp);
return $this->response->parse_response();
}
function get_response() {
return $this->response;
}
function get_response_object() {
return $this->response->data_object;
}
}
class JSON_RPC_Server {
var $php_types_to_jsonrpc_types = array(
'Boolean'=>'bit',
'Number'=>'num',
'String'=>'str',
'Array'=>'arr',
'Object'=>'obj'
);
var $methods = array();
var $object = false;
var $service_name = 'CodeIgniter JSON RPC Server';
var $service_sd_version = '1.0';
var $service_id = '';
var $service_version = '1.0';
var $service_summary = 'A JSON RPC Server for CodeIgniter. Written by Nick Husher ([email protected])';
var $service_help = '';
var $service_address = '';
function JSON_RPC_Server() {
$this->methods['system.describe'] = array(
'function'=>'this.describe',
'summary'=>'Display relevant information about the JSON RPC server.',
'help'=>'http://json-rpc.org',
'return'=>array('type'=>'obj')
);
$CI =& get_instance();
$CI->load->helper('url');
$this->service_address = current_url();
$this->service_id = current_url();
}
function define_methods($methods) {
foreach($methods as $methodName=>$methodProperties) {
$this->methods[$methodName] = $methodProperties;
}
}
function set_object($object) {
if(is_object($object)) {
$this->object =& $object;
}
}
function serve() {
global $HTTP_RAW_POST_DATA;
$incoming = new JSON_RPC_Server_Request($HTTP_RAW_POST_DATA);
if(!$incoming->parse_response()) {
$response = $this->send_error('parse_error');
echo $response->create_server_response();
return;
}
$response = $this->_execute($incoming->data_object);
echo $response->create_server_response();
}
function send_response($object) {
return new JSON_RPC_Server_Response($object);
}
function send_error($error) {
$r = new JSON_RPC_Server_Response();
$r->set_error($error);
return $r;
}
function _execute($request_object) {
// check if the method is defined on the server
if(!isset($this->methods[$request_object->method])) {
return $this->send_error('procedure_not_found');
}
$method_definition = $this->methods[$request_object->method];
// check if we have a function definition
if(!isset($method_definition['function'])) {
return $this->send_error('procedure_not_found');
}
$function_name = explode('.',$method_definition['function']);
$is_system_call = ($function_name[0] == 'this');
// check if the function/object is callable
if($is_system_call) {
if(!isset($function_name[1]) || !is_callable(array($this, $function_name[1]))) {
$r = $this->send_error('service_error');
// $r->error['code'] = 001;
return $r;
}
} else {
if(!isset($function_name[1]) || !is_callable(array($function_name[0], $function_name[1]))) {
$r = $this->send_error('service_error');
// $r->error['code'] = 002;
return $r;
}
}
// check parameters
if(isset($request_object->params)) {
$parameters = $request_object->params;
} else {
$parameters = array();
}
if(isset($method_definition['parameters']) && is_array($method_definition['parameters'])) {
$parameters = $method_definition['parameters'];
for($i = 0; $i < count($parameters); $i++) {
$current_parameter = $parameters[$i];
if(!isset($current_parameter['name'])) {
$r = $this->send_error('service_error');
// $r->error['code'] = 003;
return $r;
}
if(!isset($parameters->$current_parameter['name'])) {
return $this->send_error('bad_call');
}
if(isset($current_parameter['type']) &&
gettype($parameters->$current_parameter['name']) != $current_parameter['type'])
{
return $this->send_error('bad_call');
}
}
}
// call the function
if($is_system_call) {
$response = $this->$function_name[1]($parameters);
} else {
if(is_object($this->object)) {
$response = $this->object->$function_name[1]($parameters);
} else {
$r = $this->send_error('service_error');
// $r->error['code'] = 003;
return $r;
}
}
if(isset($request_object->id)) {
$response->id = $request_object->id;
}
return $response;
}
// system functions
function describe() {
$method_property_names = array(
'parameters'=>'params',
'summary'=>'summary',
'help'=>'help',
'return'=>'return'
);
$description = array();
$description['sdversion'] = $this->service_sd_version;
$description['name'] = $this->service_name;
$description['id'] = $this->service_id;
$description['version'] = $this->service_version;
$description['summary'] = $this->service_summary;
$description['help'] = $this->service_help;
$description['address'] = $this->service_address;
$description['procs'] = array();
foreach($this->methods as $method_name=>$method_properties) {
$method = array();
$method['name'] = $method_name;
foreach($method_property_names as $name=>$property_name) {
if(isset($method_properties[$property_name])) {
$method[$property_name] = $method_properties[$name];
} else if($name == 'parameters' || $name == 'return') {
$method[$property_name] = 'any';
}
}
$description['procs'][] = $method;
}
return $this->send_response($description);
}
}
class JSON_RPC_Parser {
function encode($val) {
return json_encode($val);
}
function decode($val) {
return json_decode($val);
}
}
?>
Initial URL
Initial Description
Initial Title
CI json_rpc library
Initial Tags
textmate, json, codeigniter
Initial Language
Other