0
0
mirror of https://github.com/phpv8/v8js.git synced 2025-04-21 19:24:36 +00:00

Merge remote-tracking branch 'rosmo/master'

This commit is contained in:
Stefan Siegl 2014-03-22 18:44:13 +01:00
commit ddbef50ea6
10 changed files with 428 additions and 5 deletions

@ -87,6 +87,12 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file name="skipif.inc" role="test" />
<file name="time_limit.phpt" role="test" />
<file name="variable_passing.phpt" role="test" />
<file name="checkstring.phpt" role="test" />
<file name="array_pass.phpt" role="test" />
<file name="array_pass_flags.phpt" role="test" />
<file name="timezones.phpt" role="test" />
<file name="long.phpt" role="test" />
<file name="datetime_pass.phpt" role="test" />
</dir>
</dir>
</contents>

@ -16,6 +16,10 @@
#ifndef PHP_V8JS_MACROS_H
#define PHP_V8JS_MACROS_H
#if __GNUC__ == 4 && __GNUC_MINOR__ == 4
#define _GLIBCXX_USE_NANOSLEEP 1
#endif
extern "C" {
#include "php.h"
#include "php_v8js.h"
@ -58,6 +62,7 @@ extern "C" {
#define V8JS_FLOAT(v) v8::Number::New(isolate, v)
#define V8JS_BOOL(v) ((v)?v8::True(isolate):v8::False(isolate))
#define V8JS_DATE(v) v8::Date::New(isolate, v)
#define V8JS_NULL v8::Null(isolate)
#define V8JS_UNDEFINED v8::Undefined(isolate)
#define V8JS_MN(name) v8js_method_##name
@ -193,6 +198,7 @@ struct php_v8js_ctx {
std::map<v8js_tmpl_t *, v8js_persistent_obj_t> weak_closures;
std::vector<php_v8js_accessor_ctx *> accessor_list;
char *tz;
#ifdef ZTS
void ***zts_ctx;
#endif
@ -238,6 +244,7 @@ ZEND_BEGIN_MODULE_GLOBALS(v8js)
/* Ini globals */
char *v8_flags; /* V8 command line flags */
bool use_date; /* Generate JS Date objects instead of PHP DateTime */
// Timer thread globals
std::stack<php_v8js_timer_ctx *> timer_stack;

99
tests/array_pass.phpt Normal file

@ -0,0 +1,99 @@
--TEST--
Test V8::executeString() : Check passing array from JS to PHP
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$v8 = new V8Js();
$v8->test = function ($arr) { var_dump($arr); };
try {
$v8->executeString('PHP.test([0, 1, 2]);');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
$v8->executeString('PHP.test({ "0" : "0", "1" : "1", "2" : "2" });');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
$v8->executeString('PHP.test({ "0" : "foo", "1" : "bar", "2" : "baz" });');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
$v8->executeString('PHP.test({ "foo" : "0", "bar" : "1", "baz" : "2" });');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
// includes gap
$v8->executeString('PHP.test({ "0" : "0", "2" : "2", "3" : "3" });');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
// mixed key types
$v8->executeString('PHP.test({ "0" : "0", "bar" : "1", "2" : "2" });');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
?>
===EOF===
--EXPECT--
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}
object(V8Object)#3 (3) {
["0"]=>
string(1) "0"
["1"]=>
string(1) "1"
["2"]=>
string(1) "2"
}
object(V8Object)#3 (3) {
["0"]=>
string(3) "foo"
["1"]=>
string(3) "bar"
["2"]=>
string(3) "baz"
}
object(V8Object)#3 (3) {
["foo"]=>
string(1) "0"
["bar"]=>
string(1) "1"
["baz"]=>
string(1) "2"
}
object(V8Object)#3 (3) {
["0"]=>
string(1) "0"
["2"]=>
string(1) "2"
["3"]=>
string(1) "3"
}
object(V8Object)#3 (3) {
["0"]=>
string(1) "0"
["2"]=>
string(1) "2"
["bar"]=>
string(1) "1"
}
===EOF===

