Using JavaScript in PHP with PECL and SpiderMonkey

The Good Old Days

Not too long ago, it seemed like there was a pretty clear distinction between client-side technologies and server-side technologies. Languages like PHP, Perl and Python resided on the server, taking care of tasks like database connectivity, transaction management and remote procedure calls, while tools like JavaScript, CSS and HTML were used exclusively on the client to render pages, perform whizzy effects and respond to user events.

Things aren’t that clear any longer. Projects like Jaxer and Phobos are blurring these distinctions, by making it possible to run JavaScript on the server and use it for tasks ranging from server-side file access to input validation. And in this article, I’m going to show you how to add a JavaScript engine to your PHP build, with a little help from PECL’s SpiderMonkey extension. Keep reading!

Monkeying Around

SpiderMonkey is an ECMAScript-compliant JavaScript engine, developed and maintained by Mozilla. It is one of the components of the Mozilla Firefox Web browser (in addition to being part of many other projects). It is released as a library that can be easily embedded into other applications.

SpiderMonkey support in PHP comes through PECL’s ext/spidermonkey extension, which is maintained by Christophe Robin, and provides an object-oriented API for accessing the SpiderMonkey library. Although this extension is currently in beta, it still allows you to do some fairly interesting things, including registering and using variables, functions and classes from PHP in JavaScript.

To get started with ext/spidermonkey, you’ll need to first make sure that you’re running PHP v5.3.x (beta or RC will do), as the extension will not work on any earlier version. If you’ve got this, download the SpiderMonkey libraries (v1.7.0) from Mozilla’s FTP site and compile them for your system. Assuming you’re on a *NIX system, here’s how:

Manually install the compiled libraries as below (based on these instructions):

Once this is done, download, compile and install the latest revision of the SpiderMonkey extension from SVN with the phpize command (this article uses revision 51):

This procedure should create a loadable PHP module named in your PHP extension directory. You should now enable the extension in the php.ini configuration file, restart your Web server, and check that the extension is enabled with a quick call to phpinfo():

Putting It All In Context

Once you’ve got the extension installed and working, it’s time to take it for a test drive. Here’s a simple example, which initializes some PHP variables and then assigns them to the JavaScript context:

The script begins by initializing the JavaScript interpreter, or “context”, as a JSContext object. This object provides an entry point into SpiderMonkey, exposing methods that can be used to register PHP objects with JavaScript. One of these methods is the assign() method, which assigns a PHP variable to a JavaScript variable. This method is used in the example above to assign the values of PHP variables $a and $b to corresponding JavaScript variables.

Once these JavaScript variables have been created, they can be used in the normal fashion, as though you were writing regular JavaScript. The example above demonstrates this, writing a one-line JavaScript program that adds them and assigns the result to another JavaScript variable. To executing the JavaScript, simply call the JSContext object’s evaluateScript() method, which takes care of actually running the code under SpiderMonkey. The return value of the evaluateScript() method is the last value in the JavaScript context’s global scope.

It’s important to note, at this point, that the SpiderMonkey library merely provides a JavaScript implementation. While it supports all the core JavaScript data types, objects and methods, it does not provide a windowing toolkit or a DOM implementation (these components are provided by the browser application) and, therefore, you can’t expect to pop up alert boxes or dynamically manipulate HTML pages with it.

Here’s the output of the previous example:

In addition to standard variables, you can also assign arrays to the JavaScript context. Here’s an example:

In this case, the PHP array $friends is assigned to the JavaScript context, and JavaScript array notation is then used, together with the JavaScript Math object, to randomly select and display an array element. Here’s what the output might look like:

The Milkman Cometh

Interestingly, you can also define functions in PHP, and then later use them in a JavaScript code block. Here’s an example, which defines a PHP function to calculate the area of a circle and then invokes this function from within a JavaScript code block:

Here’s the output:

What works with functions, also works with classes. As an illustration, consider this example PHP class:

Consider this next example, which reads and registers the above class definition in the JavaScript context and then creates a JavaScript object from it:

When you try accessing this script, you’ll see one of the following messages, depending on what time it is on the server:

An Object Lesson

You can also register PHP objects with assign(), and use them from JavaScript. Here’s a simple example to whet your appetite:

In this case, a PHP object is initialized, values are set for several properties and it is then assigned to the JavaScript context with assign(). The executeScript() method then runs a JavaScript code block that uses those object properties, printing them to the output device. Here’s what the output looks like:

This isn’t restricted just to user-defined objects either: consider the next example, which creates a SimpleXML object from an XML file and then uses it to access XML elements in the JavaScript context. First, here’s the example XML file:

And here’s the script that uses this XML:

Here, the simplexml_load_file() function is used to create a SimpleXML representation of the source XML document, and the resulting object is transferred to the JavaScript context with assign(). Individual nodes of the XML document can now be accessed in JavaScript as regular object properties. Here’s what the output of the script looks like:

Making Movies

Why stop there? You can also register built-in PHP classes with the JavaScript context, and use them in JavaScript just like you would in PHP. To illustrate, consider the next example, which registers the XMLWriter class in the JavaScript context and then uses it to create an XML document from scratch:

This script registers PHP’s XMLWriter class in the JavaScript context, and then uses it to dynamically create an XML document. Notice that although the script uses JavaScript objects, the procedure and method calls to produce the XML are exactly the same as those you would use in PHP.

Here’s what the output will look like in a Web browser:

It’s not all XML either – you can also register database access classes, like PHP’s SQLite3 or MySQLi classes, and use them in JavaScript. To illustrate, first create a simple SQLite3 database named ‘mydb’, as below

Then, register the PHP SQLite3 class with the JavaScript context, and use it to do the same thing:

This example uses the registerClass() method to register PHP’s SQLite3 class, and the registerFunction() method to register a simple utility function for printing values. It then instantiates a new JavaScript SQLite3 object and uses it to open a handle to the SQLite3 database file created in the previous step. Once this object has been instantiated, it’s possible to use SQLite3 object methods to execute a query on the database and process the resulting data in a while() loop…again, all of this using JavaScript rather than PHP.

Here’s what the output looks like:

As these examples illustrate, PHP’s SpiderMonkey extension makes it easy to add a JavaScript interpreter to your PHP build. It’s particularly good for applications that need a limited, yet extensible, JavaScript implementation, as one of its key features is the ability to register and use custom PHP classes within the JavaScript context. While the extension is still not fully complete – according to the author’s blog, support for Iterators and better error reporting are on the anvil – it nevertheless provides a testbed for some fairly interesting experiments and discussions. Try it out sometime, and see what you think!

Copyright Melonfire, 2009. All rights reserved.