Skip to content

Commit

Permalink
Merge branch 'php7' into php8
Browse files Browse the repository at this point in the history
  • Loading branch information
stesie committed Feb 3, 2023
2 parents 461230b + fa264c9 commit 7887ec6
Show file tree
Hide file tree
Showing 18 changed files with 401 additions and 29 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ class V8Js
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.
Expand Down Expand Up @@ -369,3 +378,10 @@ objects obeying the above rules and re-thrown in JavaScript context. If they
are not caught by JavaScript code the execution stops and a
`V8JsScriptException` is thrown, which has the original PHP exception accessible
via `getPrevious` method.

Consider that the JS code has access to methods like `getTrace` on the exception
object. This might be unwanted behaviour, if you execute untrusted code.
Using `setExceptionFilter` method a callable can be provided, that may convert
the PHP exception to some other value that is safe to expose. The filter may
also decide not to propagate the exception to JS at all by either re-throwing
the passed exception or throwing another exception.
26 changes: 24 additions & 2 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ if test "$PHP_V8JS" != "no"; then


AC_CACHE_CHECK(for C standard version, ac_cv_v8_cstd, [
ac_cv_v8_cstd="c++14"
ac_cv_v8_cstd="c++17"
old_CPPFLAGS=$CPPFLAGS
AC_LANG_PUSH([C++])
CPPFLAGS="-std="$ac_cv_v8_cstd
AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])],[],[ac_cv_v8_cstd="c++1y"],[])
AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])],[],[
ac_cv_v8_cstd="c++14"
CPPFLAGS="-std="$ac_cv_v8_cstd
AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])],[],[ ac_cv_v8_cstd="c++1y" ],[])
],[])
AC_LANG_POP([C++])
CPPFLAGS=$old_CPPFLAGS
]);
Expand Down Expand Up @@ -173,6 +177,24 @@ int main ()
V8_SEARCH_BLOB([snapshot_blob.bin], [PHP_V8_SNAPSHOT_BLOB_PATH])


dnl
dnl Check for v8::V8::InitializeSandbox
dnl
AC_CACHE_CHECK([for v8::V8::InitializeSandbox], ac_cv_has_initialize_sandbox, [
AC_LINK_IFELSE([AC_LANG_PROGRAM([
#define V8_ENABLE_SANDBOX 1
#include <v8.h>
], [ v8::V8::InitializeSandbox(); ])], [
ac_cv_has_initialize_sandbox=yes
], [
ac_cv_has_initialize_sandbox=no
])
])
if test "x$ac_cv_has_initialize_sandbox" = "xyes"; then
AC_DEFINE([V8_HAS_INITIALIZE_SANDBOX], [1],
[Define if V8::InitializeSandbox must be called.])
fi