@ -0,0 +1,99 @@
--TEST--
Test V8::executeString() : Check passing array from JS to PHP (using force array flag)
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$v8 = new V8Js();
$v8->test = function ($arr) { var_dump($arr); };
try {
$v8->executeString('PHP.test([0, 1, 2]);', "test", \V8Js::FLAG_FORCE_ARRAY);
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
$v8->executeString('PHP.test({ "0" : "0", "1" : "1", "2" : "2" });', "test", \V8Js::FLAG_FORCE_ARRAY);
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
$v8->executeString('PHP.test({ "0" : "foo", "1" : "bar", "2" : "baz" });', "test", \V8Js::FLAG_FORCE_ARRAY);
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
$v8->executeString('PHP.test({ "foo" : "0", "bar" : "1", "baz" : "2" });', "test", \V8Js::FLAG_FORCE_ARRAY);
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
// includes gap
$v8->executeString('PHP.test({ "0" : "0", "2" : "2", "3" : "3" });', "test", \V8Js::FLAG_FORCE_ARRAY);
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
// mixed key types
$v8->executeString('PHP.test({ "0" : "0", "bar" : "1", "2" : "2" });', "test", \V8Js::FLAG_FORCE_ARRAY);
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
?>
===EOF===
--EXPECT--
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}
array(3) {
[0]=>
string(1) "0"
[1]=>
string(1) "1"
[2]=>
string(1) "2"
}
array(3) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
[2]=>
string(3) "baz"
}
array(3) {
["foo"]=>
string(1) "0"
["bar"]=>
string(1) "1"
["baz"]=>
string(1) "2"
}
array(3) {
[0]=>
string(1) "0"
[2]=>
string(1) "2"
[3]=>
string(1) "3"
}
array(3) {
[0]=>
string(1) "0"
[2]=>
string(1) "2"
["bar"]=>
string(1) "1"
}
===EOF===

21
tests/checkstring.phpt Normal file

