CI json_rpc library


/ Published in: Other
Save to your folder(s)



Copy this code and paste it in your HTML
  1. <?php
  2.  
  3.  
  4. class Jsonrpc {
  5. var $server;
  6. var $client;
  7.  
  8. function Jsonrpc() {
  9.  
  10. }
  11.  
  12. function get_server() {
  13. if(!isset($this->server)) {
  14. $this->server = new JSON_RPC_Server();
  15. }
  16. return $this->server;
  17. }
  18.  
  19. function get_client() {
  20. if(!isset($this->client)) {
  21. $this->client = new JSON_RPC_Client();
  22. }
  23. return $this->client;
  24. }
  25. }
  26.  
  27. class JSON_RPC_Message {
  28. var $JSON_RPC_VERSION = '1.1';
  29. var $JSON_RPC_ID; // for backwards compatibility
  30.  
  31. var $CONTENT_LENGTH_KEY = 'Content-Length';
  32. var $CONTENT_TYPE_KEY = 'Content-Type';
  33. var $CONNECTION_KEY = 'Connection';
  34.  
  35. var $content_length;
  36. var $content_type = 'application/json';
  37. var $connection = 'Close';
  38.  
  39. var $error_code = '';
  40. var $error_message = '';
  41.  
  42. var $raw_data;
  43. var $data_object;
  44.  
  45. var $parser;
  46. var $VALUE_MAPPINGS = array();
  47.  
  48. function JSON_RPC_Message() {
  49. $this->parser = new JSON_RPC_Parser();
  50. $this->JSON_RPC_ID = 'ID_'.rand();
  51.  
  52. $this->VALUE_MAPPINGS = array(
  53. $this->CONTENT_LENGTH_KEY => 'content_length',
  54. $this->CONTENT_TYPE_KEY => 'content_type',
  55. $this->CONNECTION_KEY => 'connection',
  56. );
  57. }
  58.  
  59. function create_header($key, $value) {
  60. return "$key: $valuern";
  61. }
  62. function parse_header($header) {
  63. if(preg_match('/(.+):s+(.+)/', $header, $matches)) {
  64. return array($matches[1],$matches[2]);
  65. }
  66. return false;
  67. }
  68. }
  69.  
  70. class JSON_RPC_Request extends JSON_RPC_Message {
  71. var $HOST_KEY = 'Host';
  72. var $USER_AGENT_KEY = 'User-Agent';
  73. var $ACCEPT_KEY = 'Accept';
  74.  
  75. var $host;
  76. var $user_agent = 'CodeIgniter JSON RPC Client';
  77. var $accept = 'application/json,application/javascript,text/javascript';
  78.  
  79. var $path;
  80. var $remote_method;
  81. var $request_method;
  82.  
  83. function JSON_RPC_Request() {
  84. parent::JSON_RPC_Message();
  85.  
  86. $this->VALUE_MAPPINGS[$this->HOST_KEY] = 'host';
  87. $this->VALUE_MAPPINGS[$this->USER_AGENT_KEY] = 'user_agent';
  88. $this->VALUE_MAPPINGS[$this->ACCEPT_KEY] = 'accept';
  89. }
  90.  
  91. function create_request() {
  92. $req = '';
  93.  
  94. if($this->request_method == 'POST') {
  95. $data = array();
  96. $data['version'] = $this->JSON_RPC_VERSION;
  97. $data['id'] = $this->JSON_RPC_ID;
  98. $data['method'] = $this->remote_method;
  99. if(isset($this->data_object)) {
  100. $data['params'] = $this->data_object;
  101. }
  102. else
  103. {
  104. $data[ 'params' ] = array();
  105. }
  106.  
  107. $data = $this->parser->encode($data);
  108. $this->content_length = strlen($data);
  109.  
  110. $req .= "POST {$this->path} HTTP/1.1rn";
  111. $req .= $this->create_header($this->USER_AGENT_KEY, $this->user_agent);
  112. $req .= $this->create_header($this->HOST_KEY, $this->host);
  113. $req .= $this->create_header($this->CONTENT_TYPE_KEY, $this->content_type);
  114. $req .= $this->create_header($this->CONTENT_LENGTH_KEY, $this->content_length);
  115. $req .= $this->create_header($this->ACCEPT_KEY, $this->accept);
  116. $req .= $this->create_header($this->CONNECTION_KEY, $this->connection);
  117. $req .= "rn";
  118. $req .= $data;
  119. } else {
  120. $req .= 'GET ';
  121. $req .= ($this->path == '/') ? '' : $this->path;
  122. $req .= "/{$this->remote_method}?";
  123.  
  124. if(isset($this->data_object)) {
  125. foreach($this->data_object as $param_key=>$param_value) {
  126. if(!is_array($param_value) && !is_object($param_value)) {
  127. $req .= "$param_key=$param_value&";
  128. }
  129. }
  130. }
  131.  
  132. $req = substr($req, 0, -1); // rip off trailing ? or &
  133. $req .= " HTTP/1.1rn";
  134.  
  135. $req .= $this->create_header($this->USER_AGENT_KEY, $this->user_agent);
  136. $req .= $this->create_header($this->HOST_KEY, $this->host);
  137. $req .= $this->create_header($this->ACCEPT_KEY, $this->accept);
  138. $req .= $this->create_header($this->CONNECTION_KEY, $this->connection);
  139. $req .= "rn";
  140. }
  141.  
  142. $this->raw_data = $req;
  143.  
  144. return $req;
  145. }
  146. }
  147.  
  148. class JSON_RPC_Response extends JSON_RPC_Message {
  149. var $ERROR_CODES = array(
  150. 'invalid_json' => 1,
  151. 'response_not_ok' => 2,
  152. 'response_malformed' => 3,
  153. );
  154. var $ERROR_MESSAGES = array(
  155. 'invalid_json' => 'The server responded with an invalid JSON object',
  156. 'response_not_ok' => '...',
  157. 'response_malformed' => 'The server responded with a malformed HTTP request'
  158. );
  159.  
  160. var $SERVER_KEY = 'Server';
  161. var $CACHE_CONTROL_KEY = 'Cache-Control';
  162.  
  163. var $server;
  164. var $cache_control;
  165.  
  166. var $http_version;
  167. var $response_code;
  168.  
  169. var $error_code = '';
  170. var $error_message = '';
  171.  
  172. function JSON_RPC_Response($message = '', $error_code = '', $error_message = '') {
  173. parent::JSON_RPC_Message();
  174.  
  175. $this->raw_data = $message;
  176. $this->error_code = $error_code;
  177. $this->error_message = $error_message;
  178.  
  179. $this->VALUE_MAPPINGS[$this->SERVER_KEY] = 'server';
  180. $this->VALUE_MAPPINGS[$this->CACHE_CONTROL_KEY] = 'cache_control';
  181. }
  182.  
  183. function has_errors() {
  184. return (strlen($this->error_code) > 0);
  185. }
  186.  
  187. function parse_response() {
  188. if(strncmp($this->raw_data, 'HTTP', 4) == 0) {
  189. preg_match('/^HTTP/([0-9.]+) (d+) /', $this->raw_data, $response);
  190.  
  191. $this->http_version = $response[1];
  192. $this->response_code = $response[2];
  193.  
  194. if($this->response_code != '200') {
  195. $this->error_code = $this->ERROR_CODES['response_not_ok'];
  196. $this->error_message = substr($this->raw_data, 0, strpos($this->raw_data, "n")-1);
  197. return false;
  198. }
  199. } else {
  200. $this->error_code = $this->ERROR_CODES['response_malformed'];
  201. $this->error_message = $this->ERROR_MESSAGES['response_malformed'];
  202. return false;
  203. }
  204.  
  205.  
  206. $lines = explode("rn", $this->raw_data);
  207. array_shift($lines); // remove first line, as it's not technically a header
  208.  
  209. while (($line = array_shift($lines))) {
  210. if(strlen($line) < 1) { break; }
  211.  
  212. $header = $this->parse_header($line);
  213.  
  214. //echo $this->VALUE_MAPPINGS[$header[0]];
  215. //$k = $this->VALUE_MAPPINGS[$header[0]];
  216. //echo $this->$k;
  217.  
  218. if(isset($this->VALUE_MAPPINGS[$header[0]])) {
  219. $k = $this->VALUE_MAPPINGS[$header[0]];
  220.  
  221. $this->$k = $header[1];
  222. }
  223.  
  224. }
  225. $data = implode("rn", $lines);
  226.  
  227. $this->data_object = $this->parser->decode($data);
  228.  
  229. if(!is_object($this->data_object) || is_null($this->data_object)) {
  230. $this->error_code = $this->ERROR_CODES['invalid_json'];
  231. $this->error_message = $this->ERROR_MESSAGES['invalid_json'];
  232. return false;
  233. }
  234.  
  235. return true;
  236. }
  237. }
  238.  
  239. class JSON_RPC_Server_Response extends JSON_RPC_Message {
  240. var $SERVER_KEY = 'Server';
  241.  
  242. var $server = 'CodeIgniter JSON RPC Server';
  243.  
  244. var $id;
  245. var $error;
  246.  
  247. var $ERROR_CODES = array(
  248. 'bad_call'=>array(
  249. 'code'=> 000,
  250. 'name'=>'Bad call',
  251. 'message'=> 'The procedure call is not valid.'
  252. ),
  253. 'parse_error'=>array(
  254. 'code'=> 000,
  255. 'name'=> 'Parse error',
  256. 'message'=> 'An error occurred on the server while parsing the JSON text comprising the procedure call.'
  257. ),
  258. 'procedure_not_found'=>array(
  259. 'code'=> 000,
  260. 'name'=> 'Procedure not found',
  261. 'message'=> 'The call is valid but the procedure identified by the call could not be located on the service.'
  262. ),
  263. 'service_error'=>array(
  264. 'code'=> 000,
  265. 'name'=> 'Service error',
  266. 'message'=> 'The call is valid, but a general error occurred during the procedure invocation.'
  267. )
  268. );
  269.  
  270. function JSON_RPC_Server_Response($data_object = null) {
  271. parent::JSON_RPC_Message();
  272.  
  273. if($data_object != null) {
  274. $this->data_object = $data_object;
  275. }
  276. }
  277.  
  278. function set_error($error) {
  279. if(is_string($error)) {
  280. $this->error = $this->ERROR_CODES[$error];
  281. } else if(is_array($error)) {
  282. $this->error = $error;
  283. }
  284. }
  285.  
  286. function create_server_response() {
  287. $data = array();
  288. $data['version'] = $this->JSON_RPC_VERSION;
  289.  
  290. if(isset($this->id)) { $data['id'] = $this->id; }
  291. if(isset($this->error)) { $data['error'] = $this->error; }
  292. else { $data['result'] = $this->data_object; }
  293.  
  294. $data = $this->parser->encode($data);
  295. $this->content_length = strlen($data);
  296.  
  297. header("HTTP/1.1 200 OKrn");
  298. header($this->create_header($this->SERVER_KEY, $this->server));
  299. header($this->create_header($this->CONNECTION_KEY, $this->connection));
  300. // header($this->create_header($this->CONTENT_TYPE_KEY, $this->content_type));
  301. header($this->create_header($this->CONTENT_LENGTH_KEY, $this->content_length));
  302.  
  303. $this->raw_data = $data;
  304.  
  305. return $data;
  306. }
  307. }
  308.  
  309. class JSON_RPC_Server_Request extends JSON_RPC_Message {
  310. var $ERROR_CODES = array(
  311. 'invalid_json' => 1
  312. );
  313. var $ERROR_MESSAGES = array(
  314. 'invalid_json' => 'The server responded with an invalid JSON object'
  315. );
  316.  
  317. var $error_code = '';
  318. var $error_message = '';
  319.  
  320. function JSON_RPC_Server_Request($message = '', $error_code = '', $error_message = '') {
  321. parent::JSON_RPC_Message();
  322.  
  323. $this->raw_data = $message;
  324. $this->error_code = $error_code;
  325. $this->error_message = $error_message;
  326. }
  327.  
  328. function has_errors() {
  329. return (strlen($this->error_code) > 0);
  330. }
  331.  
  332. function parse_response() {
  333. $this->data_object = $this->parser->decode($this->raw_data);
  334.  
  335. if(!is_object($this->data_object) || is_null($this->data_object)) {
  336. $this->error_code = $this->ERROR_CODES['invalid_json'];
  337. $this->error_message = $this->ERROR_MESSAGES['invalid_json'];
  338. return false;
  339. }
  340.  
  341. return true;
  342. }
  343. }
  344.  
  345. class JSON_RPC_Client {
  346. var $request;
  347. var $response;
  348.  
  349. var $port = 80;
  350. var $timeout = 5;
  351.  
  352. function JSON_RPC_Client() {
  353. $this->request = new JSON_RPC_Request();
  354. }
  355. function server($url, $request_method = 'POST', $port = 80) {
  356. if (substr($url, 0, 4) != "http") {
  357. $url = "http://".$url;
  358. }
  359.  
  360. $parts = parse_url($url);
  361.  
  362. $path = ( ! isset($parts['path'])) ? '/' : $parts['path'];
  363.  
  364. if (isset($parts['query']) && $parts['query'] != '') {
  365. $path .= '?'.$parts['query'];
  366. }
  367.  
  368. $this->request->path = $path;
  369. $this->request->host = $parts['host'];
  370. $this->request->request_method = $request_method;
  371. $this->port = $port;
  372. }
  373. function method($remote_method) {
  374. $this->request->remote_method = $remote_method;
  375. }
  376. function request($request_parameters) {
  377. $this->request->data_object = $request_parameters;
  378. }
  379. function timeout($timeout = 5) {
  380. $this->timeout = $timeout;
  381. }
  382. function send_request() {
  383. $request = $this->request->create_request();
  384.  
  385. $fp = @fsockopen($this->request->host, $this->port, $errno, $errstr, $this->timeout);
  386.  
  387. if(!$fp) {
  388. $this->response = new JSON_RPC_Response('', $errno, $errstr);
  389. return false;
  390. }
  391.  
  392. fwrite($fp, $request);
  393. $response_text = '';
  394. while (!feof($fp)) {
  395. $response_text .= fgets($fp, 128);
  396. }
  397.  
  398. $this->response = new JSON_RPC_Response($response_text);
  399. fclose($fp);
  400.  
  401. return $this->response->parse_response();
  402. }
  403. function get_response() {
  404. return $this->response;
  405. }
  406. function get_response_object() {
  407. return $this->response->data_object;
  408. }
  409. }
  410.  
  411. class JSON_RPC_Server {
  412. var $php_types_to_jsonrpc_types = array(
  413. 'Boolean'=>'bit',
  414. 'Number'=>'num',
  415. 'String'=>'str',
  416. 'Array'=>'arr',
  417. 'Object'=>'obj'
  418. );
  419.  
  420. var $methods = array();
  421. var $object = false;
  422.  
  423. var $service_name = 'CodeIgniter JSON RPC Server';
  424. var $service_sd_version = '1.0';
  425. var $service_id = '';
  426. var $service_version = '1.0';
  427. var $service_summary = 'A JSON RPC Server for CodeIgniter. Written by Nick Husher ([email protected])';
  428. var $service_help = '';
  429. var $service_address = '';
  430.  
  431. function JSON_RPC_Server() {
  432. $this->methods['system.describe'] = array(
  433. 'function'=>'this.describe',
  434. 'summary'=>'Display relevant information about the JSON RPC server.',
  435. 'help'=>'http://json-rpc.org',
  436. 'return'=>array('type'=>'obj')
  437. );
  438.  
  439. $CI =& get_instance();
  440. $CI->load->helper('url');
  441.  
  442. $this->service_address = current_url();
  443. $this->service_id = current_url();
  444. }
  445.  
  446. function define_methods($methods) {
  447.  
  448. foreach($methods as $methodName=>$methodProperties) {
  449. $this->methods[$methodName] = $methodProperties;
  450. }
  451. }
  452. function set_object($object) {
  453. if(is_object($object)) {
  454. $this->object =& $object;
  455. }
  456. }
  457. function serve() {
  458. global $HTTP_RAW_POST_DATA;
  459.  
  460. $incoming = new JSON_RPC_Server_Request($HTTP_RAW_POST_DATA);
  461.  
  462. if(!$incoming->parse_response()) {
  463. $response = $this->send_error('parse_error');
  464. echo $response->create_server_response();
  465. return;
  466. }
  467.  
  468. $response = $this->_execute($incoming->data_object);
  469.  
  470. echo $response->create_server_response();
  471. }
  472.  
  473. function send_response($object) {
  474. return new JSON_RPC_Server_Response($object);
  475. }
  476. function send_error($error) {
  477. $r = new JSON_RPC_Server_Response();
  478. $r->set_error($error);
  479.  
  480. return $r;
  481. }
  482.  
  483. function _execute($request_object) {
  484. // check if the method is defined on the server
  485. if(!isset($this->methods[$request_object->method])) {
  486. return $this->send_error('procedure_not_found');
  487. }
  488. $method_definition = $this->methods[$request_object->method];
  489.  
  490. // check if we have a function definition
  491. if(!isset($method_definition['function'])) {
  492. return $this->send_error('procedure_not_found');
  493. }
  494.  
  495. $function_name = explode('.',$method_definition['function']);
  496. $is_system_call = ($function_name[0] == 'this');
  497.  
  498. // check if the function/object is callable
  499. if($is_system_call) {
  500. if(!isset($function_name[1]) || !is_callable(array($this, $function_name[1]))) {
  501. $r = $this->send_error('service_error');
  502. // $r->error['code'] = 001;
  503. return $r;
  504. }
  505. } else {
  506. if(!isset($function_name[1]) || !is_callable(array($function_name[0], $function_name[1]))) {
  507. $r = $this->send_error('service_error');
  508. // $r->error['code'] = 002;
  509. return $r;
  510. }
  511. }
  512.  
  513. // check parameters
  514. if(isset($request_object->params)) {
  515. $parameters = $request_object->params;
  516. } else {
  517. $parameters = array();
  518. }
  519.  
  520. if(isset($method_definition['parameters']) && is_array($method_definition['parameters'])) {
  521. $parameters = $method_definition['parameters'];
  522.  
  523. for($i = 0; $i < count($parameters); $i++) {
  524. $current_parameter = $parameters[$i];
  525. if(!isset($current_parameter['name'])) {
  526. $r = $this->send_error('service_error');
  527. // $r->error['code'] = 003;
  528. return $r;
  529. }
  530.  
  531. if(!isset($parameters->$current_parameter['name'])) {
  532. return $this->send_error('bad_call');
  533. }
  534.  
  535. if(isset($current_parameter['type']) &&
  536. gettype($parameters->$current_parameter['name']) != $current_parameter['type'])
  537. {
  538. return $this->send_error('bad_call');
  539. }
  540. }
  541. }
  542.  
  543. // call the function
  544. if($is_system_call) {
  545. $response = $this->$function_name[1]($parameters);
  546. } else {
  547. if(is_object($this->object)) {
  548. $response = $this->object->$function_name[1]($parameters);
  549. } else {
  550. $r = $this->send_error('service_error');
  551. // $r->error['code'] = 003;
  552. return $r;
  553. }
  554. }
  555.  
  556. if(isset($request_object->id)) {
  557. $response->id = $request_object->id;
  558. }
  559.  
  560. return $response;
  561. }
  562.  
  563. // system functions
  564. function describe() {
  565. $method_property_names = array(
  566. 'parameters'=>'params',
  567. 'summary'=>'summary',
  568. 'help'=>'help',
  569. 'return'=>'return'
  570. );
  571.  
  572. $description = array();
  573.  
  574. $description['sdversion'] = $this->service_sd_version;
  575. $description['name'] = $this->service_name;
  576. $description['id'] = $this->service_id;
  577. $description['version'] = $this->service_version;
  578. $description['summary'] = $this->service_summary;
  579. $description['help'] = $this->service_help;
  580. $description['address'] = $this->service_address;
  581.  
  582.  
  583. $description['procs'] = array();
  584. foreach($this->methods as $method_name=>$method_properties) {
  585. $method = array();
  586. $method['name'] = $method_name;
  587.  
  588. foreach($method_property_names as $name=>$property_name) {
  589. if(isset($method_properties[$property_name])) {
  590. $method[$property_name] = $method_properties[$name];
  591. } else if($name == 'parameters' || $name == 'return') {
  592. $method[$property_name] = 'any';
  593. }
  594. }
  595.  
  596. $description['procs'][] = $method;
  597. }
  598.  
  599. return $this->send_response($description);
  600. }
  601. }
  602.  
  603. class JSON_RPC_Parser {
  604. function encode($val) {
  605. return json_encode($val);
  606. }
  607. function decode($val) {
  608. return json_decode($val);
  609. }
  610. }
  611.  
  612. ?>

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.