dnl
dnl Check for v8::ArrayBuffer::Allocator::NewDefaultAllocator
dnl
Expand Down
4 changes: 4 additions & 0 deletions php_v8js_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ extern "C" {
#undef COMPILER
#endif

#ifdef V8_HAS_INITIALIZE_SANDBOX
#define V8_ENABLE_SANDBOX 1
#endif

#include <v8.h>
#include <v8-platform.h>

Expand Down
35 changes: 35 additions & 0 deletions tests/exception_filter_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
Test V8::setExceptionFilter() : String conversion
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class myv8 extends V8Js
{
public function throwException(string $message) {
throw new Exception($message);
}
}

$v8 = new myv8();
$v8->setExceptionFilter(function (Throwable $ex) {
echo "exception filter called.\n";
return $ex->getMessage();
});

$v8->executeString('
try {
PHP.throwException("Oops");
}
catch (e) {
var_dump(typeof e); // string
var_dump(e);
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
?>
===EOF===
--EXPECT--
exception filter called.
string(6) "string"
string(4) "Oops"
===EOF===
32 changes: 32 additions & 0 deletions tests/exception_filter_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
Test V8::setExceptionFilter() : Filter handling on exception in setModuleLoader
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$v8 = new V8Js();
$v8->setModuleLoader(function ($path) {
throw new Error('moep');
});

$v8->setExceptionFilter(function (Throwable $ex) {
echo "exception filter called.\n";
return $ex->getMessage();
});

$v8->executeString('
try {
require("file");
} catch(e) {
var_dump(e);
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);

?>
===EOF===
--EXPECT--
exception filter called.
string(4) "moep"
===EOF===

34 changes: 34 additions & 0 deletions tests/exception_filter_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
Test V8::setExceptionFilter() : Filter handling on exception in setModuleNormaliser
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$v8 = new V8Js();
$v8->setModuleNormaliser(function ($path) {
throw new Error('blarg');
});
$v8->setModuleLoader(function ($path) {
throw new Error('moep');
});

$v8->setExceptionFilter(function (Throwable $ex) {
echo "exception filter called.\n";
return $ex->getMessage();
});

$v8->executeString('
try {
require("file");
} catch(e) {
var_dump(e);
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);

?>
===EOF===
--EXPECT--
exception filter called.
string(5) "blarg"
===EOF===
37 changes: 37 additions & 0 deletions tests/exception_filter_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Test V8::setExceptionFilter() : Filter handling on exception in converter
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class myv8 extends V8Js
{
public function throwException(string $message) {
throw new Exception($message);
}
}

$v8 = new myv8();
$v8->setExceptionFilter(function (Throwable $ex) {
throw new Exception('moep');
});

try {
$v8->executeString('
try {
PHP.throwException("Oops");
print("done\\n");
}
catch (e) {
print("caught\\n");
var_dump(e);
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
} catch (Exception $ex) {
echo "caught in php: " . $ex->getMessage() . PHP_EOL;
}
?>
===EOF===
--EXPECT--
caught in php: moep
===EOF===
53 changes: 53 additions & 0 deletions tests/exception_filter_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
Test V8::setExceptionFilter() : Uninstall filter on NULL
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class myv8 extends V8Js
{
public function throwException(string $message) {
throw new Exception($message);
}
}

$v8 = new myv8();
$v8->setExceptionFilter(function (Throwable $ex) {
echo "exception filter called.\n";
return "moep";
});

$v8->executeString('
try {
PHP.throwException("Oops");
}
catch (e) {
var_dump(e);
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);

$v8->setExceptionFilter(null);

try {
$v8->executeString('
try {
PHP.throwException("Oops");
print("done\\n");
}
catch (e) {
print("caught\\n");
var_dump(e.getMessage());
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
} catch (Exception $ex) {
echo "caught in php: " . $ex->getMessage() . PHP_EOL;
}

?>
===EOF===
--EXPECT--
exception filter called.
string(4) "moep"
caught
string(4) "Oops"
===EOF===
38 changes: 38 additions & 0 deletions tests/exception_filter_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
Test V8::setExceptionFilter() : re-throw exception in exception filter
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class myv8 extends V8Js
{
public function throwException(string $message) {
throw new Exception($message);
}
}

$v8 = new myv8();
$v8->setExceptionFilter(function (Throwable $ex) {
// re-throw exception so it is not forwarded
throw $ex;
});

try {
$v8->executeString('
try {
PHP.throwException("Oops");
print("done\\n");
}
catch (e) {
print("caught\\n");
var_dump(e);
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
} catch (Exception $ex) {
echo "caught in php: " . $ex->getMessage() . PHP_EOL;
}
?>
===EOF===
--EXPECT--
caught in php: Oops
===EOF===
55 changes: 55 additions & 0 deletions tests/exception_filter_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
--TEST--
Test V8::setExceptionFilter() : Simple test
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
class myv8 extends V8Js
{
public function throwException(string $message) {
throw new Exception($message);
}
}

class ExceptionFilter {
private $ex;

public function __construct(Throwable $ex) {
echo "ExceptionFilter::__construct called!\n";
var_dump($ex->getMessage());

$this->ex = $ex;
}

public function getMessage() {
echo "getMessage called\n";
return $this->ex->getMessage();
}
}

$v8 = new myv8();
$v8->setExceptionFilter(function (Throwable $ex) {
echo "exception filter called.\n";
return new ExceptionFilter($ex);
});

$v8->executeString('
try {
PHP.throwException("Oops");
}
catch (e) {
var_dump(e.getMessage()); // calls ExceptionFilter::getMessage
var_dump(typeof e.getTrace);
}
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
?>
===EOF===
--EXPECT--
exception filter called.
ExceptionFilter::__construct called!
string(4) "Oops"
getMessage called
string(4) "Oops"
string(9) "undefined"
===EOF===

Loading

0 comments on commit 7887ec6

Please sign in to comment.