Skip to content

Commit 7887ec6

Browse files
committed
Merge branch 'php7' into php8
2 parents 461230b + fa264c9 commit 7887ec6

18 files changed

+401
-29
lines changed

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ class V8Js
104104
public function setModuleNormaliser(callable $normaliser)
105105
{}
106106

107+
/**
108+
* Provate a function or method to be used to convert/proxy PHP exceptions to JS.
109+
* This can be any valid PHP callable.
110+
* The converter function will receive the PHP Exception instance that has not been caught and
111+
* is due to be forwarded to JS. Pass NULL as $filter to uninstall an existing filter.
112+
*/
113+
public function setExceptionFilter(callable $filter)
114+
{}
115+
107116
/**
108117
* Compiles and executes script in object's context with optional identifier string.
109118
* A time limit (milliseconds) and/or memory limit (bytes) can be provided to restrict execution. These options will throw a V8JsTimeLimitException or V8JsMemoryLimitException.
@@ -369,3 +378,10 @@ objects obeying the above rules and re-thrown in JavaScript context. If they
369378
are not caught by JavaScript code the execution stops and a
370379
`V8JsScriptException` is thrown, which has the original PHP exception accessible
371380
via `getPrevious` method.
381+
382+
Consider that the JS code has access to methods like `getTrace` on the exception
383+
object. This might be unwanted behaviour, if you execute untrusted code.
384+
Using `setExceptionFilter` method a callable can be provided, that may convert
385+
the PHP exception to some other value that is safe to expose. The filter may
386+
also decide not to propagate the exception to JS at all by either re-throwing
387+
the passed exception or throwing another exception.

config.m4

+24-2
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ if test "$PHP_V8JS" != "no"; then
3939

4040

4141
AC_CACHE_CHECK(for C standard version, ac_cv_v8_cstd, [
42-
ac_cv_v8_cstd="c++14"
42+
ac_cv_v8_cstd="c++17"
4343
old_CPPFLAGS=$CPPFLAGS
4444
AC_LANG_PUSH([C++])
4545
CPPFLAGS="-std="$ac_cv_v8_cstd
46-
AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])],[],[ac_cv_v8_cstd="c++1y"],[])
46+
AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])],[],[
47+
ac_cv_v8_cstd="c++14"
48+
CPPFLAGS="-std="$ac_cv_v8_cstd
49+
AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])],[],[ ac_cv_v8_cstd="c++1y" ],[])
50+
],[])
4751
AC_LANG_POP([C++])
4852
CPPFLAGS=$old_CPPFLAGS
4953
]);
@@ -173,6 +177,24 @@ int main ()
173177
V8_SEARCH_BLOB([snapshot_blob.bin], [PHP_V8_SNAPSHOT_BLOB_PATH])
174178

175179

180+
dnl
181+
dnl Check for v8::V8::InitializeSandbox
182+
dnl
183+
AC_CACHE_CHECK([for v8::V8::InitializeSandbox], ac_cv_has_initialize_sandbox, [
184+
AC_LINK_IFELSE([AC_LANG_PROGRAM([
185+
#define V8_ENABLE_SANDBOX 1
186+
#include <v8.h>
187+
], [ v8::V8::InitializeSandbox(); ])], [
188+
ac_cv_has_initialize_sandbox=yes
189+
], [
190+
ac_cv_has_initialize_sandbox=no
191+
])
192+
])
193+
if test "x$ac_cv_has_initialize_sandbox" = "xyes"; then
194+
AC_DEFINE([V8_HAS_INITIALIZE_SANDBOX], [1],
195+
[Define if V8::InitializeSandbox must be called.])
196+
fi
197+
176198
dnl
177199
dnl Check for v8::ArrayBuffer::Allocator::NewDefaultAllocator
178200
dnl

php_v8js_macros.h

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ extern "C" {
5252
#undef COMPILER
5353
#endif
5454

55+
#ifdef V8_HAS_INITIALIZE_SANDBOX
56+
#define V8_ENABLE_SANDBOX 1
57+
#endif
58+
5559
#include <v8.h>
5660
#include <v8-platform.h>
5761

tests/exception_filter_001.phpt

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Test V8::setExceptionFilter() : String conversion
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
class myv8 extends V8Js
8+
{
9+
public function throwException(string $message) {
10+
throw new Exception($message);
11+
}
12+
}
13+
14+
$v8 = new myv8();
15+
$v8->setExceptionFilter(function (Throwable $ex) {
16+
echo "exception filter called.\n";
17+
return $ex->getMessage();
18+
});
19+
20+
$v8->executeString('
21+
try {
22+
PHP.throwException("Oops");
23+
}
24+
catch (e) {
25+
var_dump(typeof e); // string
26+
var_dump(e);
27+
}
28+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
29+
?>
30+
===EOF===
31+
--EXPECT--
32+
exception filter called.
33+
string(6) "string"
34+
string(4) "Oops"
35+
===EOF===

tests/exception_filter_002.phpt

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
Test V8::setExceptionFilter() : Filter handling on exception in setModuleLoader
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$v8 = new V8Js();
9+
$v8->setModuleLoader(function ($path) {
10+
throw new Error('moep');
11+
});
12+
13+
$v8->setExceptionFilter(function (Throwable $ex) {
14+
echo "exception filter called.\n";
15+
return $ex->getMessage();
16+
});
17+
18+
$v8->executeString('
19+
try {
20+
require("file");
21+
} catch(e) {
22+
var_dump(e);
23+
}
24+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
25+
26+
?>
27+
===EOF===
28+
--EXPECT--
29+
exception filter called.
30+
string(4) "moep"
31+
===EOF===
32+

tests/exception_filter_003.phpt

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
Test V8::setExceptionFilter() : Filter handling on exception in setModuleNormaliser
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$v8 = new V8Js();
9+
$v8->setModuleNormaliser(function ($path) {
10+
throw new Error('blarg');
11+
});
12+
$v8->setModuleLoader(function ($path) {
13+
throw new Error('moep');
14+
});
15+
16+
$v8->setExceptionFilter(function (Throwable $ex) {
17+
echo "exception filter called.\n";
18+
return $ex->getMessage();
19+
});
20+
21+
$v8->executeString('
22+
try {
23+
require("file");
24+
} catch(e) {
25+
var_dump(e);
26+
}
27+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
28+
29+
?>
30+
===EOF===
31+
--EXPECT--
32+
exception filter called.
33+
string(5) "blarg"
34+
===EOF===

tests/exception_filter_004.phpt

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Test V8::setExceptionFilter() : Filter handling on exception in converter
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
class myv8 extends V8Js
8+
{
9+
public function throwException(string $message) {
10+
throw new Exception($message);
11+
}
12+
}
13+
14+
$v8 = new myv8();
15+
$v8->setExceptionFilter(function (Throwable $ex) {
16+
throw new Exception('moep');
17+
});
18+
19+
try {
20+
$v8->executeString('
21+
try {
22+
PHP.throwException("Oops");
23+
print("done\\n");
24+
}
25+
catch (e) {
26+
print("caught\\n");
27+
var_dump(e);
28+
}
29+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
30+
} catch (Exception $ex) {
31+
echo "caught in php: " . $ex->getMessage() . PHP_EOL;
32+
}
33+
?>
34+
===EOF===
35+
--EXPECT--
36+
caught in php: moep
37+
===EOF===

tests/exception_filter_005.phpt

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
--TEST--
2+
Test V8::setExceptionFilter() : Uninstall filter on NULL
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
class myv8 extends V8Js
8+
{
9+
public function throwException(string $message) {
10+
throw new Exception($message);
11+
}
12+
}
13+
14+
$v8 = new myv8();
15+
$v8->setExceptionFilter(function (Throwable $ex) {
16+
echo "exception filter called.\n";
17+
return "moep";
18+
});
19+
20+
$v8->executeString('
21+
try {
22+
PHP.throwException("Oops");
23+
}
24+
catch (e) {
25+
var_dump(e);
26+
}
27+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
28+
29+
$v8->setExceptionFilter(null);
30+
31+
try {
32+
$v8->executeString('
33+
try {
34+
PHP.throwException("Oops");
35+
print("done\\n");
36+
}
37+
catch (e) {
38+
print("caught\\n");
39+
var_dump(e.getMessage());
40+
}
41+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
42+
} catch (Exception $ex) {
43+
echo "caught in php: " . $ex->getMessage() . PHP_EOL;
44+
}
45+
46+
?>
47+
===EOF===
48+
--EXPECT--
49+
exception filter called.
50+
string(4) "moep"
51+
caught
52+
string(4) "Oops"
53+
===EOF===

tests/exception_filter_006.phpt

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Test V8::setExceptionFilter() : re-throw exception in exception filter
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
class myv8 extends V8Js
8+
{
9+
public function throwException(string $message) {
10+
throw new Exception($message);
11+
}
12+
}
13+
14+
$v8 = new myv8();
15+
$v8->setExceptionFilter(function (Throwable $ex) {
16+
// re-throw exception so it is not forwarded
17+
throw $ex;
18+
});
19+
20+
try {
21+
$v8->executeString('
22+
try {
23+
PHP.throwException("Oops");
24+
print("done\\n");
25+
}
26+
catch (e) {
27+
print("caught\\n");
28+
var_dump(e);
29+
}
30+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
31+
} catch (Exception $ex) {
32+
echo "caught in php: " . $ex->getMessage() . PHP_EOL;
33+
}
34+
?>
35+
===EOF===
36+
--EXPECT--
37+
caught in php: Oops
38+
===EOF===

tests/exception_filter_basic.phpt

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
--TEST--
2+
Test V8::setExceptionFilter() : Simple test
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
class myv8 extends V8Js
8+
{
9+
public function throwException(string $message) {
10+
throw new Exception($message);
11+
}
12+
}
13+
14+
class ExceptionFilter {
15+
private $ex;
16+
17+
public function __construct(Throwable $ex) {
18+
echo "ExceptionFilter::__construct called!\n";
19+
var_dump($ex->getMessage());
20+
21+
$this->ex = $ex;
22+
}
23+
24+
public function getMessage() {
25+
echo "getMessage called\n";
26+
return $this->ex->getMessage();
27+
}
28+
}
29+
30+
$v8 = new myv8();
31+
$v8->setExceptionFilter(function (Throwable $ex) {
32+
echo "exception filter called.\n";
33+
return new ExceptionFilter($ex);
34+
});
35+
36+
$v8->executeString('
37+
try {
38+
PHP.throwException("Oops");
39+
}
40+
catch (e) {
41+
var_dump(e.getMessage()); // calls ExceptionFilter::getMessage
42+
var_dump(typeof e.getTrace);
43+
}
44+
', null, V8Js::FLAG_PROPAGATE_PHP_EXCEPTIONS);
45+
?>
46+
===EOF===
47+
--EXPECT--
48+
exception filter called.
49+
ExceptionFilter::__construct called!
50+
string(4) "Oops"
51+
getMessage called
52+
string(4) "Oops"
53+
string(9) "undefined"
54+
===EOF===
55+

0 commit comments

Comments
 (0)