{"id":625,"date":"2012-08-19T19:47:44","date_gmt":"2012-08-19T17:47:44","guid":{"rendered":"https:\/\/d-mueller.de\/blog\/?p=625"},"modified":"2012-08-19T19:49:08","modified_gmt":"2012-08-19T17:49:08","slug":"javascript-in-php-mit-der-v8-engine","status":"publish","type":"post","link":"https:\/\/d-mueller.de\/blog\/javascript-in-php-mit-der-v8-engine\/","title":{"rendered":"Javascript in PHP mit der V8-Engine"},"content":{"rendered":"<p>Googles <a href=\"http:\/\/code.google.com\/p\/v8\/\">V8-Javascript-Engine<\/a> kommt u.a. in Chrome und NodeJS zum Einsatz und ist anerkannterma\u00dfen sauschnell. Umgesetzt wird der Sprachstandard ECMA-262 (5th edition), was aktuell &#8222;bleeding edge&#8220; ist. Nun gibt es die M\u00f6glichkeit, v8 zu experimentellen Zwecken auch mal aus PHP heraus zu bedienen. Dazu liegt <a href=\"http:\/\/pecl.php.net\/package\/v8js\">v8js als PECL-Paket<\/a> vor und kann recht schmerzfrei installiert werden. Aber step by step.<\/p>\n<h2>v8js installieren<\/h2>\n<p>Habe eben unter Ubuntu 12.04 erfolgreich folgendes Prozedere durchgef\u00fchrt:<\/p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">\r\nsudo apt-get install php5-dev php-pear libv8-dev build-essential\r\nsudo pecl install pecl install channel:\/\/pecl.php.net\/v8js-0.1.3\r\nsudo echo extension=v8js.so &gt;&gt;\/etc\/php5\/cli\/php.ini\r\n<\/pre>\n<p>Dabei ist 0.1.3 die derzeit aktuellste v8js-Version, also einfach nachschauen was gerade aktuell ist. Soll auch die Apache-PHP-Version v8 abbekommen:<\/p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">\r\nsudo echo extension=v8js.so &gt;&gt;\/etc\/php5\/apache2\/php.ini\r\n<\/pre>\n<p>Testen tun wir das ganze per <i>phpinfo()<\/i> bzw. mit<\/p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">\r\nphp -m | grep v8\r\n<\/pre>\n<p>Ich habe hier PHP 5.4.4.4 verwendet, sollte aber auch gut unter PHP 5.3 funktionieren.<\/p>\n<div id=\"attachment_626\" style=\"width: 310px\" class=\"wp-caption alignnone\"><a href=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2012\/08\/install.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-626\" src=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2012\/08\/install-300x194.png\" alt=\"V8-Installation\" title=\"V8-Installation\" width=\"300\" height=\"194\" class=\"size-medium wp-image-626\" srcset=\"https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2012\/08\/install-300x194.png 300w, https:\/\/d-mueller.de\/blog\/wp-content\/uploads\/2012\/08\/install.png 767w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-626\" class=\"wp-caption-text\">V8-Installation<\/p><\/div>\n<p>Die <a href=\"http:\/\/www.php.net\/manual\/de\/book.v8js.php\">PHP-Dokumentation von v8js<\/a> ist \u00fcbrigens noch sehr sp\u00e4rlich.<\/p>\n<h2>Code!<\/h2>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$a = new V8Js();\r\n$a-&gt;executeString(&#039;var x = function(a,b,c) { return a+b+c }; print(x(1,2,3));&#039;); \/\/6\r\n<\/pre>\n<p>Die Methode <a href=\"http:\/\/www.php.net\/manual\/de\/v8js.executestring.php\">executeString<\/a> ist die Hauptanlaufstelle f\u00fcr Arbeiten mit v8js, da kommt dann einfach der Javascript-Code rein. Hier wird die Zahl <i>6<\/i> einfach ausgegeben, alternativ k\u00f6nnen wir aber auch einen Wert aus <i>executeString<\/i> zur\u00fcckgeben, und zwar den letzt instanziierten. Beispiel hierzu:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$res = $a-&gt;executeString(&#039;var x = function(a,b,c) { return a+b+c }; var ret = x(1,2,3); ret;&#039;);\r\necho $res; \/\/6\r\n<\/pre>\n<p>Die Variable <i>ret<\/i>, die das Ergebnis der Addition beinhaltet, wird an PHP zur\u00fcckgegeben.<\/p>\n<h2>Interaktion von PHP und JS<\/h2>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$a = new V8Js();\r\n$a-&gt;func = function ($a) { echo &quot;Closure with param $a\\n&quot;; };\r\n$a-&gt;executeString(&quot;print(PHP.func);&quot;); \/\/[object Closure] \r\n$a-&gt;executeString(&quot;PHP.func(1);&quot;); \/\/Closure with param 1\r\n<\/pre>\n<p>Hier greift JS auf eine vorher definierte PHP-Closure zu. Smooth!<\/p>\n<h2>OOP?<\/h2>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\nclass Testing\r\n{\r\n    public $pub = &#039;pub&#039;;\r\n    private $priv = &#039;priv&#039;;\r\n    protected $prot = &#039;prot&#039;;\r\n\r\n    public function test($a, $b, $c)\r\n    {\r\n        var_dump($a, $b, $c);\r\n    }\r\n}\r\n\r\n$a = new V8Js();\r\n$a-&gt;testing = new Testing();\r\n$a-&gt;executeString(&quot;PHP.testing.test(PHP.testing.pub, PHP.testing.priv, PHP.testing.prot);&quot;);\r\n\r\n\/*\r\nstring(3) &quot;pub&quot;\r\nNULL\r\nNULL\r\n*\/\r\n<\/pre>\n<p>Man sieht: Kein Zugriff auf protected oder private-Variablen.<\/p>\n<h2>Arrays?<\/h2>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$a = new V8Js();\r\n$a-&gt;testing = function ($arr) { echo array_sum($arr); };\r\n$a-&gt;executeString(&quot;PHP.testing([1,2,3]);&quot;); \/\/6\r\n<\/pre>\n<p>Alles kein Problem, Arrays k\u00f6nnen schonungslos weiterverarbeitet werden.<\/p>\n<h2>Extensions<\/h2>\n<p>V8 bietet es an, mittels der Methode <a href=\"http:\/\/www.php.net\/manual\/de\/v8js.registerextension.php\">registerExtensions<\/a> eine Erweiterung zu registrieren, die dann sp\u00e4ter wieder aufgerufen werden kann. Dazu die Signatur der Methode vorab:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\npublic static bool V8Js::registerExtension ( string $extension_name , string $script [, array $dependencies = array() [, bool $auto_enable = FALSE ]] )\r\n<\/pre>\n<p>Ein Beispiel verdeutlicht das Vorgehen:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$extension_name = &quot;foo_extension&quot;;\r\n$extension_code = &#039;var foo=123;&#039;;\r\n$dependencies = array();\r\n$auto_enable = false;\r\n\r\nV8Js::registerExtension($extension_name, $extension_code, $dependencies, $auto_enable);\r\n\r\n$extension_name = &quot;bar_extension&quot;;\r\n$extension_code = &#039;var bar=foo+1;&#039;;\r\n$dependencies = array(&quot;foo_extension&quot;);\r\n$auto_enable = true;\r\n\r\nV8Js::registerExtension($extension_name, $extension_code, $dependencies, $auto_enable);\r\n<\/pre>\n<p>Wir registrieren also 2 Extensions, die erste der beiden wird nicht automatisch geladen. Allerdings baut die zweite Extension auf der ersten auf, sodass Extension 1 automatisch aktiviert wird.<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\nprint_r(V8JS::getExtensions());\r\n<\/pre>\n<p>verr\u00e4t uns: <i>foo_extension<\/i> und <i>bar_extension<\/i> sind registriert. Mit folgendem Testcode sehen wir den Effekt:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$v8 = new V8Js();\r\n$v8-&gt;executeString(&#039;if (bar == 124) print(&quot;works!&quot;);&#039;); \/\/works\r\n<\/pre>\n<p>Extensions sind also super f\u00fcr wiederkehrende Aufgaben, die immer initial definiert sein sollten. Vorstellbar sind hier bspw. Javascript-Libraries oder Erweiterungen der Sprache, bspw. <i>Array.prototype.contains<\/i>, was man ja sehr h\u00e4ufig vorfindet.<\/p>\n<h2>Benchmark!<\/h2>\n<p>Aber ist der Spa\u00df denn auch schnell? Das darf man von der gefeierten V8-Engine ja wohl erwarten. Hierzu habe ich ein nutzloses Beispielscript geschrieben, welches 10000000 mal den String <i>abcd<\/i> an ein Array anh\u00e4ngt, das Array dann zu einem String macht und abschlie\u00dfend iterativ die Anzahl der <i>a<\/i>&#8217;s ermittelt &#8211; keine gro\u00dfe Sache.<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\nfunction testing_php()\r\n{\r\n\t$start = microtime(true);\r\n\t$array = array();\r\n\t$count = 10000000;\r\n\r\n\twhile ($count--) \r\n\t    $array[] = &quot;abcd&quot;;\r\n\r\n\t$string = implode(&quot;&quot;, $array);\r\n\r\n\t$a_count = 0;\r\n\tfor ($i = 0; $i &lt; strlen($string); $i++)\r\n\t    if ($string[$i] == &quot;a&quot;) $a_count++;\r\n\r\n\techo &quot;counted $a_count a&#039;s in php\\n&quot;;\r\n\techo &quot;php took &quot; . (microtime(true) - $start) . &quot;s\\n&quot;;\r\n}\r\n\r\nfunction testing_v8()\r\n{\r\n\t$start = microtime(true);\r\n\t$v8 = new V8Js();\r\n\t\r\n\t$js_code = &#039;var array = [], count = 10000000;\r\n\t\t\t\twhile (count--) \r\n\t\t\t\t\tarray.push(&quot;abcd&quot;);\r\n\r\n\t\t\t\tvar string = array.join(&quot;&quot;);\r\n\r\n\t\t\t\tvar a_count = 0;\r\n\t\t\t\tfor (var i = 0; i &lt; string.length; i++)\r\n\t\t\t\t\tif (string[i] == &quot;a&quot;) a_count++;\r\n\r\n\t\t\t\tprint(&quot;counted &quot; + a_count + &quot; a\\&#039;s in js\\\\n&quot;);&#039;;\r\n\r\n\t$v8-&gt;executeString($js_code);\r\n\techo &quot;v8 took &quot;. (microtime(true) - $start) . &quot;s\\n&quot;;\r\n}\r\n\r\ntesting_php();\r\ntesting_v8();\r\n<\/pre>\n<p>Ergebnisse?<\/p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">\r\ncounted 10000000 a&#039;s in php\r\nphp took 31.238667011261s\r\ncounted 10000000 a&#039;s in js\r\nv8 took 2.8664078712463s\r\n<\/pre>\n<p>Ist sicher nicht der ultimative Beweis der Performanz-\u00dcberlegenheit von v8 gegen\u00fcber PHP, gibt aber trotzdem zu denken. Javascript in PHP ist schneller als PHP selbst &#8211; ok! Zusammenfassend l\u00e4sst sich wohl sagen, dass das ein Projekt mit einigem Potential ist. Sogar <a href=\"http:\/\/we-love-php.blogspot.de\/2012\/07\/using-v8-javascript-engine-as-php.html\">Datenbankzugriff<\/a> ist durch PHP aus V8 heraus m\u00f6glich.<\/p>\n<p>Abschlie\u00dfend sei noch auf einen <a href=\"https:\/\/github.com\/preillyme\/v8js\/\">v8 &#8211; Githubaccount<\/a> verwiesen, in dem sich nette Beispiele finden.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Googles V8-Javascript-Engine kommt u.a. in Chrome und NodeJS zum Einsatz und ist anerkannterma\u00dfen sauschnell. Umgesetzt wird der Sprachstandard ECMA-262 (5th edition), was aktuell &#8222;bleeding edge&#8220; ist. Nun gibt es die M\u00f6glichkeit, v8 zu experimentellen Zwecken auch mal aus PHP heraus &hellip; <a href=\"https:\/\/d-mueller.de\/blog\/javascript-in-php-mit-der-v8-engine\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,12,3],"tags":[],"class_list":["post-625","post","type-post","status-publish","format-standard","hentry","category-php","category-javascript","category-webdev"],"_links":{"self":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/comments?post=625"}],"version-history":[{"count":0,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions"}],"wp:attachment":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/media?parent=625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/categories?post=625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/tags?post=625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}