/examples/jsonrpc/public/services/phpolait/phpolait.php
PHP | 486 lines | 179 code | 55 blank | 252 comment | 30 complexity | b44f6499ac5b62117de2a9fa431696da MD5 | raw file
1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 4 5/** 6 * JSON RPC Wrapper for PHP Objects. 7 * 8 * PHP versions 4 and 5 (not tested under 5) 9 * 10 * LICENSE: LGPL 11 * 12 * Version: 0.5.1 13 * 14 */ 15 16/** 17 * Utility Function. Returns an includer-relative path to a file that is relative to the CURRENT file. 18 * NOTE: They MUST both be in the same virtual root, I think - crossing virtual servers or aliases will 19 * cause problems. 20 * 21 */ 22function getIncluderRelativePath($relFromHere, $thisDir = null, $callingDir = null) { 23 // Absolute URL's are always absolute 24 if (($relFromHere{0}=='/') || (substr($relFromHere,0,7)=="http://") || (substr($relFromHere,0,8)=="https://")) 25 return $relFromHere; 26 /* 27 * The concept: 28 * The includING file is : /var/html/test/serverRoot/relDir/example/includer.php 29 * The URL path of includer.php is: /relDir/example/includer.php 30 * This file is: /var/html/test/serverRoot/relDir/lib/this.php 31 * 32 * Reduce the directories to 33 * includeING file: /example 34 * included file: /lib 35 * Now for each directory we must strip from the including file's path, we have to add a parent to the destination file. 36 */ 37 if ($thisDir==null) 38 $thisDir = str_replace("\\","/", dirname(__FILE__)); // This is the current directory of this file 39 if ($callingDir==null) 40 $callingDir = str_replace("\\", "/", realpath(".") ); // PATH_TRANSLATED under PHP4 41 42 // Reduce to the path elements that differ 43 $thisPaths = explode('/', $thisDir); 44 $callingPaths = explode('/',$callingDir); 45 $maxElements = max(count($thisPaths), count( $callingPaths)); 46 $i = 0; 47 while (($thisPaths[$i]==$callingPaths[$i]) && ($i<$maxElements)) { 48 $i++; 49 } 50 51 // Now from the calling page, we must go up by count($callingPaths)-$i 52 $relPath = str_repeat("../", count($callingPaths)-$i); 53 $relPath .= implode("/", array_slice($thisPaths,$i)); 54 return $relPath . "/" . $relFromHere; 55} 56 57/** 58 * Location and filename of the JSON-PHP library. If you're using PHP-JSON (the C 59 * extension for PHP), you don't need to worry about this value. 60 * Note, you can also set this value dynamically in the $config['jsonlib'] parameter passed 61 * to the constructor of JSONRpcServer. 62 */ 63define(JSON_PHP_FILE, "JSON.php"); 64 65/** 66 * Root location of the jsolait libraries. The jsolait directory should exist off this location. So, you should 67 * find init.js as ${JSOLAIT_ROOT}/jsolait/init.js. 68 * Note, you can also set this value dynamically in the $config['jsolaitlib'] parameter passed 69 * to the constructor of JSONRpcServer. 70 */ 71define(JSOLAIT_ROOT, getIncluderRelativePath(".")); 72 73/** 74 * Requirement for the HttpWrapper classes. 75 */ 76require_once("httpwrap.php"); 77 78/** 79 * Simple function to return true if this is PHP version 5 or greater. 80 */ 81function isPHP5() { 82 return (!version_compare(phpversion(), "5.0", "<")); 83} 84 85 86/** 87 * JSON RPC Wrapper for PHP Objects. 88 * 89 * JSON RPC Wrapper function that JSON-RPC enables any PHP class, transparently bridges PHP and JavaScript using Ajax and JSON, and provides 90 * a PHP-JSON-RPC proxy class for transparently calling JSON Servers across PHP. 91 * 92 * @author Craig Mason-Jones <craig@lateral.co.za> 93 * @copyright 2006 Craig Mason-Jones 94 * @link http://www.sourceforge.net/projects/phpolait 95 * @since File available since Release 0.5 96 * Author: Craig Mason-Jones (craig@lateral.co.za) 97 * 98 * Requirements: 99 * JSON.php (from PEAR: http://mike.teczno.com/json.html) 100 * or php-json (from http://www.aurore.net/projects/php-json/) 101 * jsolait (from http://jsolait.net) 102 * 103 * Usage: server / client integration. 104 * 105 * 1. Create the JSONRpcServer object that will handle any incoming JSON-RPC requests, serving the methods off the given object. 106 * $server = JSONRpcServer( $object_to_proxy ); 107 * 2. Insert the appropriate javascript into your HTML source so that you can call name_of_jsproxy.method(params) to call any of your methods on the proxied PHP object. 108 * $server->javascript( name_of_jsproxy ); 109 * 110 * Usage: JSON-Rpc Server 111 * 112 * 1. Create the JSONRpcServer object that will handle any incoming JSON-RPC requests, serving the methods off the given object: 113 * $server = JSONRpcServer( $object_to_proxy ); 114 * 115 * Usage: Call remote JSON-Rpc Services 116 * 117 * 1. Create a JSONRpcProxy 118 * $proxy = new JSONRpcProxy("server.php"); 119 * 2. Call remote methods: 120 * list( $result, $error, $errorAdditional) = $proxy->echo('This is an echo') ; 121 * if ($result!=null) { 122 * echo $result; 123 * } else { 124 * echo "ERROR OCCURRED: $error<hr />$errorAdditional<hr />"; 125 * } 126 * 127 * That's it. See doc/index.html for more advanced reference and additional classes. 128 * 129 */ 130 131// {{{ PHPJsonWrapper 132 133/** 134 * Wrapper for the appropriate JSON Libraries. 135 * 136 * This utility class wraps the JSON encode / decode libraries. If you are using 137 * the JSON.php PEAR package (http://mike.teczno.com/json.html), you will need to 138 * set the JSON_PHP_FILE above to locate the package appropriately. If you have 139 * installed the php-json C module (http://www.aurore.net/projects/php-json/), the 140 * PHPJsonWrapper class will locate it and use the C language PHP functions for JSON 141 * encoding / decoding. 142 * 143 * @author Craig Mason-Jones <craig@lateral.co.za> 144 * @link http://www.sourceforge.net/projects/phpolait 145 * @since Class available since Release 0.5 146 * 147 * @access protected 148 */ 149class PHPJsonWrapper { 150 // {{{ properties 151 152 /** 153 * Services_JSON object used if the json module is not loaded. 154 * 155 * If the module 'json' is not loaded, the JSON.php file is included, and its 156 * Services_JSON class is used to provide JSON encoding and decoding. 157 * This object will hold an instance of the class for that encoding / decoding. 158 * 159 * @var Object 160 */ 161 var $json; 162 163 /** 164 * Boolean indication of whether to use the json module, or the Services_JSON class from 165 * JSON.php 166 * 167 * If the module 'json' is located, the json_encode and json_decode functions are used 168 * for json encoding and decoding. 169 * 170 * @var boolean 171 */ 172 var $use_module; 173 174 // }}} 175 176 /** 177 * Constructor determines whether the json module is loaded, and uses the json module 178 * functions if it is. Otherwise, the JSON.php PEAR module is used. 179 */ 180 function PHPJsonWrapper($json_php_file) 181 { 182 $this->use_module = extension_loaded('json'); 183 if (!$this->use_module) { 184 include_once $json_php_file; 185 $this->json = new Services_JSON(); 186 } 187 } 188 189 /** 190 * Decodes the given JSON string. 191 * 192 * Decodes the given JSON string using either the json module or the 193 * Services_JSON class from JSON.php. 194 * 195 * @param string $str The string to decode. 196 * @return Object The decoded JSON object. 197 */ 198 function decode($str) 199 { 200 if ($this->use_module) { 201 return json_decode($str); 202 } else { 203 return $this->json->decode($str); 204 } 205 } 206 207 /** 208 * JSON encodes the given PHP object. 209 * 210 * Encodes the given PHP object into a JSON string. 211 * If the json module is available, it is used, otherwise the Services_JSON class 212 * from JSON.php is used. 213 * 214 * @param Object $obj The object to JSON encode. 215 * @return string The PHP object in JSON encoding. 216 */ 217 function encode($obj) 218 { 219 if ($this->use_module) { 220 return json_encode($obj); 221 } else { 222 return $this->json->encode($obj); 223 } 224 } 225} // End of class PHPJsonWrapper 226 227// }}} 228 229// {{{ JSONRpcServer 230/** 231 * Serves the methods on the given object as JSON RPC methods if a JSON-Rpc request is detected. 232 * 233 * Exposes any methods, or a given list of methods, on the given object, as JSON 234 * RPC methods. Accepts an incoming JSON request across HTTP, and outputs the 235 * JSON-encoded response string, terminating script processing. 236 * NB: If a request has been made, it terminates processing of the page. 237 * If a request has not been made, processing continues, and the class is prepared to insert the PHP-Javascript code to provide transparent server-side calls in client-side 238 * javascript. 239 * 240 * @author Craig Mason-Jones <craig@lateral.co.za> 241 * @copyright 2006 Craig Mason-Jones 242 * @license LGPL 243 * @version Release: 0.5 244 * @link http://www.sourceforge.net/phpolait 245 * @since Class available since Release 0.5 246 * 247 * @access public 248 */ 249class JSONRpcServer 250{ 251 // {{{ properties 252 /** 253 * The object that is to be JSON served. 254 * @var object 255 */ 256 var $object; 257 258 /** 259 * Contains a mapping of actual method names to desired method names. 260 * @var Associative Array 261 */ 262 var $methodMap; 263 264 265 /** 266 * Path to the jsolait library. 267 * 268 * @var string or null 269 */ 270 var $jsolaitlib; 271 272 /// }}} 273 /** 274 * Constructor will serve any JSON-RPC request received and terminate processing, or return 275 * control to the page to continue. 276 * @param Object $object The object whose methods will be made available for JSON RPC calls. 277 * @param Array $methodMap An optional associative array that can be used to map RPC method 278 * names to object methods, permitting renaming of methods. This is 279 * useful for providing PHP reserved words as methods, such as 'echo', 280 * and can be used for restricting access to methods. If this parameter 281 * is provided, but a method is not listed in the array, access to the method 282 * is denied. 283 * @param Array $config Optional configuration array. Two associative values are supported: 284 * 'jsonlib' The location of the JSON-PHP library file. 285 * 'jsolaitlib' The directory off which jsolait has been installed. 286 * 287 * @return None If a valid JSON RPC Request has been received, JSONRpcServer will return a response and terminate 288 * the page. If no such request has been received, JSON RPC will pass control back to the web page, and 289 * a call to JSONRpcServer::javascript( proxyName ) will insert the appropriate JavaScript proxy code into your 290 * web page source. 291 * 292 */ 293 function JSONRpcServer($object, $methodMap = null, $config = null) { 294 /* 295 * NOTE: The request object ($request) is parsed into an object, but the response object 296 * is an associative array. Writing this code, this distinction caused me headaches. Just a 297 * warning :-) 298 */ 299 300 $this->jsonlib = JSON_PHP_FILE; 301 $this->jsolaitlib = JSOLAIT_ROOT; 302 303 if ($config!=null) { 304 if (array_key_exists("jsonlib", $config)) { 305 $this->jsonlib = $config["jsonlib"]; 306 } 307 if (array_key_exists("jsolait", $config)) { 308 $this->jsolaitlib = $config["jsolait"]; 309 } 310 } 311 $json = new PHPJsonWrapper($this->jsonlib); 312 313 $additionalMethods = array(); 314 315 $input = file_get_contents("php://input"); 316 $request = $json->decode($input); 317 318 /* 319 * If we have no request object, we are processing our page, so prepare the js Wrappers 320 */ 321 if ($request==null) { 322 $this->object = $object; 323 $this->methodMap = $methodMap; 324 return; 325 } 326 327 $return = array ( 328 "id" => $request->id, 329 "result" => null, 330 "error" => null 331 ); 332 333 /* We've got the incoming JSON request object in request - we need to identify the method and the parameters */ 334 $method = $request->method; 335 336 /* The methodMap parameter can convert a named method as follows: 337 * string => string - simply rename the method 338 * string => anything else - permit access to the method (the actual boolean value does not matter) 339 */ 340 if ($methodMap!=null) { 341 if (array_key_exists($method, $methodMap)) { 342 if (is_string($methodMap[$method])) { 343 $method = $methodMap[$method]; 344 } 345 } else { 346 $return['error'] = "No such method (" . $method . ") permitted on this server."; 347 return $json->encode($return); 348 } 349 } 350 351 if (is_object($object)) { 352 if (!method_exists($object, $method)) { 353 $return['error'] = "No such method (" . $method . ") exists on this server."; 354 } else { 355 /* 356 * TODO: Try to catch an error in the call: use set_error_handler and restore_error_handler...? 357 */ 358 $return['result'] = call_user_func_array(array(&$object, $method), $request->params); 359 } 360 } else { 361 decho("/* object = $object */"); 362 if (!function_exists($method)) { 363 $return['error'] = "No such function (" . $method . ") exists on this server."; 364 } else { 365 $return['result'] = call_user_func_array($method, $request->params); 366 } 367 } 368 print ($json->encode($return)); 369 exit(0); 370 } 371 372 /** 373 * Add a method on a different URL that one wants to access 374 */ 375 function addMethod($url, $method, $methodName) { 376 if ($methodName==null) $methodName = $method; 377 array_push($this->additionalMethods, array ("url"=>$url, "method"=>$method, "name"=>$methodName)); 378 } 379 380 381 /** 382 * Prepares the javascript wrappers that will be presented on the client side. 383 * @param string $proxyvar The name of the proxy variable for accessing the JSON-RPC methods. 384 */ 385 function javascript($proxyvar) { 386 if ($this->methodMap==null) { // This is the easy case 387 $methods = get_class_methods( $this->object ); 388 } else { 389 $methods = array_keys( $this->methodMap ); 390 } 391 $this->jsWrapperHeader( $_SERVER["PHP_SELF"], $methods, $this->jsolaitlib); 392 foreach ($methods as $name) { 393 $this->jsWrapperMethod($name); 394 } 395 $this->jsWrapperFooter($proxyvar); 396 } 397 398 /** 399 * @param string $pageUrl URL of this page. 400 * @param array $methodArray List of methods to be called on the server. 401 */ 402 function jsWrapperHeader($pageUrl, $methodArray, $jsolaitPath) { 403 $header = <<<EOJS 404<script type="text/javascript" src="$jsolaitPath/jsolait/init.js"></script> 405<script type="text/javascript" src="$jsolaitPath/jsolait/lib/urllib.js"></script> 406<script type="text/javascript" src="$jsolaitPath/jsolait/lib/jsonrpc.js"></script> 407<script language="javascript"> 408 409function PHPOLait() { 410 var serviceURL = "$pageUrl"; 411 var methods = [%METHODLIST%]; 412 var jsonrpc = null; 413 var server = null; 414 try{ 415 jsolait.baseURL = '$jsolaitPath'; 416 jsolait.libURL = '$jsolaitPath/jsolait'; 417 jsonrpc = importModule("jsonrpc"); 418 server = new jsonrpc.ServiceProxy(serviceURL, methods); 419 }catch(e){ 420 reportException(e); 421 throw "importing of jsonrpc module failed."; 422 } 423 424 this._doJSON = function(method, args) { 425 try { 426 return server[method].apply(server,args); 427 } catch (e) { 428 alert(e); 429 } 430 } 431 432EOJS; 433 434 $header = str_replace("%PAGE_NAME%", $pageUrl, $header); 435 $methodList = "'" . implode($methodArray, "','") . "'"; 436 $header = str_replace("%METHODLIST%", $methodList, $header); 437 438 print $header; 439 } 440 441 /** 442 * Closes the class definition and sets the global variable for accessing the methods. 443 * @param string varName Name of the global variable by which to access the JSON methods. 444 */ 445 function jsWrapperFooter($varName) { 446 print <<<EOJS 447 448} 449 450var $varName = new PHPOLait(); 451</script> 452EOJS; 453 } 454 455 function jsWrapperMethod($method) { 456 print <<< EOJS 457 this.$method = function() { return this._doJSON('$method', arguments); }; 458 459EOJS; 460 } 461 462} 463 464 465// }}} // end of class JSONRpcServer 466 467/** 468 * 469 */ 470// {{{ JSONRpcProxy 471 472 473if (isPHP5()) { 474 require_once("rpcproxy.php5"); 475} else { 476 require_once("rpcproxy.php4"); 477} 478 479/* 480 * Local variables: 481 * tab-width: 4 482 * c-basic-offset: 4 483 * c-hanging-comment-ender-p: nil 484 * End: 485 */ 486?>