Running JavaScript inside PHP code
Join the DZone community and get the full member experience.
Join For Freev8js is a new PHP extension able to run JavaScript code inside V8, Google's JavaScript interpreter that powers for example Chrome and NodeJS.
This extension is highly alpha - and its API would probably change in the months ahead. Since documentation is lacking, I invite you to repeat the discovering process I follow in this post in case you find some differences in a new version of v8js.
Installation
V8 must be present on the machine in order to install the extension. On a Debian/Ubuntu system, run the following:
sudo apt-get install libv8-dev libv8-dbg
libv8-dev will also install libv8 in its latest version.
Afterwards, download and compile the extension with:
sudo pecl install v8js
There are no more requirements for compilation apart from the usual dependencies for PECL packages, like build-essential.
After that, add:
extension=v8js.so
to php.ini or to a section in conf.d.
$ php -m | grep v8 v8js
will confirm you the extension is loaded.
Some introspection
PHP's reflection let us take a look at the classes and methods provided by this extension, even without documentation available. It probably hasn't been written yet, due to the unstable API.
$ php -r 'var_dump(get_declared_classes());' | grep V8 string(8) "V8Object" string(10) "V8Function" string(4) "V8Js" string(13) "V8JsException" $ php -r '$class = new ReflectionClass("V8Js"); var_dump($class->getMethods());' array(5) { [0]=> &object(ReflectionMethod)#2 (2) { ["name"]=> string(11) "__construct" ["class"]=> string(4) "V8Js" } [1]=> &object(ReflectionMethod)#3 (2) { ["name"]=> string(13) "executeString" ["class"]=> string(4) "V8Js" } [2]=> &object(ReflectionMethod)#4 (2) { ["name"]=> string(19) "getPendingException" ["class"]=> string(4) "V8Js" } [3]=> &object(ReflectionMethod)#5 (2) { ["name"]=> string(17) "registerExtension" ["class"]=> string(4) "V8Js" } [4]=> &object(ReflectionMethod)#6 (2) { ["name"]=> string(13) "getExtensions" ["class"]=> string(4) "V8Js" } } $ php -r '$v8 = new V8Js(); var_dump($v8->executeString("1+2+3"));' int(6)
Interesting! We have just executed our first JavaScript expression inside a PHP process. Apparently the last statement's value is returned by the executeString() method, with a rough conversion preserving the type:
$ php -r '$v8 = new V8Js(); var_dump($v8->executeString("var obj = {}; obj.field = 1; obj.field++; obj.field;"));' int(2)
Syntax or runtime errors are signaled with a V8JsException:
$ php -r '$v8 = new V8Js(); var_dump($v8->executeString("var obj = {"));' PHP Fatal error: Uncaught exception 'V8JsException' with message 'V8Js::executeString():1: SyntaxError: Unexpected end of input' in Command line code:1 Stack trace: #0 Command line code(1): V8Js->executeString('var obj = {') #1 {main} thrown in Command line code on line 1
Let's add more difficulty
The FizzBuzz kata OO solution is an example of JavaScript code creating an object and executing anonymous functions: it's a good test bench for our integration.Since evaluating a variable as the last line returns it, that is our channel of communication, supporting integers, strings, floats, booleans, arrays (not objects at this time). Meanwhile, input for JavaScript code can be embedded into the executed string.
This code will output string(8) "FizzBuzz":
<?php $javaScriptCode = ' function FizzBuzz(correspondences) { this.correspondences = correspondences; this.accept = function (number) { var result = "" for (var divisor in this.correspondences) { if (number % divisor == 0) { result = result + this.correspondences[divisor]; } } if (result) { return result; } else { return number; } } } var myFizzBuzz = new FizzBuzz({3 : "Fizz", 5 : "Buzz"}); myFizzBuzz.accept(15); '; $v8 = new V8Js(); var_dump($v8->executeString($javaScriptCode));
By changing it a bit, we can build a JSON string by backslashing the double quotes ("), and returns it in lieu of an object to communicate to the PHP process a complex result:
... var myFizzBuzz = new FizzBuzz({3 : "Fizz", 5 : "Buzz"}); "{\"15\" : \"" + myFizzBuzz.accept(15) + "\", \"5\" : \"" + myFizzBuzz.accept(5) + "\"}"; '; $v8 = new V8Js(); $result = $v8->executeString($javaScriptCode); var_dump($result); var_dump(json_decode($result));
The output is:
string(33) "{"15" : "FizzBuzz", "5" : "Buzz"}" object(stdClass)#2 (2) { ["15"]=> string(8) "FizzBuzz" ["5"]=> string(4) "Buzz" }
Wiring
Executing an external script would be nice: it would provide better stack traces, with traceable line numbers at the JavaScript level. It would also mean we won't need backslashing for single quotes, simplifying the syntax.
We cannot load scripts on the JavaScript side out of the box, due to V8 missing this functionality (Node JS adds this feature) but we can load the code on the PHP Side:
// test.js $ php loadingfiles.php string(24) "test.js file was loaded." // loadingfiles.php <?php $javascriptCode = file_get_contents("test.js"); $v8 = new V8Js(); $result = $v8->executeString($javascriptCode); var_dump($result);
Execution results in:
$ php loadingfiles.php string(24) "test.js file was loaded."
However, we still miss the capability of including JavaScript libraries.
Conclusion
There are many possible use cases for v8js, like sandboxed scripting or the integration of some code which was written for the client side. That would not be the most clean solution, but it's a Turing complete approach, so why not?
After all, it's already possible to run PHP on Java or Python and Ruby on .NET. JavaScript is becoming ubiquitous, so why not providing support for it, even in a caged box?
Opinions expressed by DZone contributors are their own.
Comments