Posts Tagged spidermonkey

Spidermonkey in PECL

Today I finally released spidermonkey on PECL, you can find it here: http://pecl.php.net/package/spidermonkey/

The whole thing should work fine, I’m already working on several optimisations thanks to jorendorff from Mozilla on IRC. The next thing is to provide better error report, compilation/decompilation of scripts and more default functions for a bunch of things like Iterators and the like.

The last thing I added is a bunch of function applied to streams, here is an exemple:

/* create context */
$ctx = new JSContext();
/* provide fopen/fclose functions */
$ctx->registerFunction('fopen');
$ctx->registerFunction('fclose');
/* for output */
$ctx->registerFunction('printf');

Then in Javascript:

/* open file */
fd = fopen('somefile.txt', 'r')
while (line = fd.getline()) {
  printf("%s\n", line)
}
fclose(fd)

Or

/* open file */
fd = fopen('somefile.txt', 'w')
/* here fd.SEEK_SET is the default value and is optional */
fd.seek(1024, fd.SEEK_SET)
fd.write("some text here :D")
fclose(fd)

, , , , ,

15 Comments

Current status and API of spidermonkey

Spidermonkey had many features and changes done this week, so let’s look at what it can actually do, and what it can’t.

Features

  1. Execute Javascript ( well, of course it does :D )
  2. Register PHP functions to use in Javascript
  3. Assign PHP values in Javascript
  4. Register Classes in Javascript, allowing the user to instanciate them.

Just with those functionalities, you can already do a great deal of things with JS, and extend it really easily.

Conversion

When working with each others, Javascript convert is types to Zval ( PHP native type ) and vice-versa. Most types are successfully converted, but there are some exceptions:

  • Javascript arrays are converted to stdClass due to the fact that they are stored as Objects by Spidermonkey.
  • Javascript closures are not yet supported, but because PHP 5.3 support closures too, it should be done soon.
  • Javascript regexp are not supported either, because there are no equivalents on the PHP side. In the end they’ll be converted to string.

Streams resources will soon have a prototype added by default with functions like read(), getline(), write(), etc…
Objects extending the Iterator abstract will also have a forEach function defined.

API

Here is the API:

class JSContext {
    /* evaluate javascript source code
     *
     * @param string $script             A string containing the javascript source code
     * @return mixed                     The last value in the global scope is returned to PHP
     */

    public mixed  evaluateScript(string $script);

    /* register a function for use in javascript
     *
     * @param callback $callback         A valid callback that will be called
     * @param string   $name             The name under which the function will appear in Javascript,
     *                                   mandatory for closures and recommanded for array($obj, 'function')
     */

    public void   registerFunction(callback $callback [, string $name]):

    /* register a class for use in javascript so that it can be instancied using "new"
     *
     * @param callback $class_name       A valid class to export
     * @param string   $exported_name    The name under which the class will appear
     */

    public void   registerClass(string $class_name [, string $exported_name])

    /* register a variable for use in javascript
     *
     * @param string   $name             The name of the variable in Javascript
     * @param mixed    $value            Value for the variable, objects  and resources
     *                                   are passed by reference
     */

    public void   assign(string $name, mixed $value)

    /* those function allow you to play with the version the engine is running
     * it's not totally compatible and most versions will not be loaded by
     * the engine, you can still have a try with the constants defined above */

    public mixed  setVersion(long $version)
    public long   getVersion()
    public string getVersionString(long $version)
}

This is the current status at revision 38. Next revisions will be aimed at bug correction and error management. If you have any ideas for features, feel free to comments this post.

, , ,

1 Comment

New feature for spidermonkey: registerClass()

As I explained in my previous blogpost, you can export objects to spidermonkey by using the assign() function on this object. While it’s enough for basic cases, It’s not enough for complex situations where Javascript need to create itself objects instances.

Solution

Tonight I worked on the solution named “registerClass()“. As examples are better than words, here is how it works:

/* first create context */
$ctx = new JSContext();
/* register mysqli */
$ctx->registerClass('mysqli');

