V8Js ist eine PHP-Erweiterung für die V8-Javascript-Engine von Google.
Mit der Erweiterung können Sie Javascript-Code in einer sicheren Sandbox von PHP aus ausführen. Der ausgeführte Code kann durch ein Zeitlimit und/oder ein Speicherlimit eingeschränkt werden. Dies bietet die Möglichkeit, nicht vertrauenswürdigen Code sicher auszuführen.
V8 Javascript Engine-Bibliothek (libv8) Master https://github.com/v8/v8-git-mirror (trunk)
V8 ist Googles Open-Source-Javascript-Engine. V8 ist in C++ geschrieben und wird in Google Chrome, dem Open-Source-Browser von Google, verwendet. V8 implementiert ECMAScript gemäß ECMA-262, 5. Ausgabe.
Diese Erweiterung erfordert V8 9.0 oder höher.
V8-Releases werden ziemlich schnell veröffentlicht und das V8-Team bietet in der Regel Sicherheitsunterstützung für die Versionslinie, die mit dem Chrome-Browser (stabiler Kanal) und neueren Versionen (nur) ausgeliefert wird. Eine Versionsübersicht finden Sie unter https://chromiumdash.appspot.com/branches.
PHP 8.0.0+
Diese eingebettete Implementierung der V8-Engine verwendet Thread-Sperre, sodass sie mit aktiviertem ZTS funktioniert.
Windows wird derzeit nicht offiziell unterstützt. Vor allem, weil ich nicht die Zeit habe, selbst den Support dafür aufrechtzuerhalten, und nicht wirklich über Windows-Rechner verfüge, mit denen ich Dinge ausprobieren kann. Es wäre großartig, wenn jemand eingreifen und Probleme unter Windows beheben, vorgefertigte V8-Binärdateien usw. bereitstellen könnte.
Es gibt einen Zweig namens php7 , der auf PHP 7.0.0+ abzielt
Für einige allererste Schritte sollten Sie statt des manuellen Kompilierens vielleicht das V8Js-Docker-Image ausprobieren. Es sind v8, v8js und php-cli vorinstalliert, sodass Sie es mit PHP im „interaktiven Modus“ ausprobieren können. Es läuft jedoch kein Apache usw.
Das Erstellen unter Microsoft Windows ist etwas komplizierter. Einen kurzen Überblick finden Sie in der Datei README.Win32.md. Der Aufbau auf GNU/Linux und MacOS
<?php
class V8Js
{
/* Constants */
const V8_VERSION = '' ;
const FLAG_NONE = 1 ;
const FLAG_FORCE_ARRAY = 2 ;
const FLAG_PROPAGATE_PHP_EXCEPTIONS = 4 ;
/* Methods */
/**
* Initializes and starts V8 engine and returns new V8Js object with it's own V8 context.
* @param string $object_name
* @param array $variables
* @param string $snapshot_blob
*/
public function __construct ( $ object_name = " PHP " , array $ variables = [], $ snapshot_blob = NULL )
{}
/**
* Provide a function or method to be used to load required modules. This can be any valid PHP callable.
* The loader function will receive the normalised module path and should return Javascript code to be executed.
* @param callable $loader
*/
public function setModuleLoader ( callable $ loader )
{}
/**
* Provide a function or method to be used to normalise module paths. This can be any valid PHP callable.
* This can be used in combination with setModuleLoader to influence normalisation of the module path (which
* is normally done by V8Js itself but can be overriden this way).
* The normaliser function will receive the base path of the current module (if any; otherwise an empty string)
* and the literate string provided to the require method and should return an array of two strings (the new
* module base path as well as the normalised name). Both are joined by a '/' and then passed on to the
* module loader (unless the module was cached before).
* @param callable $normaliser
*/
public function setModuleNormaliser ( callable $ normaliser )
{}
/**
* Provate a function or method to be used to convert/proxy PHP exceptions to JS.
* This can be any valid PHP callable.
* The converter function will receive the PHP Exception instance that has not been caught and
* is due to be forwarded to JS. Pass NULL as $filter to uninstall an existing filter.
*/
public function setExceptionFilter ( callable $ filter )
{}
/**
* Compiles and executes script in object's context with optional identifier string.
* A time limit (milliseconds) and/or memory limit (bytes) can be provided to restrict execution. These options will throw a V8JsTimeLimitException or V8JsMemoryLimitException.
* @param string $script
* @param string $identifier
* @param int $flags
* @param int $time_limit in milliseconds
* @param int $memory_limit in bytes
* @return mixed
*/
public function executeString ( $ script , $ identifier = '' , $ flags = V8Js:: FLAG_NONE , $ time_limit = 0 , $ memory_limit = 0 )
{}
/**
* Compiles a script in object's context with optional identifier string.
* @param $script
* @param string $identifier
* @return resource
*/
public function compileString ( $ script , $ identifier = '' )
{}
/**
* Executes a precompiled script in object's context.
* A time limit (milliseconds) and/or memory limit (bytes) can be provided to restrict execution. These options will throw a V8JsTimeLimitException or V8JsMemoryLimitException.
* @param resource $script
* @param int $flags
* @param int $time_limit
* @param int $memory_limit
*/
public function executeScript ( $ script , $ flags = V8Js:: FLAG_NONE , $ time_limit = 0 , $ memory_limit = 0 )
{}
/**
* Set the time limit (in milliseconds) for this V8Js object
* works similar to the set_time_limit php
* @param int $limit
*/
public function setTimeLimit ( $ limit )
{}
/**
* Set the memory limit (in bytes) for this V8Js object
* @param int $limit
*/
public function setMemoryLimit ( $ limit )
{}
/**
* Set the average object size (in bytes) for this V8Js object.
* V8's "amount of external memory" is adjusted by this value for every exported object. V8 triggers a garbage collection once this totals to 192 MB.
* @param int $average_object_size
*/
public function setAverageObjectSize ( $ average_object_size )
{}
/**
* Returns uncaught pending exception or null if there is no pending exception.
* @return V8JsScriptException|null
*/
public function getPendingException ()
{}
/**
* Clears the uncaught pending exception
*/
public function clearPendingException ()
{}
/** Static methods **/
/**
* Creates a custom V8 heap snapshot with the provided JavaScript source embedded.
* @param string $embed_source
* @return string|false
*/
public static function createSnapshot ( $ embed_source )
{}
}
final class V8JsScriptException extends Exception
{
/**
* @return string
*/
final public function getJsFileName ( ) {}
/**
* @return int
*/
final public function getJsLineNumber ( ) {}
/**
* @return int
*/
final public function getJsStartColumn ( ) {}
/**
* @return int
*/
final public function getJsEndColumn ( ) {}
/**
* @return string
*/
final public function getJsSourceLine ( ) {}
/**
* @return string
*/
final public function getJsTrace ( ) {}
}
final class V8JsTimeLimitException extends Exception
{
}
final class V8JsMemoryLimitException extends Exception
{
} // Print a string.
print ( string ) ;
// Dump the contents of a variable.
var_dump ( value ) ;
// Terminate Javascript execution immediately.
exit ( ) ;
// CommonJS Module support to require external code.
// This makes use of the PHP module loader provided via V8Js::setModuleLoader (see PHP API above).
require ( "path/to/module" ) ; Wenn der JavaScript- in -Operator auf ein umschlossenes PHP-Objekt angewendet wird, funktioniert er genauso wie die PHP-Funktion isset() . In ähnlicher Weise funktioniert JavaScript delete , wenn es auf ein umschlossenes PHP-Objekt angewendet wird, wie PHP unset .
<?php
class Foo {
var $ bar = null ;
}
$ v8 = new V8Js ();
$ v8 -> foo = new Foo ;
// This prints "no"
$ v8 -> executeString ( ' print( "bar" in PHP.foo ? "yes" : "no" ); ' );
?> PHP verfügt über separate Namensräume für Eigenschaften und Methoden, während JavaScript nur einen hat. Normalerweise stellt dies kein Problem dar, aber bei Bedarf können Sie ein führendes $ verwenden, um eine Eigenschaft anzugeben, oder __call um eine Methode gezielt aufzurufen.
<?php
class Foo {
var $ bar = " bar " ;
function bar ( $ what ) { echo " I'm a " , $ what , " ! n" ; }
}
$ foo = new Foo ;
// This prints 'bar'
echo $ foo -> bar , "n" ;
// This prints "I'm a function!"
$ foo -> bar ( " function " );
$ v8 = new V8Js ();
$ v8 -> foo = new Foo ;
// This prints 'bar'
$ v8 -> executeString ( ' print(PHP.foo.$bar, "n"); ' );
// This prints "I'm a function!"
$ v8 -> executeString ( ' PHP.foo.__call("bar", ["function"]); ' );
?> PHP- und JavaScript-Datentypen stimmen nicht genau überein. Dies bedeutet natürlich, dass beide Sprachen über Datentypen zur Verarbeitung von Zahlen verfügen. Allerdings unterscheidet PHP zwischen Ganzzahlen und Gleitkommazahlen, während JavaScript nur einen Typ Number hat, der eine IEEE 754-Gleitkommazahl ist. In vielen Fällen spielt das überhaupt keine Rolle, wenn beide Sprachen die gleiche Zahl gut darstellen können. Es gibt jedoch Randfälle.
Auf 64-Bit-Systemen erlaubt PHP Ganzzahlen mit 64 signifikanten Bits, der Zahlentyp von JavaScript (z. B. IEEE 754) hat jedoch nur 52-Bit-Mantissen. Dadurch geht etwas Präzision verloren. Dies spielt eine Rolle, wenn Sie ganzzahlige Werte mit mehr als 15 genauen Dezimalstellen weitergeben.
Trotz des gebräuchlichen Namens unterscheidet sich das Array-Konzept zwischen PHP und JavaScript stark. In JavaScript ist ein Array eine zusammenhängende Sammlung von Elementen, die durch ganze Zahlen von Null aufwärts indiziert sind. In PHP können Arrays spärlich besetzt sein, dh ganzzahlige Schlüssel müssen nicht zusammenhängend sein und können sogar negativ sein. Darüber hinaus können PHP-Arrays nicht nur ganze Zahlen als Schlüssel verwenden, sondern auch Zeichenfolgen (sog. assoziative Arrays). Im Gegensatz zu JavaScript-Arrays können Eigenschaften an Arrays angehängt werden, was von PHP nicht unterstützt wird. Diese Eigenschaften sind nicht Teil der Arrays-Sammlung, beispielsweise „sieht“ die Methode Array.prototype.forEach “ diese nicht.
Im Allgemeinen werden PHP-Arrays auf „native“ JavaScript-Arrays abgebildet, wenn dies möglich ist, dh das PHP-Array verwendet zusammenhängende numerische Schlüssel von Null aufwärts. Sowohl assoziative als auch spärliche Arrays werden JavaScript-Objekten zugeordnet. Diese Objekte haben einen Konstruktor, der auch „Array“ genannt wird, aber sie sind keine nativen Arrays und teilen den Array.prototype nicht, daher unterstützen sie nicht (direkt) die typischen Array-Funktionen wie join , forEach usw. PHP-Arrays sind es Sofort Wert für Wert exportiert, ohne Live-Bindung. Das heißt, wenn Sie einen Wert auf der JavaScript-Seite ändern oder weitere Werte auf das Array übertragen, wird diese Änderung nicht auf der PHP-Seite widergespiegelt.
Wenn JavaScript-Arrays an PHP zurückgegeben werden, wird das JavaScript-Array immer in ein PHP-Array konvertiert. Wenn an das JavaScript-Array (eigene) Eigenschaften angehängt sind, werden diese auch in Schlüssel des PHP-Arrays umgewandelt.
An JavaScript übergebene PHP-Objekte werden nativen JavaScript-Objekten zugeordnet, die über eine „virtuelle“ Konstruktorfunktion mit dem Namen der Klasse des PHP-Objekts verfügen. Mit dieser Konstruktorfunktion können neue Instanzen der PHP-Klasse erstellt werden, sofern die PHP-Klasse nicht über eine nicht öffentliche __construct Methode verfügt. Alle öffentlichen Methoden und Eigenschaften sind für JavaScript-Code sichtbar und die Eigenschaften sind live gebunden, d. h. wenn der Wert einer Eigenschaft durch JavaScript-Code geändert wird, ist auch das PHP-Objekt betroffen.
Wenn ein natives JavaScript-Objekt an PHP übergeben wird, wird das JavaScript-Objekt einem PHP-Objekt der V8Object -Klasse zugeordnet. Dieses Objekt verfügt über alle Eigenschaften des JavaScript-Objekts und ist vollständig veränderbar. Wenn einer dieser Eigenschaften eine Funktion zugewiesen ist, kann sie auch durch PHP-Code aufgerufen werden. Die Funktion executeString kann so konfiguriert werden, dass JavaScript-Objekte immer PHP-Arrays zugeordnet werden, indem das Flag V8Js::FLAG_FORCE_ARRAY gesetzt wird. Dann gilt das Standard-Array-Verhalten, dass Werte nicht live gebunden sind, d. h. wenn Sie Werte des resultierenden PHP-Arrays ändern, hat dies keine Auswirkungen auf das JavaScript-Objekt.
Die obige Regel, dass PHP-Objekte im Allgemeinen in JavaScript-Objekte konvertiert werden, gilt auch für PHP-Objekte vom Typ ArrayObject oder andere Klassen, die sowohl die ArrayAccess als auch die Countable -Schnittstelle implementieren – auch wenn sie sich wie PHP-Arrays verhalten.
Dieses Verhalten kann durch Aktivieren des php.ini-Flags v8js.use_array_access geändert werden. Wenn festgelegt, werden Objekte von PHP-Klassen, die die oben genannten Schnittstellen implementieren, in JavaScript-Array-ähnliche Objekte konvertiert. Dies ist ein Indexzugriff auf dieses Objekt, der zu sofortigen Aufrufen der PHP-Methoden offsetGet oder offsetSet führt (effektiv handelt es sich dabei um eine Live-Bindung von JavaScript an das PHP-Objekt). Ein solches Array-ähnliches Objekt unterstützt auch den Aufruf aller angehängten öffentlichen Methoden des PHP-Objekts + Methoden der nativen Array.prototype-Methoden von JavaScript (sofern sie nicht durch PHP-Methoden überlastet werden).
Zunächst einmal handelt es sich bei benutzerdefinierten Startup-Snapshots um eine von V8 selbst bereitgestellte Funktion, die auf der allgemeinen Heap-Snapshots-Funktion aufbaut. Da es durchaus üblich ist, vor der eigentlichen Arbeit eine JavaScript-Bibliothek zu laden, besteht die Idee darin, dass dieser Bibliothekscode auch in den Heap-Snapshot integriert wird.
Diese Erweiterung bietet eine einfache Möglichkeit, diese benutzerdefinierten Snapshots zu erstellen. Um einen solchen Snapshot mit einer integrierten fibonacci -Funktion zu erstellen, rufen Sie V8Js::createSnapshot einfach statisch wie folgt auf:
$ snapshot = V8Js:: createSnapshot ( ' var fibonacci = n => n < 3 ? 1 : fibonacci(n - 1) + fibonacci(n - 2) ' ); Behalten Sie dann den Inhalt von $snapshot an einem beliebigen Ort bei, z. B. im lokalen Dateisystem oder möglicherweise in Redis.
Wenn Sie eine neue V8Js-Instanz erstellen müssen, übergeben Sie einfach den Snapshot als fünftes Argument an den V8Js-Konstruktor:
$ jscript = new V8Js ( ' php ' , array (), array (), true , $ snapshot );
echo $ jscript -> executeString ( ' fibonacci(43) ' ) . "n" ;Beachten Sie, dass der Code, der in den Snapshot aufgenommen werden soll, möglicherweise keine der aus PHP exportierten Funktionen direkt aufruft, da diese direkt nach der Ausführung des Snapshot-Codes hinzugefügt werden.
Wenn der JavaScript-Code (ohne Abfangen) auslöst, Fehler verursacht oder nicht kompiliert werden kann, werden V8JsScriptException -Ausnahmen ausgelöst.
PHP-Ausnahmen, die aufgrund von Aufrufen von JavaScript-Code auftreten, werden standardmäßig nicht erneut in den JavaScript-Kontext geworfen, sondern bewirken, dass die JavaScript-Ausführung sofort gestoppt wird, und werden dann an der Stelle gemeldet, an der der JS-Code aufgerufen wird.
Dieses Verhalten kann durch Setzen des Flags FLAG_PROPAGATE_PHP_EXCEPTIONS geändert werden. Wenn es gesetzt ist, werden PHP-Ausnahmeobjekte (Objekte) in JavaScript-Objekte konvertiert, die den oben genannten Regeln entsprechen, und im JavaScript-Kontext erneut ausgelöst. Wenn sie nicht vom JavaScript-Code abgefangen werden, stoppt die Ausführung und es wird eine V8JsScriptException ausgelöst, die über die getPrevious -Methode auf die ursprüngliche PHP-Ausnahme zugreift.
Bedenken Sie, dass der JS-Code Zugriff auf Methoden wie getTrace für das Ausnahmeobjekt hat. Dies kann unerwünschtes Verhalten sein, wenn Sie nicht vertrauenswürdigen Code ausführen. Mithilfe setExceptionFilter -Methode kann eine aufrufbare Funktion bereitgestellt werden, die die PHP-Ausnahme in einen anderen Wert umwandeln kann, der sicher verfügbar gemacht werden kann. Der Filter kann auch entscheiden, die Ausnahme überhaupt nicht an JS weiterzugeben, indem er entweder die übergebene Ausnahme erneut auslöst oder eine andere Ausnahme auslöst.