I’ve been working on Javascript-PHP remote call mapping techniques. A set of PHP classes with static functions are selected as a public interface and automatically exported so that the Javascript can call them. Usually, this kind of mapping involves three difficult points:
- Detecting what functions are exported and building the appropriate JS code.
- Detecting what function the JS is calling.
- Transforming data between JS and PHP.
I solve these with glob(), __autoload and json_encode (respectively) in a little prototype I called JSOS (JavaScript Or Something). jQuery on the client side provides me with synchronous AJAX queries. The code (PHP with echoed Javascript) looks like this, and is placed in a controller:
<?php
function __autoload($classname)
{
require_once($classname . '.inc.php');
}
if (!isset($_POST['cls'])) {
echo '<html><head><title>JSOS</title>';
echo '<script type="text/javascript" src="jquery.js"></script>';
echo '<script type="text/javascript">var jsos={};';
echo '$.ajaxSetup({async:false,timeout:5000});';
echo 'jsos.$=function(c,f,a){';
echo 'var o={exception:"Server disconnect"},';
echo 't={cls:c,func:f};';
echo 'for(var i in a)t[""+i]=a[i];$.post(".",t,';
echo 'function(d){o=d},"json");if("exception" in o)throw o.exception;';
echo 'return o.result};';
foreach (glob('*.inc.php') as $file)
{
$class = str_replace('.inc.php', '', $file);
echo 'jsos.' . strtolower($class) . '={};';
$text = file_get_contents($file);
preg_match_all('/public static function ([A-Za-z_0-9]+)\(([^)]*)\)/',
$text, $func);
foreach ($func[1] as $id => $value) {
echo 'jsos.'.strtolower($class).'.'.strtolower($value).'=function(';
$args = explode(',', $func[2][$id]);
foreach (array_keys($args) as $key) {
$args[$key] = str_replace(array(' ', '$'), '', $args[$key]);
}
$args = array_filter($args);
echo implode(',', $args).'){return jsos.$';
echo '("'.$class.'","'.strtolower($value).'",['.implode(',',$args).'])};';
}
}
echo '</script></head><body></body></html>';
exit;
}
$args = array();
foreach ($_POST as $key => $val) {
if ($key === 'cls')
$cls = $val;
elseif ($key === 'func')
$func = $val;
else
$args[(int)$key] = $val;
}
ksort($args);
if (!class_exists($cls)) {
echo '{exception:"No such package!"}';
exit;
}
if (!method_exists($cls, $func)) {
echo '{exception:"No such method!"}';
exit;
}
try {
$result = call_user_func_array(array($cls, $func), $args);
echo json_encode(compact('result'));
exit;
}
catch (Exception $e) {
$exception = "$e";
echo json_encode(compact('exception'));
exit;
}
All files in the current directory with a ‘.inc.php’ extension are assumed to contain a similarly-named class, and all public static functions of that class are exported. For example, suppose Test.inc.php contains the following definition:
class Test
{
public static function Run($a) { return "$a$a"; }
}
Then, to call the Test::Run function from the above page, one would simply type in Javascript:
jsos.test.run('Hello');
And indeed, with only five lines of code, the result to a client request is computed on the server and displayed on the client again, without issues. Here’s the result running in Firebug:
Further work should include handling errors (right now, if the server encounters an error or outputs non-JSON data, the call will die with a “Server disconnect” exception), which makes debugging easier than having to wade through the Net tab of Firebug.

Hi. I'm Victor Nicollet,
Recent Comments