PageRenderTime 210ms CodeModel.GetById 101ms app.highlight 12ms RepoModel.GetById 89ms app.codeStats 0ms

/examples/jsonrpc/public/services/phpolait/phpolait.php

http://pyjamas.googlecode.com/
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?>