@ -0,0 +1,21 @@
--TEST--
Test V8::executeString() : Script validator test
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$v8 = new V8Js();
var_dump($v8->checkString('print("Hello World!");'));
try {
var_dump($v8->checkString('print("Hello World!);'));
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
?>
===EOF===
--EXPECT--
bool(true)
string(60) "V8Js::checkString():1: SyntaxError: Unexpected token ILLEGAL"
===EOF===

23
tests/datetime_pass.phpt Normal file

@ -0,0 +1,23 @@
--TEST--
Test V8::executeString() : Pass JS date to PHP
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
ini_set('v8js.use_date', 1);
$a = new V8Js();
$a->var = new \DateTime("Wed, 19 Mar 2014 14:37:11 +0000");
$a->executeString('print(PHP.var.toGMTString()); print("\n");');
ini_set('v8js.use_date', 0);
$a = new V8Js();
$a->var = new \DateTime("Wed, 19 Mar 2014 14:37:11 +0000");
$a->executeString('print(PHP.var.toString()); print("\n");');
?>
===EOF===
--EXPECT--
Wed, 19 Mar 2014 14:37:11 GMT
[object DateTime]
===EOF===

44
tests/long.phpt Normal file

@ -0,0 +1,44 @@
--TEST--
Test V8::executeString() : Check long integer handling from PHP to JS
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$v8 = new V8Js();
$v8->long = pow(2, 31);
try {
$v8->executeString('print(PHP.long); print("\n");');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
$v8->long = pow(2, 31) + 1;
try {
$v8->executeString('print(PHP.long); print("\n");');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
$v8->long = -pow(2, 31);
try {
$v8->executeString('print(PHP.long); print("\n");');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
$v8->long = -pow(2, 31) - 1;
try {
$v8->executeString('print(PHP.long); print("\n");');
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
?>
===EOF===
--EXPECT--
2147483648
2147483649
-2147483648
-2147483649
===EOF===

38
tests/timezones.phpt Normal file

@ -0,0 +1,38 @@
--TEST--
Test V8::executeString() : Check timezone handling
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php
$v8 = new V8Js();
try {
putenv('TZ=Europe/Helsinki');
$v8->executeString('print (new Date("Thu, 20 Mar 2014 09:03:24 +0000")).toString();');
echo "\n";
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
putenv('TZ=America/New_York');
$v8->executeString('print (new Date("Thu, 20 Mar 2014 09:03:24 +0000")).toString();');
echo "\n";
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
try {
putenv('TZ=Europe/Helsinki');
$v8->executeString('print (new Date("Thu, 20 Mar 2014 09:03:24 +0000")).toString();');
echo "\n";
} catch (V8JsScriptException $e) {
var_dump($e->getMessage());
}
?>
===EOF===
--EXPECT--
Thu Mar 20 2014 11:03:24 GMT+0200 (EET)
Thu Mar 20 2014 05:03:24 GMT-0400 (EDT)
Thu Mar 20 2014 11:03:24 GMT+0200 (EET)
===EOF===

69
v8js.cc

@ -54,10 +54,27 @@ static ZEND_INI_MH(v8js_OnUpdateV8Flags) /* {{{ */
return SUCCESS;
}
static ZEND_INI_MH(v8js_OnUpdateUseDate) /* {{{ */
{
bool value;
if (new_value_length==2 && strcasecmp("on", new_value)==0) {
value = (bool) 1;
} else if (new_value_length==3 && strcasecmp("yes", new_value)==0) {
value = (bool) 1;
} else if (new_value_length==4 && strcasecmp("true", new_value)==0) {
value = (bool) 1;
} else {
value = (bool) atoi(new_value);
}
V8JSG(use_date) = value;
return SUCCESS;
}
/* }}} */
ZEND_INI_BEGIN() /* {{{ */
ZEND_INI_ENTRY("v8js.flags", NULL, ZEND_INI_ALL, v8js_OnUpdateV8Flags)
ZEND_INI_ENTRY("v8js.use_date", "0", ZEND_INI_ALL, v8js_OnUpdateUseDate)
ZEND_INI_END()
/* }}} */
@ -261,7 +278,7 @@ int php_v8js_v8_get_properties_hash(v8::Handle<v8::Value> jsValue, HashTable *re
v8::Local<v8::String> jsKey = jsKeys->Get(i)->ToString();
/* Skip any prototype properties */
if (!jsObj->HasRealNamedProperty(jsKey) && !jsObj->HasRealNamedCallbackProperty(jsKey) && !jsObj->HasRealIndexedProperty(i)) {
if (!jsObj->HasOwnProperty(jsKey) && !jsObj->HasRealNamedProperty(jsKey) && !jsObj->HasRealNamedCallbackProperty(jsKey)) {
continue;
}
@ -1030,7 +1047,7 @@ static void php_v8js_timer_thread(TSRMLS_D)
*/
static PHP_METHOD(V8Js, executeString)
{
char *str = NULL, *identifier = NULL;
char *str = NULL, *identifier = NULL, *tz = NULL;
int str_len = 0, identifier_len = 0;
long flags = V8JS_FLAG_NONE, time_limit = 0, memory_limit = 0;
@ -1064,6 +1081,15 @@ static PHP_METHOD(V8Js, executeString)
/* Set flags for runtime use */
V8JS_GLOBAL_SET_FLAGS(isolate, flags);
/* Check if timezone has been changed and notify V8 */
tz = getenv("TZ");
if (c->tz != NULL) {
if (c->tz != NULL && strcmp(c->tz, tz) != 0) {
v8::Date::DateTimeConfigurationChangeNotification(c->isolate);
}
}
c->tz = tz;
if (time_limit > 0 || memory_limit > 0) {
// If timer thread is not running then start it
if (!V8JSG(timer_thread)) {
@ -1153,6 +1179,40 @@ static PHP_METHOD(V8Js, executeString)
}
/* }}} */
/* {{{ proto mixed V8Js::checkString(string script)
*/
static PHP_METHOD(V8Js, checkString)
{
char *str = NULL;
int str_len = 0;
long flags = V8JS_FLAG_NONE, time_limit = 0, memory_limit = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
return;
}
V8JS_BEGIN_CTX(c, getThis())
/* Catch JS exceptions */
v8::TryCatch try_catch;
/* Set script identifier */
v8::Local<v8::String> sname = V8JS_SYM("V8Js::checkString()");
/* Compiles a string context independently. TODO: Add a php function which calls this and returns the result as resource which can be executed later. */
v8::Local<v8::String> source = V8JS_STRL(str, str_len);
v8::Local<v8::Script> script = v8::Script::New(source, sname);
/* Compile errors? */
if (script.IsEmpty()) {
php_v8js_throw_script_exception(&try_catch TSRMLS_CC);
return;
}
RETURN_TRUE;
}
/* }}} */
#ifdef ENABLE_DEBUGGER_SUPPORT
/* {{{ proto void V8Js::__destruct()
__destruct for V8Js */
@ -1407,6 +1467,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_executestring, 0, 0, 1)
ZEND_ARG_INFO(0, memory_limit)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_checkstring, 0, 0, 1)
ZEND_ARG_INFO(0, script)
ZEND_END_ARG_INFO()
#ifdef ENABLE_DEBUGGER_SUPPORT
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_destruct, 0, 0, 0)
ZEND_END_ARG_INFO()
@ -1454,6 +1518,7 @@ static const zend_function_entry v8_function_methods[] = { /* {{{ */
static const zend_function_entry v8js_methods[] = { /* {{{ */
PHP_ME(V8Js, __construct, arginfo_v8js_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(V8Js, executeString, arginfo_v8js_executestring, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, checkString, arginfo_v8js_checkstring, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, getPendingException, arginfo_v8js_getpendingexception, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, setModuleLoader, arginfo_v8js_setmoduleloader, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, registerExtension, arginfo_v8js_registerextension, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)

@ -28,6 +28,7 @@ extern "C" {
#include "php_v8js_macros.h"
#include <v8.h>
#include <stdexcept>
#include <limits>
static void php_v8js_weak_object_callback(const v8::WeakCallbackData<v8::Object, zval> &data);
@ -884,6 +885,8 @@ static v8::Handle<v8::Value> php_v8js_hash_to_jsarr(zval *value, v8::Isolate *is
v8::Handle<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC) /* {{{ */
{
v8::Handle<v8::Value> jsValue;
long v;
zend_class_entry *ce;
switch (Z_TYPE_P(value))
{
@ -892,7 +895,19 @@ v8::Handle<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC)
break;
case IS_OBJECT:
jsValue = php_v8js_hash_to_jsobj(value, isolate TSRMLS_CC);
if (V8JSG(use_date)) {
ce = php_date_get_date_ce();
if (instanceof_function(Z_OBJCE_P(value), ce TSRMLS_CC)) {
zval *dtval;
zend_call_method_with_0_params(&value, NULL, NULL, "getTimestamp", &dtval);
if (dtval)
jsValue = V8JS_DATE(((double)Z_LVAL_P(dtval) * 1000.0));
else
jsValue = V8JS_NULL;
} else
jsValue = php_v8js_hash_to_jsobj(value, isolate TSRMLS_CC);
} else
jsValue = php_v8js_hash_to_jsobj(value, isolate TSRMLS_CC);
break;
case IS_STRING:
@ -900,7 +915,12 @@ v8::Handle<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC)
break;
case IS_LONG:
jsValue = V8JS_INT(Z_LVAL_P(value));
v = Z_LVAL_P(value);
if (v < - std::numeric_limits<int32_t>::min() || v > std::numeric_limits<int32_t>::max()) {
jsValue = V8JS_FLOAT((double)v);
} else {
jsValue = V8JS_INT(v);
}
break;
case IS_DOUBLE:
@ -926,7 +946,8 @@ int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags, v
{
v8::String::Utf8Value str(jsValue);
const char *cstr = ToCString(str);
RETVAL_STRING(cstr, 1);
RETVAL_STRINGL(cstr, jsValue->ToString()->Utf8Length(), 1);
// RETVAL_STRING(cstr, 1);
}
else if (jsValue->IsBoolean())
{