PHP-O-Lait
Two Lines Client-Side JavaScript to Server-Side PHP
News
PHP-O-Lait version 0.5.1 is released, with a bug fix that caused the
jsolait.js
JavaScript library
not to be included
correctly in certain circumstances. Many thanks to Olaf Bottek for locating the bug and assisting in
correcting it. A few documentation changes have also been made, including an example of using
PHP-O-Lait for asynchronous communication: thanks to Keith Powell for the idea.
If you enjoy PHP-O-Lait, please drop me a line at
craig@lateral.co.za
What is PHP-O-Lait?
PHP-O-Lait, in two boiler-plate lines of code (three if you include the 'require_once') provides a transparent bridge between server-side PHP methods and client-side JavaScript code.
Here's an example of PHP-O-Lait in action:
examples/simple.php
<?php
require_once("phpolait/phpolait.php"); /* First line */
class MyClass {
function hi() {
return "Hello, World!";
}
}
$server = new JSONRpcServer(new MyClass()); /* Second Line */
?>
<html>
<head>
<?php $server->javascript("test"); /* Third Line */ ?>
</head>
<body onLoad="alert(test.hi());">
That's it!
</body>
</html>
Under the hood, PHP-O-Lait uses
Ajax to send the request and receive the response, and
JSON for the data encoding / decoding. You don't need to know what either of these are to use PHP-O-Lait, but Ajax is a bag-of-tricks using client-side JavaScript to communicate with a server without loading a new page in the browser, and JSON is a light-weight data transfer protocol that is much more bandwidth-friendly than XML, and, I think, better suited to loosely-typed languages such as PHP and JavaScript.
Licence
PHP-O-Lait is licenced under the
LGPL, as is
jsolait (which is included in the PHP-O-Lait distribution).
JSON-PHP is also included in the PHP-O-Lait distribution, and it is licenced under a
BSD licence.
Download
All files are on the
SourceForge Download Page.
Version History
Version | Date | Notes |
0.5.1 | 22 February 2006 |
Bug fix for javascript library inclusion in certain circumstances. Some documentation changes.
|
0.5.00 | 12 January 2006 |
First public release. |
Installation
Installation should be straightforward: unzip the zip file into your webserver directory. I presume, for this documentation, that it's in the root of your webserver, but it should work correctly anywhere. You will have three directories:
/doc
, which contains this documentation,
/phpolait
, which contains everything you need to write PHP-O-Lait enabled applications, and
/examples
, which contains the some examples.
Configuring PHP-O-Lait
PHP-O-Lait should require no configuration at all (thanks to the kindness of Jan-Klaas Kollhof, author of
jsolait, and Michal Migurski, author of
JSON-PHP), since all required packages are bundled in the
phpolait
directory.
Optional Libraries
If you want to use JSON-RPC calls to other JSON-RPC servers, PHP-O-Lait has built-in support for native PHP http requests and for the Curl library. If you have the Curl extension installed, PHP-O-Lait will use is automatically. You can also use your own Http classes, if you have particular http request requirements on your server. This process is described in
JSONRpcProxy.
You can also install the
PHP-JSON C extension for PHP. This is
much faster than the bundled JSON-PHP library, but you need to be able to install extensions on your PHP server. Once installed, PHP-O-Lait will automatically detect and use this library in preference to the bundled library.
Usage
Transparent Browser-Server Proxies
Using PHP-O-Lait is straightforward. Both the server-side PHP code and the client-side JavaScript code are placed in the same page. The page is divided into three sections:
- Code a class that provides all the server-side functionality your page will need.
- Three boilerplate steps:
- Instantiation of
JSONRpcServer
to JSON-RPC enable the PHP class: $server=new JSONRpcServer(new MyClass());
- Start of HTML page:
<html><head>
- Include PHP-O-Lait generated JavaScript proxy to access your server-side class:
<?php $server->javascript('proxy'); ?>
- Write the rest of your HTML page, in JavaScript and HTML.
The first step, defining the PHP class that provides the server-side functionality (call it
MyClass
), is the developers, as is the third step, defining the client-side HTML and JavaScript. The middle second step is boilerplate, except for some configuration or access-restrictions you might like to add:
$server = new JSONRpcServer(new MyClass());
?>
<html>
<head>
<?php $server->javascript("myproxy");?>
After this code, you can access your server-side code by calling the methods on the global client-side JavaScript object
myproxy
.
PHP 4.x: Case-Sensitivity
PHP 4.x function and method names, unlike JavaScript, are
case-insensitive. Because of this, in PHP 4.x, all the proxy methods generated for JavaScript are lower-case, irrespective of their case in PHP. If you really don't like this, you can use the
$methodMap
parameter to
rename your methods.
If you're reading this for the first time, I suggest you try some
Examples now to see how simple PHP-O-Lait can be. The next two sections are more advanced functionality of the PHP-O-Lait library.
Asynchronous method calling
Once you have the client side Javascript object, any method can be called asynchronously on your client side by passing a callback function of the form
function callback(result, error) {..}
as the last parameter to the method. Consider this example:
$server = new JSONRpcServer(new MyServer());
?>
<html>
<head>
<?php $server->javascript('server');?>
<script language="javascript">
function callback(res, error) {
alert('res');
}
function sync() {
alert('1');
alert(server.myecho(2));
alert('3');
}
function async() {
alert('1');
server.myecho(2,callback);
alert('3');
}
</script>
In this example, the
sync()
function will show three alerts in the order 1, 2, 3. The
async()
function, on the other hand, will show the three alerts in the order 1, 3, 2, and, since the call is asynchronous, control will revert to the browser between the 3 and the 2.
Using PHP-O-Lait to JSON-Rpc Enable any class
JSON-RPC is a protocol enabling any client to make a remote call to a method on a server. The transfer protocol that PHP-O-Lait supports is, obviously, http, and the encoding of the request and the response is done in JSON (JavaScript Object Notation). The JSONRpcServer class, besides the javascript-generating sugar, can JSON-RPC enable any PHP class. Simply pass an instantiation of the class to a new instance of the JSONRpcServer class.
Example 1
new JSONRpcServer( new MyServerClass() );
That's it. Any method on
MyServerClass
can now be accessed over JSON-RPC by making a JSON-RPC request to the url of your page.
Note that, if the JSONRpcServer class finds a JSON-RPC request that requires processing, it processes that request and terminates the script. The script will only continue if no such request was found.
You can only have one JSONRpcServer per page, both because
JSONRpcServer
terminates the script after it has processed a request, and because multiple classes on a page would not make sense from a caller's perspective.
See
JSONRpcServer for details of additional parameters.
Calling remote JSON-RPC Servers from PHP
PHP-O-Lait also supports the ability to make a remote JSON-RPC call from PHP code. This is done using the
JSONRpcProxy
class.
To call a remote JSON-RPC server, simply create an instance of
JSONRpcProxy
with the Url of the server.
$proxy = new JSONRpcProxy("http://jsolait.net/testj.py");
list ($result, $error) = $proxy->echo("Testing JSONRPC on Jan-Klaas Kollhof's server!");
?>
Any method called through
JSONRpcProxy
returns 3 results in an array, a result that is the value the call returned, an error, that will be null if no error occurred, and extended error information that is the actual response from the remote server. This is useful for debugging, since, if there is an error in a JSON-RPC server code, no JSON result might be returned, but an error code could be generated. In this case, result would be null, error would indicate that the call failed, and the extended error information would contain the actual response received from the server.
Note that a
null
result does not mean that an error occurred: a method can quite legitimately return
null
. An error occurred only if the error value returned is non-
null
.
Examples
The code for these examples is in the
/examples
directory.
Hello, World-O-Lait!
Very simple example of using JSON-RPC to retrieve a value from the server. [
Demo]
examples/helloworld.php
<?php
require_once("../phpolait/phpolait.php");
class MyClass {
function hi() {
return "Hello, World-O-Lait!";
}
}
$server = new JSONRpcServer(new MyClass());
?>
<html>
<head>
<?php $server->javascript("test"); ?>
</head>
<body onLoad="alert(test.hi());">
</body>
</html>
Calculator
Demonstrates integration of Server-Side PHP execution and Client-Side JavaScript again, this time with a slightly more interesting example. [
Demo]
examples/calculator.php
<?php
require_once ("../phpolait/phpolait.php");
class MyServer {
function add ($a, $b) {
return $a+$b;
}
}
$server = new JSONRpcServer(new MyServer());
?>
<html>
<head>
<? $server->javascript("proxy"); ?>
<script language="javascript">
function doCalculation() {
var firstValue = document.getElementById('firstValue').value;
var secondValue = document.getElementById('secondValue').value;
document.getElementById('result').value = proxy.add( firstValue, secondValue );
}
</script>
</head>
<body>
<form>
Server-Side Calculator:<br />
<input type="text" id="firstValue" size="5"> +
<input type="text" id="secondValue" size="5">
<input type="button" onClick="doCalculation();" value="=">
<input type="text" id="result" size="5">
</form>
</body>
</html>
Renaming Methods
This demonstrates renaming and restricting method-access. Notice that renaming is being used to introduce capitalisation in JavaScript (the
Echo
method), and also that renaming permits a method to have a name that PHP would not permit (
Echo
is a reserved word in PHP).
The
version
method is available because it appears as a key in the
$methodMap
array. Renaming only occurs if the value is a
string
. If the value is any other type, the method will be accessible by its PHP name (which will be lowercased if you're using PHP 4.x).
[
Demo]
examples/rename.php
<?php
require_once ("../phpolait/phpolait.php");
/**
* Simple test class for the JSON RPC server
*/
class RestrictedServer {
function myecho($msg) {
return $msg;
}
function hidden() {
return "Inaccessible method";
}
function version() {
return "PHP-O-Lait Version 0.5";
}
}
$server = new JSONRpcServer( new RestrictedServer(),
array ("version"=>true, "Echo"=>"myecho")
);
?>
<html>
<head>
<?php $server->javascript('proxy'); ?>
</head>
<html>
<body>
<a href="javascript:alert(proxy.Echo('Echo from server'));">
Echo('Echo from server')</a><br />
<a href="javascript:alert(proxy.version());">
version()</a><br />
<a href="javascript:alert(proxy.hidden());">
hidden()</a>
(Won't produce an error since the JavaScript method does not exist.)<br />
</body>
</html>
Asynchronous Method Calls
Passing a final parameter that is a callback function to a method call will cause the method to be called asynchronously. When the result is available, the callback function is called.
[
Demo]
examples/async.php
<?php
require_once("../phpolait/phpolait.php");
class MyClass {
function myecho($msg) {
return $msg;
}
}
$server = new JSONRpcServer(new MyClass());
?>
<html>
<head>
<?php $server->javascript("test"); ?>
<script language="javascript">
function syncCall() {
alert('syncCall()');
alert( test.myecho('Hello from synchronous call') );
alert('leaving syncCall()');
}
function asyncCall() {
alert('asyncCall()');
test.myecho('Hello from synchronous call', asyncCallback);
alert('leaving asyncCall()');
}
function asyncCallback(res, err) {
alert(res);
}
</script>
</head>
<body>
<a href="javascript:syncCall();">Synchronous call</a><br />
<a href="javascript:asyncCall();">Asynchronous call</a><br />
</body>
</html>
Remote-Server Access
Because a browser restricts our connections to our originating server, we can't fetch arbitrary websites. This examples uses JSON-RPC and an http proxy of sorts on the server to fetch an arbitrary website. [
Demo]
examples/webfetch.php
<?php
require_once ("../phpolait/phpolait.php");
/**
* Proxy reads a URL and returns data to the client. Client-side security restrictions
* prevent this being done client-side.
*/
class WebFetch {
function get($url) {
if (substr($url,0,7)!="http://") {
// Otherwise, could read system files!
return "Access denied";
}
return file_get_contents($url);
}
}
$server = new JSONRpcServer( new WebFetch());
?>
<html>
<head>
<?php $server->javascript("webfetch"); ?>
<script language="javascript">
function fetchWebSource() {
var url = document.getElementById('url').value;
webfetch.showErrors=true;
document.getElementById('html').value = webfetch.get(url);
}
</script>
</head>
<body>
<form>
URL: <input type="text" id="url" size="60"
value="http://www.sourceforge.net/projects/phpolait/">
<input type="button"
onClick="javascript:fetchWebSource();" value="Fetch">
<br />
<textarea rows="25" cols="60" wrap="true" id="html"></textarea><br />
</form>
</body>
</html>
JSON Rpc Services
This demonstrates accessing remote JSON Rpc Services from PHP using
JSONRpcProxy
. [
Demo]
examples/echo.php
<?php
require_once("../phpolait/phpolait.php");
class EchoServer {
function myecho($msg) { return $msg; }
}
$server = new JSONRpcServer(new EchoServer(), array("echo"=>"myecho"));
?>
<html>
<head><?php $server->javascript("proxy"); ?>
<script language="javascript">
function doEcho() {
msg = document.getElementById('msg').value;
document.getElementById('returned').value = proxy.echo(msg);
}
</script>
<body>
<form>
<input type="text" id="msg" value="This echoes">
<input type="button" value="send" onClick="doEcho()" >
<input type="text" id="returned" value="">
</form>
</body>
</html>
examples/jsonrpc.php
<?php
require ("../phpolait/phpolait.php");
$proxy = new JSONRpcProxy(
"echo.php"
);
list ($result, $err, $ext) =
$proxy->echo("Testing JSONRPC echo on PHP-O-Lait's server!");
if ($err!=null) {
echo "ERROR $err: $ext";
} else {
echo $result;
}
?>
Proxy to Another Page
Suppose that I have used PHP-O-Lait to access some class on one web-page and I want to use the same functionality on another page. Obviously, I don't want to duplicate the code. Since the first page uses a class, I could simply re-use the class on another page, or use the class as a base class and add new functionality for the current page, or create proxy methods on the current page's class that calls methods on the first page's class, or JSON-RPC Proxy my way to the third page.
This example demonstrates the latter method (which it not the recommended method), since the other methods are standard OO approaches.
[
Demo]
examples/adduser.php
<?php
require("../phpolait/phpolait.php");
class AddUserServer {
function AddUser($name) {
return "Added user [$name]";
}
};
$server = new JSONRpcServer(new AddUserServer());
?>
<html>
<body>
Regular PHP-O-Lait functionality to add a user.
</body>
</html>
examples/manageuser.php
<?php
require("../phpolait/phpolait.php");
class UserManagementServer {
function AddUser($name) {
$proxy = new JSONRpcProxy("adduser.php");
list ($result, $error) = $proxy->AddUser($name);
return $result;
}
function DeleteUser($name) {
return "Deleted user [$name]";
}
};
$server = new JSONRpcServer(new UserManagementServer(),
array("adduser"=>"AddUser","deleteuser"=>"DeleteUser")
);
?>
<html>
<head>
<?php $server->javascript('server'); ?>
<script language="javascript">
function doClick(act) {
var name = document.getElementById('name').value;
if (act == 'add') alert(server.adduser(name));
if (act == 'del') alert(server.deleteuser(name));
}
</script>
<body>
<form>
<input type="text" id="name">
<input type="button" onClick="doClick('add');" value="Add" >
<input type="button" onClick="doClick('del');" value="Delete" >
</body>
</html>
Reference
new JSONRpcProxy ($server_url [, $HttpWrapper=null [ , $jsonlib=null]] )
- A
JSONRpcProxy
provides a proxy object to make calls on a remote (or local to the current server) JSON RPC server. The $server_url
is the url of the server to be accessed. It can be an absolute or a relative path.
Example 1
MyServer.php
class MyServer {
function add($a,$b) { return $a+$b; }
};
new JSONRpcServer(new MyServer());
MyClient.php
$proxy = new JSONRpcProxy("MyServer.php");
echo "1 + 2 = " . $proxy->add(1,2);
The optional $httpWrapper
parameter can specify a class to provide Http functionality. The HttpWrapper classes HttpWrapper_CURL
and HttpWrapper_PHP
classes are defined in httpwrap.php
, and use the CURL and native PHP sockets respectively. Any HttpWrapper class is expected to provide a single function:
class HttpWrapper_X {
function post($url, $data, $referrer=null) {
// Perform the HTTP Post and return ONLY the body of the HTTP result
};
}
You can use any class of your own making to provide this functionality, and simply pass an instance of your own class as the second parameter to the constructor of the JSONRpcProxy
class.
If no HttpWrapper is provided in the constructor, JSONRpcProxy
uses the HttpWrapper::GetWrapper()
method defined in httpwrap.php
to select a suitable wrapper (CURL if you have the Curl extension installed, native PHP otherwise). If you want to customize PHP-O-Lait for your site, it can be easily achieved by changing this method to return the particular HttpWrapper type class you would like to use.
The optional third parameter, $jsonlib
, points to the location of the JSON.php
JSON-PHP library file. You should never need to use this parameter unless you are restructuring the location of your files for some reason.
new JSONRpcServer ( $object [, $methodMaps=null [, $config=null ]])
- Constructs a new
JSONRpcServer
to provide access to the methods of $object
.
Example 1: Basic Usage
require_once ("jsonrpc.php");
class MyServer {
function add($a, $b) {
return $a+$b;
}
};
$server = new JSONRpcServer(new MyServer());
By default, every method of the object passed to JSONRpcServer will be available as a JSON-RPC Method. Since this might not be ideal, for security or other reasons, the $methodMap
parameter can be used to:
- Restrict access to particular methods, or
- Rename methods.
The $methodMap
parameter consists of an associative array that maps JSON-RPC method names to the actual methods that will be called on the object. If a method name exists as a key in $methodMap
, a method by that name will be accessible to the client code. If a name does not exist as a key, the method will not be accessible. This is useful for security. If a method name in $methodMap
is mapped to a string, then the method will be given the key-name on the client side, but the value-name will be the actual method called. This might seem like a strange idea (why offer method renaming?), but it is in fact useful: it offers the ability to have methods that are PHP-reserved words. The method in Example 2 is called echo
, but you can't write a function called echo
.
Example 2: Renaming Methods and Restricting Access
class ProtectedServer {
function myecho($msg) {
return $msg;
}
function privateFunction($code) {
eval($code);
}
function version() {
return "Version 1.0 alpha";
}
}
$server = JSONRpcServer( new ProtectedServer(), array ("version"=>true, "echo"=>"myecho"));
The final, optional parameter, can be used to specify the locations of the JSON-PHP library (the PHP library) or the jsolait library files.
The full file location of the JSON-PHP library can be specified by passing a jsonlib
key-value in the $config
parameter, and the root directory where the jsolait
directory is located can be specified in a jsolait
key-value. If either is absent, the default is used.
It is extremely unlikely you will ever need to use this parameter.
Example 3: Configuring locations of JSON-PHP and jsolait libraries
$server = JSONRpcServer( new MyServer()
, null
, array ("jsonlib"=>"/libs/jsonlib.php", "jsolait"=>"/libs")
);
JSONRpcServer::javascript ( $varName )
- Once the
JSONRpcServer
has been constructed, the javascript
method inserts the appropriate JavaScript code into the webpage. It takes a single parameter, the name of the global
variable to hold the proxy object.
It is recommended to call the javascript
method in the <head>
tag of your web page, but do not put the call between <script>
tags: the method will insert these itself.
Note: If you're using PHP 4.x, unless you rename methods using the $methodMap
parameter in the constructor of the JSONRpcServer
, all methods will be lowercase in JavaScript. This is because methods in PHP 4.x are case-insensitive.
Example 1
<html>
<head>
<?php echo $server->javascript("proxy"); ?>
</head>
<html>
...
After this code has been inserted, you can code your own Javascript functions, calling you PHP methods on the JavaScript proxy object. PHP-O-Lait will route these calls to the server, execute the method on your PHP object, and return the results as JavaScript values (strings, bools, ints, Arrays or Objects).
Roadmap
For the 0.6 release of PHP-O-Lait I am planning improved error handling and debugging tools, an area where JavaScript is not particularly strong. I am also considering adding the ability to provide JavaScript side connections to other web pages (ie. not just back to one's own page), so that one can access server objects elsewhere on ones site. As discussed above, this might not actually be necessary, since one can achieve this easily using OO techniques.
Thanks
- Special thanks to Jan Kollhof for jsolait and for graciously allowing me to use the PHP-O-Lait name for this project, and to bundle jsolait with this distribution.
- Special thanks to Michal Migurski for developing JSON-PHP and allowing me to bundle JSON-PHP with this distribution.
- Thanks to the folks developing PHP-JSON, which can also be used to provide server-side JSON encoding.
- Thanks to the folks at Sajax, where I shamelessly stole the idea of wrapping all functionality into a single page, and having the JSONRpcServer object generate all the appropriate Javascript proxies.