Then in your javascript source:

db = new mysqli('host', 'user', 'pass', 'db')
res = db.query('SELECT * FROM t1');
while (line = res.fetch_assoc())
{
    // do something with line
}

As you can see, this function allow you to instanciate objects yourself in Javascript, providing more power and flexibility. This function will be available tomorrow morning in the SVN.

, , , ,

1 Comment

Bringing Javascript to the server using PHP * update *

I’ve been working for the last month on my first PHP extension since 2 years ago, a nice way to learn the new stuff Zend had in store for PHP 5.3. This extension is named SpiderMonkey, and embed the original engine made by Mozilla and available on most Linux distributions.

SpiderMonkey

SpiderMonkey is the Javascript engine used by Mozilla’s main products. It provide a nice C API for executing and interacting with Javascript. I base myself on the 1.7.0 version, which is the latest stable version. 1.8.0 is supposed to come soon but is still under development.

Usage

The API is straight-forward, a Javascript Runtime is first created, it’s a container for all “global” variables and Javascript Contexts. Contexts is the global scope where your program runs, all scripts executed on the same contexts will share the same global variables.

This part changed in rev. 38, see below

/* Initialisation of extension */
$rt  = new JSRuntime();
/* Create a single context, most application will only use
* one context while a server ( like the pinetd httpd server )
* would create a context by request */

$ctx = $rt->createContext();

Since rev. 38, you don’t need to instanciate the JSRuntime anymore

/* Create a single context, most application will only use
* one context while a server ( like the pinetd httpd server )
* would create a context by request */

$ctx = new JSContext();

Once the context created, you’ll want to do two things:

  1. Assign values from PHP to JS
  2. Register functions from PHP to JS

For now, variables assigned from PHP are converted to a new Javascript variable and changing them in JS will not affect the original PHP value, while it’s supposed to be allowed later ( using a different function that will assign the variable as a reference and not a copy ), it’s already enough to provide $_GET, $_POST, $_FILES and the likes to JS. The only case where this change is for objects and resources, which keeps pointers to themselves thus allowing you to export a $dom variable containing a DOMDocument and acting on it by the mean of the object methods ( the object properties are not available ).

Exported functions are declared in the global scope.

Here is an exemple with mysqli:

/* Create a new database object */
$mysqli = new mysqli("host", "user", "pass", "db");
/* Allow Javascript to call sprintf under the name sprintf */
$ctx->registerFunction('sprintf', 'sprintf');
/* Provide our db connexion to Javascript */
$ctx->assign('db', $mysqli);

Then in Javascript you’d do:

/* i know the sprintf is not the best way to do it, but for readability
* i'll stick with it in this sample */

res = db.query(sprintf('SELECT * FROM my_table WHERE field = %s', db.escape_string(dynamic_data)))
if (res) {
    while (line = res.fetch_assoc())
    {
        printf("%s\n", line.field2);
    }
}

Because this is based on PHP 5.3, you could also use closures in the assign function, allowing things like:

$ctx->registerFunction('foo', function() { echo "bar"; });

Allowing you to create a function foo() that echo “bar” when called.

Future

A lot of new features are already being implemented, here is a non-exhaustive list:

  1. Allowing to pass all variables as reference, allowing the Javascript to modify the PHP value
  2. Allow to register “classes” prototypes, like:
    $ctx->registerClass('DOMDocument');

    Then you could just do “var a = DOMDocument();” in Javascript to instanciate the object.

  3. Autoregister a prototype on some types of resources. A Stream resource could then be assigned in JS, and you could call stream.write, stream.read, etc…

How-to test ?

The whole source code is available on a public read-only SVN: https://ookoo.org/svn/pecl-spidermonkey. Be careful, you need PHP 5.3.0 beta 1 to compile it ( it will not even compile on PHP 5.2 ). There may be some issues when dealing with many objects so don’t use it for production purpose.

svn co https://ookoo.org/svn/pecl-spidermonkey
cd pecl-spidermonkey
phpize
./configure
make
./test.sh

, , , , , ,

9 Comments