mirror of
https://github.com/phpv8/v8js.git
synced 2024-12-22 09:21:52 +00:00
Initial import
This commit is contained in:
parent
1fcedd8b09
commit
30e1d22863
11
Makefile.frag
Normal file
11
Makefile.frag
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
testv8: all
|
||||
$(PHP_EXECUTABLE) -n -d extension_dir=./modules -d extension=v8js.so test.php
|
||||
|
||||
debugv8: all
|
||||
gdb --arg $(PHP_EXECUTABLE) -n -d extension_dir=./modules -d extension=v8js.so test.php
|
||||
|
||||
valgrindv8: all
|
||||
USE_ZEND_ALLOC=0 valgrind --leak-check=full --show-reachable=yes --track-origins=yes $(PHP_EXECUTABLE) -n -d extension_dir=./modules -d extension=v8js.so test.php 2> valgrind.dump
|
||||
|
||||
|
5
TODO
Normal file
5
TODO
Normal file
@ -0,0 +1,5 @@
|
||||
- Feature: Extension registering from php.ini
|
||||
- Feature: Thread safety
|
||||
- Missing: Indexed property handlers
|
||||
- Bug: exception propagation fails when property getter is set
|
||||
- Bug: method_exists() leaks when used with V8 objects
|
2874
acinclude.m4
Normal file
2874
acinclude.m4
Normal file
File diff suppressed because it is too large
Load Diff
10857
aclocal.m4
vendored
Normal file
10857
aclocal.m4
vendored
Normal file
File diff suppressed because it is too large
Load Diff
73
config.m4
Normal file
73
config.m4
Normal file
@ -0,0 +1,73 @@
|
||||
dnl $Id$
|
||||
|
||||
PHP_ARG_WITH(v8js, for V8 Javascript Engine,
|
||||
[ --with-v8js Include V8 JavaScript Engine])
|
||||
|
||||
if test "$PHP_V8JS" != "no"; then
|
||||
SEARCH_PATH="/usr/local /usr"
|
||||
SEARCH_FOR="/include/v8.h"
|
||||
|
||||
if test -r $PHP_V8JS/$SEARCH_FOR; then
|
||||
V8_DIR=$PHP_V8JS
|
||||
else
|
||||
AC_MSG_CHECKING([for V8 files in default path])
|
||||
for i in $SEARCH_PATH ; do
|
||||
if test -r $i/$SEARCH_FOR; then
|
||||
V8_DIR=$i
|
||||
AC_MSG_RESULT(found in $i)
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if test -z "$V8_DIR"; then
|
||||
AC_MSG_RESULT([not found])
|
||||
AC_MSG_ERROR([Please reinstall the v8 distribution])
|
||||
fi
|
||||
|
||||
PHP_ADD_INCLUDE($V8_DIR/include)
|
||||
PHP_ADD_LIBRARY_WITH_PATH(v8, $V8_DIR/$PHP_LIBDIR, V8JS_SHARED_LIBADD)
|
||||
PHP_SUBST(V8JS_SHARED_LIBADD)
|
||||
PHP_REQUIRE_CXX()
|
||||
|
||||
AC_CACHE_CHECK(for V8 version, ac_cv_v8_version, [
|
||||
old_LIBS=$LIBS
|
||||
old_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS=-L$V8_DIR/$PHP_LIBDIR
|
||||
LIBS=-lv8
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_CPLUSPLUS
|
||||
AC_TRY_RUN([#include <v8.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
using namespace std;
|
||||
|
||||
int main ()
|
||||
{
|
||||
ofstream testfile ("conftestval");
|
||||
if (testfile.is_open()) {
|
||||
testfile << v8::V8::GetVersion();
|
||||
testfile << "\n";
|
||||
testfile.close();
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}], [ac_cv_v8_version=`cat ./conftestval`], [ac_cv_v8_version=NONE], [ac_cv_v8_version=NONE])
|
||||
AC_LANG_RESTORE
|
||||
LIBS=$old_LIBS
|
||||
LDFLAGS=$old_LDFLAGS
|
||||
])
|
||||
|
||||
if test "$ac_cv_v8_version" != "NONE"; then
|
||||
ac_IFS=$IFS
|
||||
IFS=.
|
||||
set $ac_cv_v8_version
|
||||
IFS=$ac_IFS
|
||||
V8_API_VERSION=`expr [$]1 \* 1000000 + [$]2 \* 1000 + [$]3`
|
||||
AC_DEFINE_UNQUOTED([PHP_V8_API_VERSION], $V8_API_VERSION, [ ])
|
||||
AC_DEFINE_UNQUOTED([PHP_V8_VERSION], "$ac_cv_v8_version", [ ])
|
||||
fi
|
||||
|
||||
PHP_NEW_EXTENSION(v8js, v8js.cc v8js_convert.cc v8js_methods.cc v8js_variables.cc, $ext_shared)
|
||||
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
fi
|
203
configure.in
Normal file
203
configure.in
Normal file
@ -0,0 +1,203 @@
|
||||
dnl This file becomes configure.in for self-contained extensions.
|
||||
|
||||
dnl divert(1)
|
||||
|
||||
AC_PREREQ(2.13)
|
||||
AC_INIT(config.m4)
|
||||
|
||||
PHP_CONFIG_NICE(config.nice)
|
||||
|
||||
dnl
|
||||
AC_DEFUN([PHP_EXT_BUILDDIR],[.])dnl
|
||||
AC_DEFUN([PHP_EXT_DIR],[""])dnl
|
||||
AC_DEFUN([PHP_EXT_SRCDIR],[$abs_srcdir])dnl
|
||||
AC_DEFUN([PHP_ALWAYS_SHARED],[
|
||||
ext_output="yes, shared"
|
||||
ext_shared=yes
|
||||
test "[$]$1" = "no" && $1=yes
|
||||
])dnl
|
||||
dnl
|
||||
|
||||
test -z "$CFLAGS" && auto_cflags=1
|
||||
|
||||
abs_srcdir=`(cd $srcdir && pwd)`
|
||||
abs_builddir=`pwd`
|
||||
|
||||
AC_PROG_CC([cc gcc])
|
||||
PHP_DETECT_ICC
|
||||
PHP_DETECT_SUNCC
|
||||
AC_PROG_CC_C_O
|
||||
|
||||
dnl Support systems with system libraries in e.g. /usr/lib64
|
||||
PHP_ARG_WITH(libdir, for system library directory,
|
||||
[ --with-libdir=NAME Look for libraries in .../NAME rather than .../lib], lib, no)
|
||||
|
||||
PHP_RUNPATH_SWITCH
|
||||
PHP_SHLIB_SUFFIX_NAMES
|
||||
|
||||
dnl Find php-config script
|
||||
PHP_ARG_WITH(php-config,,
|
||||
[ --with-php-config=PATH Path to php-config [php-config]], php-config, no)
|
||||
|
||||
dnl For BC
|
||||
PHP_CONFIG=$PHP_PHP_CONFIG
|
||||
prefix=`$PHP_CONFIG --prefix 2>/dev/null`
|
||||
phpincludedir=`$PHP_CONFIG --include-dir 2>/dev/null`
|
||||
INCLUDES=`$PHP_CONFIG --includes 2>/dev/null`
|
||||
EXTENSION_DIR=`$PHP_CONFIG --extension-dir 2>/dev/null`
|
||||
PHP_EXECUTABLE=`$PHP_CONFIG --php-binary 2>/dev/null`
|
||||
|
||||
if test -z "$prefix"; then
|
||||
AC_MSG_ERROR([Cannot find php-config. Please use --with-php-config=PATH])
|
||||
fi
|
||||
|
||||
php_shtool=$srcdir/build/shtool
|
||||
PHP_INIT_BUILD_SYSTEM
|
||||
|
||||
AC_MSG_CHECKING([for PHP prefix])
|
||||
AC_MSG_RESULT([$prefix])
|
||||
AC_MSG_CHECKING([for PHP includes])
|
||||
AC_MSG_RESULT([$INCLUDES])
|
||||
AC_MSG_CHECKING([for PHP extension directory])
|
||||
AC_MSG_RESULT([$EXTENSION_DIR])
|
||||
AC_MSG_CHECKING([for PHP installed headers prefix])
|
||||
AC_MSG_RESULT([$phpincludedir])
|
||||
|
||||
dnl Checks for PHP_DEBUG / ZEND_DEBUG / ZTS
|
||||
AC_MSG_CHECKING([if debug is enabled])
|
||||
old_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="-I$phpincludedir"
|
||||
AC_EGREP_CPP(php_debug_is_enabled,[
|
||||
#include <main/php_config.h>
|
||||
#if ZEND_DEBUG
|
||||
php_debug_is_enabled
|
||||
#endif
|
||||
],[
|
||||
PHP_DEBUG=yes
|
||||
],[
|
||||
PHP_DEBUG=no
|
||||
])
|
||||
CPPFLAGS=$old_CPPFLAGS
|
||||
AC_MSG_RESULT([$PHP_DEBUG])
|
||||
|
||||
AC_MSG_CHECKING([if zts is enabled])
|
||||
old_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="-I$phpincludedir"
|
||||
AC_EGREP_CPP(php_zts_is_enabled,[
|
||||
#include <main/php_config.h>
|
||||
#if ZTS
|
||||
php_zts_is_enabled
|
||||
#endif
|
||||
],[
|
||||
PHP_THREAD_SAFETY=yes
|
||||
],[
|
||||
PHP_THREAD_SAFETY=no
|
||||
])
|
||||
CPPFLAGS=$old_CPPFLAGS
|
||||
AC_MSG_RESULT([$PHP_DEBUG])
|
||||
|
||||
dnl Support for building and testing Zend extensions
|
||||
ZEND_EXT_TYPE="zend_extension"
|
||||
PHP_SUBST(ZEND_EXT_TYPE)
|
||||
|
||||
dnl Discard optimization flags when debugging is enabled
|
||||
if test "$PHP_DEBUG" = "yes"; then
|
||||
PHP_DEBUG=1
|
||||
ZEND_DEBUG=yes
|
||||
changequote({,})
|
||||
CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9s]*//g'`
|
||||
CXXFLAGS=`echo "$CXXFLAGS" | $SED -e 's/-O[0-9s]*//g'`
|
||||
changequote([,])
|
||||
dnl add -O0 only if GCC or ICC is used
|
||||
if test "$GCC" = "yes" || test "$ICC" = "yes"; then
|
||||
CFLAGS="$CFLAGS -O0"
|
||||
CXXFLAGS="$CXXFLAGS -O0"
|
||||
fi
|
||||
if test "$SUNCC" = "yes"; then
|
||||
if test -n "$auto_cflags"; then
|
||||
CFLAGS="-g"
|
||||
CXXFLAGS="-g"
|
||||
else
|
||||
CFLAGS="$CFLAGS -g"
|
||||
CXXFLAGS="$CFLAGS -g"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
PHP_DEBUG=0
|
||||
ZEND_DEBUG=no
|
||||
fi
|
||||
|
||||
dnl Always shared
|
||||
PHP_BUILD_SHARED
|
||||
|
||||
dnl Required programs
|
||||
PHP_PROG_RE2C
|
||||
PHP_PROG_AWK
|
||||
|
||||
sinclude(config.m4)
|
||||
|
||||
enable_static=no
|
||||
enable_shared=yes
|
||||
|
||||
dnl Only allow AC_PROG_CXX and AC_PROG_CXXCPP if they are explicitly called (by PHP_REQUIRE_CXX).
|
||||
dnl Otherwise AC_PROG_LIBTOOL fails if there is no working C++ compiler.
|
||||
AC_PROVIDE_IFELSE([PHP_REQUIRE_CXX], [], [
|
||||
undefine([AC_PROG_CXX])
|
||||
AC_DEFUN([AC_PROG_CXX], [])
|
||||
undefine([AC_PROG_CXXCPP])
|
||||
AC_DEFUN([AC_PROG_CXXCPP], [php_prog_cxxcpp=disabled])
|
||||
])
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
all_targets='$(PHP_MODULES) $(PHP_ZEND_EX)'
|
||||
install_targets="install-modules install-headers"
|
||||
phplibdir="`pwd`/modules"
|
||||
CPPFLAGS="$CPPFLAGS -DHAVE_CONFIG_H"
|
||||
CFLAGS_CLEAN='$(CFLAGS)'
|
||||
CXXFLAGS_CLEAN='$(CXXFLAGS)'
|
||||
|
||||
test "$prefix" = "NONE" && prefix="/usr/local"
|
||||
test "$exec_prefix" = "NONE" && exec_prefix='$(prefix)'
|
||||
|
||||
PHP_SUBST(PHP_MODULES)
|
||||
PHP_SUBST(PHP_ZEND_EX)
|
||||
|
||||
PHP_SUBST(all_targets)
|
||||
PHP_SUBST(install_targets)
|
||||
|
||||
PHP_SUBST(prefix)
|
||||
PHP_SUBST(exec_prefix)
|
||||
PHP_SUBST(libdir)
|
||||
PHP_SUBST(prefix)
|
||||
PHP_SUBST(phplibdir)
|
||||
PHP_SUBST(phpincludedir)
|
||||
|
||||
PHP_SUBST(CC)
|
||||
PHP_SUBST(CFLAGS)
|
||||
PHP_SUBST(CFLAGS_CLEAN)
|
||||
PHP_SUBST(CPP)
|
||||
PHP_SUBST(CPPFLAGS)
|
||||
PHP_SUBST(CXX)
|
||||
PHP_SUBST(CXXFLAGS)
|
||||
PHP_SUBST(CXXFLAGS_CLEAN)
|
||||
PHP_SUBST(EXTENSION_DIR)
|
||||
PHP_SUBST(PHP_EXECUTABLE)
|
||||
PHP_SUBST(EXTRA_LDFLAGS)
|
||||
PHP_SUBST(EXTRA_LIBS)
|
||||
PHP_SUBST(INCLUDES)
|
||||
PHP_SUBST(LFLAGS)
|
||||
PHP_SUBST(LDFLAGS)
|
||||
PHP_SUBST(SHARED_LIBTOOL)
|
||||
PHP_SUBST(LIBTOOL)
|
||||
PHP_SUBST(SHELL)
|
||||
PHP_SUBST(INSTALL_HEADERS)
|
||||
|
||||
PHP_GEN_BUILD_DIRS
|
||||
PHP_GEN_GLOBAL_MAKEFILE
|
||||
|
||||
test -d modules || $php_shtool mkdir modules
|
||||
touch .deps
|
||||
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
|
||||
AC_OUTPUT()
|
0
install-sh
Normal file
0
install-sh
Normal file
839
js/json-template.js
Normal file
839
js/json-template.js
Normal file
@ -0,0 +1,839 @@
|
||||
// Copyright (C) 2009 Andy Chu
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// $Id$
|
||||
|
||||
//
|
||||
// JavaScript implementation of json-template.
|
||||
//
|
||||
|
||||
// This is predefined in tests, shouldn't be defined anywhere else. TODO: Do
|
||||
// something nicer.
|
||||
var log = log || function() {};
|
||||
var repr = repr || function() {};
|
||||
|
||||
|
||||
// The "module" exported by this script is called "jsontemplate":
|
||||
|
||||
var jsontemplate = function() {
|
||||
|
||||
|
||||
// Regex escaping for metacharacters
|
||||
function EscapeMeta(meta) {
|
||||
return meta.replace(/([\{\}\(\)\[\]\|\^\$\-\+\?])/g, '\\$1');
|
||||
}
|
||||
|
||||
var token_re_cache = {};
|
||||
|
||||
function _MakeTokenRegex(meta_left, meta_right) {
|
||||
var key = meta_left + meta_right;
|
||||
var regex = token_re_cache[key];
|
||||
if (regex === undefined) {
|
||||
var str = '(' + EscapeMeta(meta_left) + '.*?' + EscapeMeta(meta_right) +
|
||||
'\n?)';
|
||||
regex = new RegExp(str, 'g');
|
||||
}
|
||||
return regex;
|
||||
}
|
||||
|
||||
//
|
||||
// Formatters
|
||||
//
|
||||
|
||||
function HtmlEscape(s) {
|
||||
return s.replace(/&/g,'&').
|
||||
replace(/>/g,'>').
|
||||
replace(/</g,'<');
|
||||
}
|
||||
|
||||
function HtmlTagEscape(s) {
|
||||
return s.replace(/&/g,'&').
|
||||
replace(/>/g,'>').
|
||||
replace(/</g,'<').
|
||||
replace(/"/g,'"');
|
||||
}
|
||||
|
||||
// Default ToString can be changed
|
||||
function ToString(s) {
|
||||
if (s === null) {
|
||||
return 'null';
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
// Formatter to pluralize words
|
||||
function _Pluralize(value, unused_context, args) {
|
||||
var s, p;
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
s = ''; p = 's';
|
||||
break;
|
||||
case 1:
|
||||
s = ''; p = args[0];
|
||||
break;
|
||||
case 2:
|
||||
s = args[0]; p = args[1];
|
||||
break;
|
||||
default:
|
||||
// Should have been checked at compile time
|
||||
throw {
|
||||
name: 'EvaluationError', message: 'pluralize got too many args'
|
||||
};
|
||||
}
|
||||
return (value > 1) ? p : s;
|
||||
}
|
||||
|
||||
function _Cycle(value, unused_context, args) {
|
||||
// Cycle between various values on consecutive integers.
|
||||
// @index starts from 1, so use 1-based indexing.
|
||||
return args[(value - 1) % args.length];
|
||||
}
|
||||
|
||||
var DEFAULT_FORMATTERS = {
|
||||
'html': HtmlEscape,
|
||||
'htmltag': HtmlTagEscape,
|
||||
'html-attr-value': HtmlTagEscape,
|
||||
'str': ToString,
|
||||
'raw': function(x) { return x; },
|
||||
'AbsUrl': function(value, context) {
|
||||
// TODO: Normalize leading/trailing slashes
|
||||
return context.get('base-url') + '/' + value;
|
||||
}
|
||||
};
|
||||
|
||||
var DEFAULT_PREDICATES = {
|
||||
'singular?': function(x) { return x == 1; },
|
||||
'plural?': function(x) { return x > 1; },
|
||||
'Debug?': function(unused, context) {
|
||||
try {
|
||||
return context.get('debug');
|
||||
} catch(err) {
|
||||
if (err.name == 'UndefinedVariable') {
|
||||
return false;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var FunctionRegistry = function() {
|
||||
return {
|
||||
lookup: function(user_str) {
|
||||
return [null, null];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var SimpleRegistry = function(obj) {
|
||||
return {
|
||||
lookup: function(user_str) {
|
||||
var func = obj[user_str] || null;
|
||||
return [func, null];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var CallableRegistry = function(callable) {
|
||||
return {
|
||||
lookup: function(user_str) {
|
||||
var func = callable(user_str);
|
||||
return [func, null];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Default formatters which can't be expressed in DEFAULT_FORMATTERS
|
||||
var PrefixRegistry = function(functions) {
|
||||
return {
|
||||
lookup: function(user_str) {
|
||||
for (var i = 0; i < functions.length; i++) {
|
||||
var name = functions[i].name, func = functions[i].func;
|
||||
if (user_str.slice(0, name.length) == name) {
|
||||
// Delimiter is usually a space, but could be something else
|
||||
var args;
|
||||
var splitchar = user_str.charAt(name.length);
|
||||
if (splitchar === '') {
|
||||
args = []; // No arguments
|
||||
} else {
|
||||
args = user_str.split(splitchar).slice(1);
|
||||
}
|
||||
return [func, args];
|
||||
}
|
||||
}
|
||||
return [null, null]; // No formatter
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var ChainedRegistry = function(registries) {
|
||||
return {
|
||||
lookup: function(user_str) {
|
||||
for (var i=0; i<registries.length; i++) {
|
||||
var result = registries[i].lookup(user_str);
|
||||
if (result[0]) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return [null, null]; // Nothing found
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// Template implementation
|
||||
//
|
||||
|
||||
function _ScopedContext(context, undefined_str) {
|
||||
// The stack contains:
|
||||
// The current context (an object).
|
||||
// An iteration index. -1 means we're NOT iterating.
|
||||
var stack = [{context: context, index: -1}];
|
||||
|
||||
return {
|
||||
PushSection: function(name) {
|
||||
if (name === undefined || name === null) {
|
||||
return null;
|
||||
}
|
||||
var new_context;
|
||||
if (name == '@') {
|
||||
new_context = stack[stack.length-1].context;
|
||||
} else {
|
||||
new_context = stack[stack.length-1].context[name] || null;
|
||||
}
|
||||
stack.push({context: new_context, index: -1});
|
||||
return new_context;
|
||||
},
|
||||
|
||||
Pop: function() {
|
||||
stack.pop();
|
||||
},
|
||||
|
||||
next: function() {
|
||||
var stacktop = stack[stack.length-1];
|
||||
|
||||
// Now we're iterating -- push a new mutable object onto the stack
|
||||
if (stacktop.index == -1) {
|
||||
stacktop = {context: null, index: 0};
|
||||
stack.push(stacktop);
|
||||
}
|
||||
|
||||
// The thing we're iterating over
|
||||
var context_array = stack[stack.length-2].context;
|
||||
|
||||
// We're already done
|
||||
if (stacktop.index == context_array.length) {
|
||||
stack.pop();
|
||||
return undefined; // sentinel to say that we're done
|
||||
}
|
||||
|
||||
stacktop.context = context_array[stacktop.index++];
|
||||
return true; // OK, we mutated the stack
|
||||
},
|
||||
|
||||
_Undefined: function(name) {
|
||||
if (undefined_str === undefined) {
|
||||
throw {
|
||||
name: 'UndefinedVariable', message: name + ' is not defined'
|
||||
};
|
||||
} else {
|
||||
return undefined_str;
|
||||
}
|
||||
},
|
||||
|
||||
_LookUpStack: function(name) {
|
||||
var i = stack.length - 1;
|
||||
while (true) {
|
||||
var frame = stack[i];
|
||||
if (name == '@index') {
|
||||
if (frame.index != -1) { // -1 is undefined
|
||||
return frame.index;
|
||||
}
|
||||
} else {
|
||||
var context = frame.context;
|
||||
if (typeof context === 'object') {
|
||||
var value = context[name];
|
||||
if (value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
i--;
|
||||
if (i <= -1) {
|
||||
return this._Undefined(name);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get: function(name) {
|
||||
if (name == '@') {
|
||||
return stack[stack.length-1].context;
|
||||
}
|
||||
var parts = name.split('.');
|
||||
var value = this._LookUpStack(parts[0]);
|
||||
if (parts.length > 1) {
|
||||
for (var i=1; i<parts.length; i++) {
|
||||
value = value[parts[i]];
|
||||
if (value === undefined) {
|
||||
return this._Undefined(parts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Crockford's "functional inheritance" pattern
|
||||
|
||||
var _AbstractSection = function(spec) {
|
||||
var that = {};
|
||||
that.current_clause = [];
|
||||
|
||||
that.Append = function(statement) {
|
||||
that.current_clause.push(statement);
|
||||
};
|
||||
|
||||
that.AlternatesWith = function() {
|
||||
throw {
|
||||
name: 'TemplateSyntaxError',
|
||||
message:
|
||||
'{.alternates with} can only appear with in {.repeated section ...}'
|
||||
};
|
||||
};
|
||||
|
||||
that.NewOrClause = function(pred) {
|
||||
throw { name: 'NotImplemented' }; // "Abstract"
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
var _Section = function(spec) {
|
||||
var that = _AbstractSection(spec);
|
||||
that.statements = {'default': that.current_clause};
|
||||
|
||||
that.section_name = spec.section_name;
|
||||
|
||||
that.Statements = function(clause) {
|
||||
clause = clause || 'default';
|
||||
return that.statements[clause] || [];
|
||||
};
|
||||
|
||||
that.NewOrClause = function(pred) {
|
||||
if (pred) {
|
||||
throw {
|
||||
name: 'TemplateSyntaxError',
|
||||
message: '{.or} clause only takes a predicate inside predicate blocks'
|
||||
};
|
||||
}
|
||||
that.current_clause = [];
|
||||
that.statements['or'] = that.current_clause;
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
// Repeated section is like section, but it supports {.alternates with}
|
||||
var _RepeatedSection = function(spec) {
|
||||
var that = _Section(spec);
|
||||
|
||||
that.AlternatesWith = function() {
|
||||
that.current_clause = [];
|
||||
that.statements['alternate'] = that.current_clause;
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
// Represents a sequence of predicate clauses.
|
||||
var _PredicateSection = function(spec) {
|
||||
var that = _AbstractSection(spec);
|
||||
// Array of func, statements
|
||||
that.clauses = [];
|
||||
|
||||
that.NewOrClause = function(pred) {
|
||||
// {.or} always executes if reached, so use identity func with no args
|
||||
pred = pred || [function(x) { return true; }, null];
|
||||
that.current_clause = [];
|
||||
that.clauses.push([pred, that.current_clause]);
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
|
||||
function _Execute(statements, context, callback) {
|
||||
for (var i=0; i<statements.length; i++) {
|
||||
var statement = statements[i];
|
||||
|
||||
if (typeof(statement) == 'string') {
|
||||
callback(statement);
|
||||
} else {
|
||||
var func = statement[0];
|
||||
var args = statement[1];
|
||||
func(args, context, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _DoSubstitute(statement, context, callback) {
|
||||
var value;
|
||||
value = context.get(statement.name);
|
||||
|
||||
// Format values
|
||||
for (var i=0; i<statement.formatters.length; i++) {
|
||||
var pair = statement.formatters[i];
|
||||
var formatter = pair[0];
|
||||
var args = pair[1];
|
||||
value = formatter(value, context, args);
|
||||
}
|
||||
|
||||
callback(value);
|
||||
}
|
||||
|
||||
// for [section foo]
|
||||
function _DoSection(args, context, callback) {
|
||||
|
||||
var block = args;
|
||||
var value = context.PushSection(block.section_name);
|
||||
var do_section = false;
|
||||
|
||||
// "truthy" values should have their sections executed.
|
||||
if (value) {
|
||||
do_section = true;
|
||||
}
|
||||
// Except: if the value is a zero-length array (which is "truthy")
|
||||
if (value && value.length === 0) {
|
||||
do_section = false;
|
||||
}
|
||||
|
||||
if (do_section) {
|
||||
_Execute(block.Statements(), context, callback);
|
||||
context.Pop();
|
||||
} else { // Empty list, None, False, etc.
|
||||
context.Pop();
|
||||
_Execute(block.Statements('or'), context, callback);
|
||||
}
|
||||
}
|
||||
|
||||
// {.pred1?} A {.or pred2?} B ... {.or} Z {.end}
|
||||
function _DoPredicates(args, context, callback) {
|
||||
// Here we execute the first clause that evaluates to true, and then stop.
|
||||
var block = args;
|
||||
var value = context.get('@');
|
||||
for (var i=0; i<block.clauses.length; i++) {
|
||||
var clause = block.clauses[i];
|
||||
var predicate = clause[0][0];
|
||||
var pred_args = clause[0][1];
|
||||
var statements = clause[1];
|
||||
|
||||
var do_clause = predicate(value, context, pred_args);
|
||||
if (do_clause) {
|
||||
_Execute(statements, context, callback);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _DoRepeatedSection(args, context, callback) {
|
||||
var block = args;
|
||||
|
||||
items = context.PushSection(block.section_name);
|
||||
pushed = true;
|
||||
|
||||
if (items && items.length > 0) {
|
||||
// TODO: check that items is an array; apparently this is hard in JavaScript
|
||||
//if type(items) is not list:
|
||||
// raise EvaluationError('Expected a list; got %s' % type(items))
|
||||
|
||||
// Execute the statements in the block for every item in the list.
|
||||
// Execute the alternate block on every iteration except the last. Each
|
||||
// item could be an atom (string, integer, etc.) or a dictionary.
|
||||
|
||||
var last_index = items.length - 1;
|
||||
var statements = block.Statements();
|
||||
var alt_statements = block.Statements('alternate');
|
||||
|
||||
for (var i=0; context.next() !== undefined; i++) {
|
||||
_Execute(statements, context, callback);
|
||||
if (i != last_index) {
|
||||
_Execute(alt_statements, context, callback);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_Execute(block.Statements('or'), context, callback);
|
||||
}
|
||||
|
||||
context.Pop();
|
||||
}
|
||||
|
||||
|
||||
var _SECTION_RE = /(repeated)?\s*(section)\s+(\S+)?/;
|
||||
var _OR_RE = /or(?:\s+(.+))?/;
|
||||
var _IF_RE = /if(?:\s+(.+))?/;
|
||||
|
||||
|
||||
// Turn a object literal, function, or Registry into a Registry
|
||||
function MakeRegistry(obj) {
|
||||
if (!obj) {
|
||||
// if null/undefined, use a totally empty FunctionRegistry
|
||||
return new FunctionRegistry();
|
||||
} else if (typeof obj === 'function') {
|
||||
return new CallableRegistry(obj);
|
||||
} else if (obj.lookup !== undefined) {
|
||||
// TODO: Is this a good pattern? There is a namespace conflict where get
|
||||
// could be either a formatter or a method on a FunctionRegistry.
|
||||
// instanceof might be more robust.
|
||||
return obj;
|
||||
} else if (typeof obj === 'object') {
|
||||
return new SimpleRegistry(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The compile function could be in a different module, in case we want to
|
||||
// compile on the server side.
|
||||
function _Compile(template_str, options) {
|
||||
var more_formatters = MakeRegistry(options.more_formatters);
|
||||
|
||||
// default formatters with arguments
|
||||
var default_formatters = PrefixRegistry([
|
||||
{name: 'pluralize', func: _Pluralize},
|
||||
{name: 'cycle', func: _Cycle}
|
||||
]);
|
||||
var all_formatters = new ChainedRegistry([
|
||||
more_formatters,
|
||||
SimpleRegistry(DEFAULT_FORMATTERS),
|
||||
default_formatters
|
||||
]);
|
||||
|
||||
var more_predicates = MakeRegistry(options.more_predicates);
|
||||
|
||||
// TODO: Add defaults
|
||||
var all_predicates = new ChainedRegistry([
|
||||
more_predicates, SimpleRegistry(DEFAULT_PREDICATES)
|
||||
]);
|
||||
|
||||
// We want to allow an explicit null value for default_formatter, which means
|
||||
// that an error is raised if no formatter is specified.
|
||||
var default_formatter;
|
||||
if (options.default_formatter === undefined) {
|
||||
default_formatter = 'str';
|
||||
} else {
|
||||
default_formatter = options.default_formatter;
|
||||
}
|
||||
|
||||
function GetFormatter(format_str) {
|
||||
var pair = all_formatters.lookup(format_str);
|
||||
if (!pair[0]) {
|
||||
throw {
|
||||
name: 'BadFormatter',
|
||||
message: format_str + ' is not a valid formatter'
|
||||
};
|
||||
}
|
||||
return pair;
|
||||
}
|
||||
|
||||
function GetPredicate(pred_str) {
|
||||
var pair = all_predicates.lookup(pred_str);
|
||||
if (!pair[0]) {
|
||||
throw {
|
||||
name: 'BadPredicate',
|
||||
message: pred_str + ' is not a valid predicate'
|
||||
};
|
||||
}
|
||||
return pair;
|
||||
}
|
||||
|
||||
var format_char = options.format_char || '|';
|
||||
if (format_char != ':' && format_char != '|') {
|
||||
throw {
|
||||
name: 'ConfigurationError',
|
||||
message: 'Only format characters : and | are accepted'
|
||||
};
|
||||
}
|
||||
|
||||
var meta = options.meta || '{}';
|
||||
var n = meta.length;
|
||||
if (n % 2 == 1) {
|
||||
throw {
|
||||
name: 'ConfigurationError',
|
||||
message: meta + ' has an odd number of metacharacters'
|
||||
};
|
||||
}
|
||||
var meta_left = meta.substring(0, n/2);
|
||||
var meta_right = meta.substring(n/2, n);
|
||||
|
||||
var token_re = _MakeTokenRegex(meta_left, meta_right);
|
||||
var current_block = _Section({});
|
||||
var stack = [current_block];
|
||||
|
||||
var strip_num = meta_left.length; // assume they're the same length
|
||||
|
||||
var token_match;
|
||||
var last_index = 0;
|
||||
|
||||
while (true) {
|
||||
token_match = token_re.exec(template_str);
|
||||
if (token_match === null) {
|
||||
break;
|
||||
} else {
|
||||
var token = token_match[0];
|
||||
}
|
||||
|
||||
// Add the previous literal to the program
|
||||
if (token_match.index > last_index) {
|
||||
var tok = template_str.slice(last_index, token_match.index);
|
||||
current_block.Append(tok);
|
||||
}
|
||||
last_index = token_re.lastIndex;
|
||||
|
||||
var had_newline = false;
|
||||
if (token.slice(-1) == '\n') {
|
||||
token = token.slice(null, -1);
|
||||
had_newline = true;
|
||||
}
|
||||
|
||||
token = token.slice(strip_num, -strip_num);
|
||||
|
||||
if (token.charAt(0) == '#') {
|
||||
continue; // comment
|
||||
}
|
||||
|
||||
if (token.charAt(0) == '.') { // Keyword
|
||||
token = token.substring(1, token.length);
|
||||
|
||||
var literal = {
|
||||
'meta-left': meta_left,
|
||||
'meta-right': meta_right,
|
||||
'space': ' ',
|
||||
'tab': '\t',
|
||||
'newline': '\n'
|
||||
}[token];
|
||||
|
||||
if (literal !== undefined) {
|
||||
current_block.Append(literal);
|
||||
continue;
|
||||
}
|
||||
|
||||
var new_block, func;
|
||||
|
||||
var section_match = token.match(_SECTION_RE);
|
||||
if (section_match) {
|
||||
var repeated = section_match[1];
|
||||
var section_name = section_match[3];
|
||||
|
||||
if (repeated) {
|
||||
func = _DoRepeatedSection;
|
||||
new_block = _RepeatedSection({section_name: section_name});
|
||||
} else {
|
||||
func = _DoSection;
|
||||
new_block = _Section({section_name: section_name});
|
||||
}
|
||||
current_block.Append([func, new_block]);
|
||||
stack.push(new_block);
|
||||
current_block = new_block;
|
||||
continue;
|
||||
}
|
||||
|
||||
var pred_str, pred;
|
||||
|
||||
// Check {.or pred?} before {.pred?}
|
||||
var or_match = token.match(_OR_RE);
|
||||
if (or_match) {
|
||||
pred_str = or_match[1];
|
||||
pred = pred_str ? GetPredicate(pred_str) : null;
|
||||
current_block.NewOrClause(pred);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Match either {.pred?} or {.if pred?}
|
||||
var matched = false;
|
||||
|
||||
var if_match = token.match(_IF_RE);
|
||||
if (if_match) {
|
||||
pred_str = if_match[1];
|
||||
matched = true;
|
||||
} else if (token.charAt(token.length-1) == '?') {
|
||||
pred_str = token;
|
||||
matched = true;
|
||||
}
|
||||
if (matched) {
|
||||
pred = pred_str ? GetPredicate(pred_str) : null;
|
||||
new_block = _PredicateSection();
|
||||
new_block.NewOrClause(pred);
|
||||
current_block.Append([_DoPredicates, new_block]);
|
||||
stack.push(new_block);
|
||||
current_block = new_block;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == 'alternates with') {
|
||||
current_block.AlternatesWith();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == 'end') {
|
||||
// End the block
|
||||
stack.pop();
|
||||
if (stack.length > 0) {
|
||||
current_block = stack[stack.length-1];
|
||||
} else {
|
||||
throw {
|
||||
name: 'TemplateSyntaxError',
|
||||
message: 'Got too many {end} statements'
|
||||
};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// A variable substitution
|
||||
var parts = token.split(format_char);
|
||||
var formatters;
|
||||
var name;
|
||||
if (parts.length == 1) {
|
||||
if (default_formatter === null) {
|
||||
throw {
|
||||
name: 'MissingFormatter',
|
||||
message: 'This template requires explicit formatters.'
|
||||
};
|
||||
}
|
||||
// If no formatter is specified, use the default.
|
||||
formatters = [GetFormatter(default_formatter)];
|
||||
name = token;
|
||||
} else {
|
||||
formatters = [];
|
||||
for (var j=1; j<parts.length; j++) {
|
||||
formatters.push(GetFormatter(parts[j]));
|
||||
}
|
||||
name = parts[0];
|
||||
}
|
||||
current_block.Append([_DoSubstitute, {name: name, formatters: formatters}]);
|
||||
if (had_newline) {
|
||||
current_block.Append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
// Add the trailing literal
|
||||
current_block.Append(template_str.slice(last_index));
|
||||
|
||||
if (stack.length !== 1) {
|
||||
throw {
|
||||
name: 'TemplateSyntaxError',
|
||||
message: 'Got too few {end} statements'
|
||||
};
|
||||
}
|
||||
return current_block;
|
||||
}
|
||||
|
||||
// The Template class is defined in the traditional style so that users can add
|
||||
// methods by mutating the prototype attribute. TODO: Need a good idiom for
|
||||
// inheritance without mutating globals.
|
||||
|
||||
function Template(template_str, options) {
|
||||
|
||||
// Add 'new' if we were not called with 'new', so prototyping works.
|
||||
if(!(this instanceof Template)) {
|
||||
return new Template(template_str, options);
|
||||
}
|
||||
|
||||
this._options = options || {};
|
||||
this._program = _Compile(template_str, this._options);
|
||||
}
|
||||
|
||||
Template.prototype.render = function(data_dict, callback) {
|
||||
// options.undefined_str can either be a string or undefined
|
||||
var context = _ScopedContext(data_dict, this._options.undefined_str);
|
||||
_Execute(this._program.Statements(), context, callback);
|
||||
};
|
||||
|
||||
Template.prototype.expand = function(data_dict) {
|
||||
var tokens = [];
|
||||
this.render(data_dict, function(x) { tokens.push(x); });
|
||||
return tokens.join('');
|
||||
};
|
||||
|
||||
// fromString is a construction method that allows metadata to be written at the
|
||||
// beginning of the template string. See Python's FromFile for a detailed
|
||||
// description of the format.
|
||||
//
|
||||
// The argument 'options' takes precedence over the options in the template, and
|
||||
// can be used for non-serializable options like template formatters.
|
||||
|
||||
var OPTION_RE = /^([a-zA-Z\-]+):\s*(.*)/;
|
||||
var OPTION_NAMES = [
|
||||
'meta', 'format-char', 'default-formatter', 'undefined-str'];
|
||||
// Use this "linear search" instead of Array.indexOf, which is nonstandard
|
||||
var OPTION_NAMES_RE = new RegExp(OPTION_NAMES.join('|'));
|
||||
|
||||
function fromString(s, options) {
|
||||
var parsed = {};
|
||||
var begin = 0, end = 0;
|
||||
|
||||
while (true) {
|
||||
var parsedOption = false;
|
||||
end = s.indexOf('\n', begin);
|
||||
if (end == -1) {
|
||||
break;
|
||||
}
|
||||
var line = s.slice(begin, end);
|
||||
begin = end+1;
|
||||
var match = line.match(OPTION_RE);
|
||||
if (match !== null) {
|
||||
var name = match[1].toLowerCase(), value = match[2];
|
||||
if (name.match(OPTION_NAMES_RE)) {
|
||||
name = name.replace('-', '_');
|
||||
value = value.replace(/^\s+/, '').replace(/\s+$/, '');
|
||||
if (name == 'default_formatter' && value.toLowerCase() == 'none') {
|
||||
value = null;
|
||||
}
|
||||
parsed[name] = value;
|
||||
parsedOption = true;
|
||||
}
|
||||
}
|
||||
if (!parsedOption) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: This doesn't enforce the blank line between options and template, but
|
||||
// that might be more trouble than it's worth
|
||||
if (parsed !== {}) {
|
||||
body = s.slice(begin);
|
||||
} else {
|
||||
body = s;
|
||||
}
|
||||
for (var o in options) {
|
||||
parsed[o] = options[o];
|
||||
}
|
||||
return Template(body, parsed);
|
||||
}
|
||||
|
||||
|
||||
// We just export one name for now, the Template "class".
|
||||
// We need HtmlEscape in the browser tests, so might as well export it.
|
||||
|
||||
return {
|
||||
Template: Template, HtmlEscape: HtmlEscape,
|
||||
FunctionRegistry: FunctionRegistry, SimpleRegistry: SimpleRegistry,
|
||||
CallableRegistry: CallableRegistry, ChainedRegistry: ChainedRegistry,
|
||||
fromString: fromString,
|
||||
// Private but exposed for testing
|
||||
_Section: _Section
|
||||
};
|
||||
|
||||
}();
|
339
js/jstparser.js
Normal file
339
js/jstparser.js
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
Copyright 2008, mark turansky (www.markturansky.com)
|
||||
Copyright 2010, Andrew Kelley (superjoesoftware.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
(This is the license from www.json.org and I think it's awesome)
|
||||
*/
|
||||
|
||||
String.prototype.endsWith = function endsWith(c) {
|
||||
if (this.charAt(this.length - 1) == c) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
String.prototype.startsWith = function startsWith(c) {
|
||||
if (this.charAt(0) == c) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
RegExp.quote = function(str) {
|
||||
return str.replace(/([.?*+\^$\[\]\\(){}\-])/g, "\\$1");
|
||||
};
|
||||
|
||||
String.prototype.replaceAll = function replaceAll(a, b) {
|
||||
return this.replace(new RegExp(RegExp.quote(a), 'g'), b);
|
||||
};
|
||||
|
||||
var Jst = function () {
|
||||
// private variables:
|
||||
var that; // reference to the public object
|
||||
|
||||
// all write and writeln functions eval'd by Jst
|
||||
// concatenate to this internal variable
|
||||
var html = "";
|
||||
|
||||
// private functions
|
||||
function CharacterStack(str) {
|
||||
var i;
|
||||
|
||||
this.characters = [];
|
||||
this.peek = function peek() {
|
||||
return this.characters[this.characters.length - 1];
|
||||
};
|
||||
this.pop = function pop() {
|
||||
return this.characters.pop();
|
||||
};
|
||||
this.push = function push(c) {
|
||||
this.characters.push(c);
|
||||
};
|
||||
this.hasMore = function hasMore() {
|
||||
if (this.characters.length > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
for (i = str.length; i >= 0; i -= 1) {
|
||||
this.characters.push(str.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
function StringWriter() {
|
||||
this.str = "";
|
||||
this.write = function write(s) {
|
||||
this.str += s;
|
||||
};
|
||||
this.toString = function toString() {
|
||||
return this.str;
|
||||
};
|
||||
}
|
||||
|
||||
function parseScriptlet(stack) {
|
||||
var fragment = new StringWriter();
|
||||
var c; // character
|
||||
|
||||
while (stack.hasMore()) {
|
||||
if (stack.peek() == '%') { //possible end delimiter
|
||||
c = stack.pop();
|
||||
if (stack.peek() == '>') { //end delimiter
|
||||
// pop > so that it is not available to main parse loop
|
||||
stack.pop();
|
||||
if (stack.peek() == '\n') {
|
||||
fragment.write(stack.pop());
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
fragment.write(c);
|
||||
}
|
||||
} else {
|
||||
fragment.write(stack.pop());
|
||||
}
|
||||
}
|
||||
return fragment.toString();
|
||||
}
|
||||
|
||||
function isOpeningDelimiter(c) {
|
||||
if (c == "<" || c == "%lt;") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isClosingDelimiter(c) {
|
||||
if (c == ">" || c == "%gt;") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function appendExpressionFragment(writer, fragment, jstWriter) {
|
||||
var i,j;
|
||||
var c;
|
||||
|
||||
// check to be sure quotes are on both ends of a string literal
|
||||
if (fragment.startsWith("\"") && !fragment.endsWith("\"")) {
|
||||
//some scriptlets end with \n, especially if the script ends the file
|
||||
if (fragment.endsWith("\n") && fragment.charAt(fragment.length - 2) == '"') {
|
||||
//we're ok...
|
||||
} else {
|
||||
throw { "message":"'" + fragment + "' is not properly quoted"};
|
||||
}
|
||||
}
|
||||
|
||||
if (!fragment.startsWith("\"") && fragment.endsWith("\"")) {
|
||||
throw { "message":"'" + fragment + "' is not properly quoted"};
|
||||
}
|
||||
|
||||
// print or println?
|
||||
if (fragment.endsWith("\n")) {
|
||||
writer.write(jstWriter + "ln(");
|
||||
//strip the newline
|
||||
fragment = fragment.substring(0, fragment.length - 1);
|
||||
} else {
|
||||
writer.write(jstWriter + "(");
|
||||
}
|
||||
|
||||
if (fragment.startsWith("\"") && fragment.endsWith("\"")) {
|
||||
//strip the quotes
|
||||
fragment = fragment.substring(1, fragment.length - 1);
|
||||
writer.write("\"");
|
||||
for (i = 0; i < fragment.length; i += 1) {
|
||||
c = fragment.charAt(i);
|
||||
if (c == '"') {
|
||||
writer.write("\\");
|
||||
writer.write(c);
|
||||
}
|
||||
}
|
||||
writer.write("\"");
|
||||
} else {
|
||||
for (j = 0; j < fragment.length; j += 1) {
|
||||
writer.write(fragment.charAt(j));
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(");");
|
||||
}
|
||||
|
||||
function appendTextFragment(writer, fragment) {
|
||||
var i;
|
||||
var c;
|
||||
|
||||
if (fragment.endsWith("\n")) {
|
||||
writer.write("writeln(\"");
|
||||
} else {
|
||||
writer.write("write(\"");
|
||||
}
|
||||
|
||||
for (i = 0; i < fragment.length; i += 1) {
|
||||
c = fragment.charAt(i);
|
||||
if (c == '"') {
|
||||
writer.write("\\");
|
||||
}
|
||||
// we took care of the line break with print vs. println
|
||||
if (c != '\n' && c != '\r') {
|
||||
writer.write(c);
|
||||
}
|
||||
}
|
||||
|
||||
writer.write("\");");
|
||||
}
|
||||
|
||||
function parseExpression(stack) {
|
||||
var fragment = new StringWriter();
|
||||
var c;
|
||||
|
||||
while (stack.hasMore()) {
|
||||
if (stack.peek() == '%') { //possible end delimiter
|
||||
c = stack.pop();
|
||||
if (isClosingDelimiter(stack.peek())) { //end delimiter
|
||||
//pop > so that it is not available to main parse loop
|
||||
stack.pop();
|
||||
if (stack.peek() == '\n') {
|
||||
fragment.write(stack.pop());
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
fragment.write("%");
|
||||
}
|
||||
} else {
|
||||
fragment.write(stack.pop());
|
||||
}
|
||||
}
|
||||
|
||||
return fragment.toString();
|
||||
}
|
||||
|
||||
function parseText(stack) {
|
||||
var fragment = new StringWriter();
|
||||
var c,d;
|
||||
|
||||
while (stack.hasMore()) {
|
||||
if (isOpeningDelimiter(stack.peek())) { //possible delimiter
|
||||
c = stack.pop();
|
||||
if (stack.peek() == '%') { // delimiter!
|
||||
// push c onto the stack to be used in main parse loop
|
||||
stack.push(c);
|
||||
break;
|
||||
} else {
|
||||
fragment.write(c);
|
||||
}
|
||||
} else {
|
||||
d = stack.pop();
|
||||
fragment.write(d);
|
||||
if (d == '\n') { //done with this fragment. println it.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fragment.toString();
|
||||
}
|
||||
|
||||
function safeWrite(s) {
|
||||
s = s.toString();
|
||||
s = s.replaceAll('&', '&');
|
||||
s = s.replaceAll('"', '"');
|
||||
s = s.replaceAll('<', '<');
|
||||
s = s.replaceAll('>', '>');
|
||||
html += s;
|
||||
}
|
||||
|
||||
function safeWriteln(s) {
|
||||
safeWrite(s + "\n");
|
||||
}
|
||||
|
||||
function write(s) {
|
||||
html += s;
|
||||
}
|
||||
|
||||
function writeln(s) {
|
||||
write(s + "\n");
|
||||
}
|
||||
|
||||
that = {
|
||||
// public methods:
|
||||
// pre-compile a template for quicker rendering. save the return value and
|
||||
// pass it to evaluate.
|
||||
compile: function (src) {
|
||||
var stack = new CharacterStack(src);
|
||||
var writer = new StringWriter();
|
||||
|
||||
var c;
|
||||
var fragment;
|
||||
while (stack.hasMore()) {
|
||||
if (isOpeningDelimiter(stack.peek())) { //possible delimiter
|
||||
c = stack.pop();
|
||||
if (stack.peek() == '%') { //delimiter!
|
||||
c = stack.pop();
|
||||
if (stack.peek() == "=") {
|
||||
// expression, escape all html
|
||||
stack.pop();
|
||||
fragment = parseExpression(stack);
|
||||
appendExpressionFragment(writer, fragment,
|
||||
"safeWrite");
|
||||
} else if (stack.peek() == "+") {
|
||||
// expression, don't escape html
|
||||
stack.pop();
|
||||
fragment = parseExpression(stack);
|
||||
appendExpressionFragment(writer, fragment,
|
||||
"write");
|
||||
} else {
|
||||
fragment = parseScriptlet(stack);
|
||||
writer.write(fragment);
|
||||
}
|
||||
} else { //not a delimiter
|
||||
stack.push(c);
|
||||
fragment = parseText(stack);
|
||||
appendTextFragment(writer, fragment);
|
||||
}
|
||||
} else {
|
||||
fragment = parseText(stack);
|
||||
appendTextFragment(writer, fragment);
|
||||
}
|
||||
}
|
||||
return writer.toString();
|
||||
},
|
||||
|
||||
// evaluate a pre-compiled script. recommended approach
|
||||
evaluate: function (script, args) {
|
||||
with(args) {
|
||||
html = "";
|
||||
eval(script);
|
||||
return html;
|
||||
}
|
||||
},
|
||||
|
||||
// if you're lazy, you can use this
|
||||
evaluateSingleShot: function (src, args) {
|
||||
return this.evaluate(this.compile(src), args);
|
||||
}
|
||||
};
|
||||
return that;
|
||||
}();
|
0
mkinstalldirs
Normal file
0
mkinstalldirs
Normal file
66
package.xml
Normal file
66
package.xml
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package packagerversion="1.4.11" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
|
||||
http://pear.php.net/dtd/tasks-1.0.xsd
|
||||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>v8js</name>
|
||||
<channel>pecl.php.net</channel>
|
||||
<summary>V8 Javascript Engine for PHP</summary>
|
||||
<description>
|
||||
This extension embeds the Google's V8 Javascript Engine into PHP.
|
||||
</description>
|
||||
<lead>
|
||||
<name>Jani Taskinen</name>
|
||||
<user>jani</user>
|
||||
<email>jani@php.net</email>
|
||||
<active>yes</active>
|
||||
</lead>
|
||||
|
||||
<date>2010-12-31</date>
|
||||
<version><release>0.1.1</release><api>0.1.1</api></version>
|
||||
<stability><release>beta</release><api>beta</api></stability>
|
||||
<license uri="http://www.php.net/license">PHP</license>
|
||||
<notes>
|
||||
- Fixed crash bug in setting v8.flags ini directive.
|
||||
- Added notice to registerExtension() if trying to use it when V8 is already initialized.
|
||||
</notes>
|
||||
|
||||
<contents>
|
||||
<dir name="/">
|
||||
<file name="CREDITS" role="doc" />
|
||||
<file name="config.m4" role="src" />
|
||||
<file name="Makefile.frag" role="src" />
|
||||
<file name="v8js.cc" role="src" />
|
||||
<file name="v8js_convert.cc" role="src" />
|
||||
<file name="v8js_methods.cc" role="src" />
|
||||
<file name="v8js_variables.cc" role="src" />
|
||||
<file name="php_v8js.h" role="src" />
|
||||
<file name="php_v8js_macros.h" role="src" />
|
||||
</dir> <!-- / -->
|
||||
</contents>
|
||||
<dependencies>
|
||||
<required>
|
||||
<php>
|
||||
<min>5.3.3</min>
|
||||
</php>
|
||||
<pearinstaller>
|
||||
<min>1.4.0</min>
|
||||
</pearinstaller>
|
||||
</required>
|
||||
</dependencies>
|
||||
<providesextension>v8js</providesextension>
|
||||
<extsrcrelease>
|
||||
<configureoption default="autodetect" name="with-v8js" prompt="Please provide the installation prefix of libv8" />
|
||||
</extsrcrelease>
|
||||
<changelog>
|
||||
<release>
|
||||
<date>2010-12-30</date>
|
||||
<version><release>0.1.0</release><api>0.1.0</api></version>
|
||||
<stability><release>beta</release><api>beta</api></stability>
|
||||
<license uri="http://www.php.net/license">PHP</license>
|
||||
<notes>
|
||||
- Initial PECL release.
|
||||
</notes>
|
||||
</release>
|
||||
</changelog>
|
||||
</package>
|
36
php_v8js.h
Normal file
36
php_v8js.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2010 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id: php_v8js.h 306926 2010-12-31 14:15:54Z felipe $ */
|
||||
|
||||
#ifndef PHP_V8JS_H
|
||||
#define PHP_V8JS_H
|
||||
|
||||
extern zend_module_entry v8js_module_entry;
|
||||
#define phpext_v8js_ptr &v8js_module_entry
|
||||
|
||||
#endif /* PHP_V8JS_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
100
php_v8js_macros.h
Normal file
100
php_v8js_macros.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2010 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id: php_v8js_macros.h 306926 2010-12-31 14:15:54Z felipe $ */
|
||||
|
||||
#ifndef PHP_V8JS_MACROS_H
|
||||
#define PHP_V8JS_MACROS_H
|
||||
|
||||
#include <v8.h>
|
||||
|
||||
/* V8Js Version */
|
||||
#define V8JS_VERSION "0.1.1"
|
||||
|
||||
/* Helper macros */
|
||||
#define V8JS_SYM(v) v8::String::NewSymbol(v, sizeof(v) - 1)
|
||||
#define V8JS_SYML(v, l) v8::String::NewSymbol(v, l)
|
||||
#define V8JS_STR(v) v8::String::New(v)
|
||||
#define V8JS_STRL(v, l) v8::String::New(v, l)
|
||||
#define V8JS_INT(v) v8::Integer::New(v)
|
||||
#define V8JS_FLOAT(v) v8::Number::New(v)
|
||||
#define V8JS_BOOL(v) v8::Boolean::New(v)
|
||||
#define V8JS_NULL v8::Null()
|
||||
#define V8JS_MN(name) v8js_method_##name
|
||||
#define V8JS_METHOD(name) v8::Handle<v8::Value> V8JS_MN(name)(const v8::Arguments& args)
|
||||
#define V8JS_THROW(type, message, message_len) v8::ThrowException(v8::Exception::type(V8JS_STRL(message, message_len)))
|
||||
#define V8JS_GLOBAL v8::Context::GetCurrent()->Global()
|
||||
|
||||
/* Helper macros */
|
||||
#if PHP_V8_API_VERSION < 2005009
|
||||
# define V8JS_GET_CLASS_NAME(var, obj) \
|
||||
/* Hack to prevent calling possibly set user-defined __toString() messing class name */ \
|
||||
v8::Local<v8::Function> constructor = v8::Local<v8::Function>::Cast(obj->Get(V8JS_SYM("constructor"))); \
|
||||
v8::String::Utf8Value var(constructor->GetName());
|
||||
#else
|
||||
# define V8JS_GET_CLASS_NAME(var, obj) \
|
||||
v8::String::Utf8Value var(obj->GetConstructorName());
|
||||
#endif
|
||||
|
||||
/* Global flags */
|
||||
#define V8JS_GLOBAL_SET_FLAGS(flags) V8JS_GLOBAL->SetHiddenValue(V8JS_SYM("__php_flags__"), V8JS_INT(flags))
|
||||
#define V8JS_GLOBAL_GET_FLAGS() V8JS_GLOBAL->GetHiddenValue(V8JS_SYM("__php_flags__"))->IntegerValue();
|
||||
|
||||
/* Options */
|
||||
#define V8JS_FLAG_NONE (1<<0)
|
||||
#define V8JS_FLAG_FORCE_ARRAY (1<<1)
|
||||
|
||||
/* Extracts a C string from a V8 Utf8Value. */
|
||||
static const char * ToCString(const v8::String::Utf8Value &value) /* {{{ */
|
||||
{
|
||||
return *value ? *value : "<string conversion failed>";
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Extern Class entries */
|
||||
extern zend_class_entry *php_ce_v8_object;
|
||||
extern zend_class_entry *php_ce_v8_function;
|
||||
|
||||
/* Create PHP V8 object */
|
||||
void php_v8js_create_v8(zval *, v8::Handle<v8::Value>, int TSRMLS_DC);
|
||||
|
||||
/* Fetch V8 object properties */
|
||||
int php_v8js_v8_get_properties_hash(v8::Handle<v8::Value>, HashTable *, int TSRMLS_DC);
|
||||
|
||||
/* Convert zval into V8 value */
|
||||
v8::Handle<v8::Value> zval_to_v8js(zval * TSRMLS_DC);
|
||||
|
||||
/* Convert V8 value into zval */
|
||||
int v8js_to_zval(v8::Handle<v8::Value>, zval *, int TSRMLS_DC);
|
||||
|
||||
/* Register builtin methods into passed object */
|
||||
void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate>);
|
||||
|
||||
/* Register accessors into passed object */
|
||||
void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate>, zval * TSRMLS_DC);
|
||||
|
||||
#endif /* PHP_V8JS_MACROS_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
2456
run-tests.php
Normal file
2456
run-tests.php
Normal file
File diff suppressed because it is too large
Load Diff
15
samples/dlopen.supp
Normal file
15
samples/dlopen.supp
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
Ignore dlopen bug #1.
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:_dl_open
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
Ignore dlopen bug #2.
|
||||
Memcheck:Leak
|
||||
...
|
||||
fun:_dl_close
|
||||
...
|
||||
}
|
74
samples/test_call.php
Normal file
74
samples/test_call.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
var $bar = 'foobar';
|
||||
|
||||
function MyOwnFunc() {}
|
||||
|
||||
function __Get($name) {
|
||||
echo "Called __get(): ";
|
||||
var_dump($name);
|
||||
}
|
||||
function __Set($name, $args) {
|
||||
echo "Called __set(): ";
|
||||
var_dump($name, $args);
|
||||
}
|
||||
function __Isset($name) {
|
||||
echo "Called __isset(): ";
|
||||
var_dump($name);
|
||||
return true;
|
||||
}
|
||||
function __call($name, $args) {
|
||||
echo "Called __call(): ";
|
||||
var_dump($name, $args);
|
||||
}
|
||||
function __Invoke($name, $arg1, $arg2) {
|
||||
echo "Called __invoke(): ";
|
||||
var_dump(func_get_args());
|
||||
return 'foobar';
|
||||
}
|
||||
function __toString() {
|
||||
echo "Called __tostring: ";
|
||||
return $this->bar;
|
||||
}
|
||||
}
|
||||
|
||||
$blaa = new V8Js();
|
||||
$blaa->obj = new Foo;
|
||||
|
||||
try {
|
||||
echo "__invoke()\n";
|
||||
$blaa->executeString("var_dump(PHP.obj('arg1','arg2','arg3'));", "invoke_test1 #1.js");
|
||||
echo "------------\n";
|
||||
|
||||
echo " __invoke() with new\n";
|
||||
$blaa->executeString("myobj = new PHP.obj('arg1','arg2','arg3'); var_dump(myobj);", "invoke_test2 #2.js");
|
||||
echo "------------\n";
|
||||
|
||||
echo " __tostring()\n";
|
||||
$blaa->executeString('print(PHP.obj + "\n");', "tostring_test #3.js");
|
||||
echo "------------\n";
|
||||
|
||||
echo " __isset() not called with existing property\n";
|
||||
$blaa->executeString('if ("bar" in PHP.obj) print("bar exists\n");', "isset_test1 #4.js");
|
||||
echo "------------\n";
|
||||
|
||||
echo " __isset() with non-existing property\n";
|
||||
$blaa->executeString('if (!("foobar" in PHP.obj)) print("foobar does not exist\n"); else print("We called __isset and it said yes!\n");', "isset_test2 #5.js");
|
||||
echo "------------\n";
|
||||
|
||||
echo " __get() not called with existing property\n";
|
||||
$blaa->executeString('var_dump(PHP.obj.bar);', "get_test1 #6.js");
|
||||
echo "------------\n";
|
||||
|
||||
echo " __get() with non-existing property\n";
|
||||
$blaa->executeString('var_dump(PHP.obj.foo);', "get_test2 #7.js");
|
||||
echo "------------\n";
|
||||
|
||||
echo " __call()\n";
|
||||
$blaa->executeString('PHP.obj.foo(1,2,3);', "call_test1 #8.js");
|
||||
echo "------------\n";
|
||||
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
32
samples/test_callback.php
Normal file
32
samples/test_callback.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
$a = new V8Js();
|
||||
|
||||
// Should not work with closure
|
||||
$a->test = function ($params) { return (method_exists($params, 'cb1')) ? $params->cb1("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test(function (foo) { return foo + " world"; });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// Test is_a()
|
||||
$a->test = function ($params) { return (is_a($params, 'V8Object')) ? $params->cb1("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// Test is_a()
|
||||
$a->test = function ($params) { return (is_a($params, 'V8Function')) ? $params("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test(function (foo) { return foo + " world"; });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// Should not work with object
|
||||
$a->test = function ($params) { return (is_a($params, 'Closure')) ? $params("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
$a->test = function ($params) { var_dump($params); return $params->cb1("hello"); };
|
||||
$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// FIX! method_exists() Leaks!
|
||||
$a->test = function ($params) { var_dump($params, method_exists($params, 'cb1'), $params->cb1); };
|
||||
$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
|
||||
|
11
samples/test_closure.php
Normal file
11
samples/test_closure.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
$a = new V8Js();
|
||||
$a->func = function ($a) { echo "Closure..\n"; };
|
||||
|
||||
try {
|
||||
$a->executeString("print(PHP.func); PHP.func(1);", "closure_test.js");
|
||||
$a->executeString("print(PHP.func); PHP.func(1);", "closure_test.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
5
samples/test_crash.php
Normal file
5
samples/test_crash.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php {
|
||||
$a = new V8Js();
|
||||
|
||||
var_dump($a->executeString('Jst.write = function(s) { html += "EI TOIMI"; };' ."\n" .' Jst.evaluate("lol testi <%= 1 %>", {});'));
|
||||
}
|
9
samples/test_date.php
Normal file
9
samples/test_date.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
$a = new V8Js();
|
||||
|
||||
try {
|
||||
var_dump($a->executeString("date = new Date('September 8, 1975 09:00:00'); print(date + '\\n'); date;", "test.js"));
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
59
samples/test_dumper.php
Normal file
59
samples/test_dumper.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
var $foo = 'bar';
|
||||
var $true = true;
|
||||
var $false = false;
|
||||
var $bar = array(1,2,3,1.23456789);
|
||||
var $ass = array("life" => 42, "foo" => "bar");
|
||||
function __set($name, $value)
|
||||
{
|
||||
echo "I'm setter!\n";
|
||||
var_dump($name, $value);
|
||||
}
|
||||
function __get($name)
|
||||
{
|
||||
echo "I'm getter!\n";
|
||||
var_dump($name);
|
||||
}
|
||||
function __call($name, $args)
|
||||
{
|
||||
echo "I'm caller!\n";
|
||||
var_dump($name, $args);
|
||||
}
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$obj = new Foo;
|
||||
$a->arr = array("foobar" => $obj);
|
||||
|
||||
$JS = <<< 'EOF'
|
||||
var example = new Object;
|
||||
example.foo = function () {
|
||||
print("this is foo");
|
||||
}
|
||||
example.bar = function () {
|
||||
print("this is bar");
|
||||
}
|
||||
example.__noSuchMethod__ = function (id, args) {
|
||||
print("tried to handle unknown method " + id);
|
||||
if (args.length != 0)
|
||||
print("it had arguments: " + args);
|
||||
}
|
||||
example.foo(); // alerts "this is foo"
|
||||
example.bar(); // alerts "this is bar"
|
||||
example.grill(); // alerts "tried to handle unknown method grill"
|
||||
example.ding("dong"); // alerts "tried to handle unknown method ding"
|
||||
EOF;
|
||||
|
||||
try {
|
||||
$a->executeString("var myarr = new Array(); myarr[0] = 'foo'; myarr[1] = 'bar'; var_dump(myarr); var_dump(new Date('September 8, 1975 09:00:00'))", "call_test1.js");
|
||||
$a->executeString("var_dump(PHP.arr.foobar.bar);", "call_test2.js");
|
||||
$a->executeString("var_dump(PHP.arr.foobar.bar[0]);", "call_test3.js");
|
||||
$a->executeString("var_dump(var_dump(PHP.arr));", "call_test4.js");
|
||||
$a->executeString("var patt1=/[^a-h]/g; var_dump(patt1);", "call_test5.js");
|
||||
$a->executeString("var_dump(Math.PI, Infinity, null, undefined);", "call_test6.js");
|
||||
// $a->executeString($JS);
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
26
samples/test_exception.php
Normal file
26
samples/test_exception.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php {
|
||||
class Foo {
|
||||
private $v8 = NULL;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->v8 = new V8Js();
|
||||
$this->v8->foo = $this;
|
||||
var_dump($this->v8->executeString('throw 1; PHP.foo.bar();', 'trycatch1'));
|
||||
var_dump($this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print("catched!\n"); }', 'trycatch2'));
|
||||
}
|
||||
|
||||
public function bar()
|
||||
{
|
||||
echo "To Bar!\n";
|
||||
var_dump($this->v8->executeString('throw new Error();', 'throw'));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
|
||||
}
|
||||
|
||||
}
|
34
samples/test_exception2.php
Normal file
34
samples/test_exception2.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php {
|
||||
class Foo {
|
||||
private $v8 = NULL;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->v8 = new V8Js(null, array(), false);
|
||||
$this->v8->foo = $this;
|
||||
// $this->v8->executeString('asdasda< / sd', 'trycatch0');
|
||||
// $this->v8->executeString('blahnothere', 'trycatch1');
|
||||
// $this->v8->executeString('throw new SyntaxError();', 'throw');
|
||||
// $this->v8->executeString('function foo() {throw new SyntaxError();}', 'trycatch2');
|
||||
// $this->v8->executeString('try { foo(); } catch (e) { print(e + " catched by pure JS!\n"); }', 'trycatch3');
|
||||
// $this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " catched via PHP callback!\n"); }', 'trycatch4');
|
||||
// $this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print("catched!\n"); }', 'trycatch5');
|
||||
// $this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print("catched!\n"); }', 'trycatch5');
|
||||
var_dump($this->v8->getPendingException());
|
||||
}
|
||||
|
||||
public function bar()
|
||||
{
|
||||
// $this->v8->executeString('asdasda< / sd', 'trycatch0');
|
||||
// $this->v8->executeString('blahnothere', 'trycatch1');
|
||||
$this->v8->executeString('throw new Error();', 'throw');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
}
|
23
samples/test_extend.php
Normal file
23
samples/test_extend.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
// Test class
|
||||
class Testing extends V8Js
|
||||
{
|
||||
public $foo = 'ORIGINAL';
|
||||
private $my_private = 'arf'; // Should not show in JS side
|
||||
protected $my_protected = 'argh'; // Should not show in JS side
|
||||
|
||||
public function mytest($a, $b, $c = NULL)
|
||||
{
|
||||
var_dump(func_get_args());
|
||||
}
|
||||
}
|
||||
|
||||
$a = new Testing();
|
||||
echo $a;
|
||||
|
||||
try {
|
||||
$a->executeString("PHP.mytest(PHP.foo, PHP.my_private, PHP.my_protected);", "test7.js");
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
9
samples/test_extension.php
Normal file
9
samples/test_extension.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
V8Js::registerExtension('a', file_get_contents('js/json-template.js'), array('b'));
|
||||
V8Js::registerExtension('b', file_get_contents('js/jstparser.js'), array('a'));
|
||||
|
||||
var_dump(V8JS::getExtensions());
|
||||
|
||||
$a = new V8Js('myobj', array(), array('b'));
|
||||
|
48
samples/test_method.php
Normal file
48
samples/test_method.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
// Test class
|
||||
class Testing
|
||||
{
|
||||
public $foo = 'ORIGINAL';
|
||||
private $my_private = 'arf'; // Should not show in JS side
|
||||
protected $my_protected = 'argh'; // Should not show in JS side
|
||||
|
||||
function mytest($a, $b, $c = NULL)
|
||||
{
|
||||
var_dump(func_get_args());
|
||||
}
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->myobj = new Testing();
|
||||
|
||||
$a->executeString("PHP.myobj.mytest('arg1', 'arg2');", "test1.js");
|
||||
$a->executeString("PHP.myobj.mytest(true, false, 1234567890);", "test2.js");
|
||||
$a->executeString("PHP.myobj.mytest(3.14, 42, null);", "test3.js");
|
||||
|
||||
// Invalid parameters
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest();", "test4.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest('arg1', 'arg2', 'arg3', 'extra_arg');", "test5.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
// Array / Object
|
||||
try {
|
||||
// date_default_timezone_set("UTC");
|
||||
$a->executeString("date = new Date('September 8, 1975 09:00:00'); PHP.print(date); PHP.myobj.mytest(date, PHP.myobj, new Array(1,2,3));", "test6.js");
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest(PHP.myobj, new Array(1,2,3), new Array('foo', 'bar', PHP.myobj));", "test7.js");
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
173
test.php
Normal file
173
test.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/*
|
||||
$v8 = new V8Js;
|
||||
$v8->func = function ($a) { return var_export(func_get_args(), TRUE); };
|
||||
$v8->executeString("PHP.func();", "arg_test1.js");
|
||||
exit;
|
||||
*/
|
||||
|
||||
var_dump(V8Js::registerExtension('myparser.js', 'function foo() { print("foobar!\n"}', array('jstparser.js', 'json-template.js'), false));
|
||||
var_dump(V8Js::registerExtension('myparser.js', 'function foo() { print("foobar!\n"}', array('jstparser.js', 'json-template.js'), false));
|
||||
var_dump(V8Js::registerExtension('jstparser.js', file_get_contents('js/jstparser.js'), array(), false));
|
||||
//V8Js::registerExtension('json-template.js', file_get_contents('js/json-template.js'), array(), false);
|
||||
|
||||
var_dump(V8JS::getExtensions());
|
||||
|
||||
$a = new V8Js('myobj', array(), array('jstparser.js'));
|
||||
|
||||
$jstparser = <<< 'EOT'
|
||||
var template = 'Gold & Hot Rod Red, as seen in the new <a href="http://blog.markturansky.com/archives/51">Iron Man trailer</a>!' + "\n" +
|
||||
'<table cellspacing="0" cellpadding="4">' + "\n" +
|
||||
' <% for(var i = 0; i < 10; i++){ %> ' + "\n" +
|
||||
' <tr>' + "\n" +
|
||||
' <td style="background-color: <%= i % 2 == 0 ? \'red\' : \'gold\' %>">' + "\n" +
|
||||
' Hi, <%=name%>! i is <%= i %>' + "\n" +
|
||||
' </td>' + "\n" +
|
||||
' </tr>' + "\n" +
|
||||
' <% } %>' + "\n" +
|
||||
'</table>' + "\n" +
|
||||
'Note that name is HTML escaped by default. Here it is without escaping:'+
|
||||
'<%+ name %>';
|
||||
Jst.evaluateSingleShot(template, {"name":"foobar"});
|
||||
EOT;
|
||||
|
||||
echo($a->executeString($jstparser, "ext_test1.js")), "\n";
|
||||
|
||||
$a->_SERVER = $_SERVER;
|
||||
$a->func = function ($a) { echo "Closure..\n"; };
|
||||
|
||||
$a->executeString("print(myobj._SERVER['HOSTNAME']);", "test1.js");
|
||||
$a->executeString("print(myobj.func); myobj.func(1);", "closure_test.js");
|
||||
|
||||
$JS = <<<'EOT'
|
||||
function dump(a)
|
||||
{
|
||||
for (var i in a) {
|
||||
var val = a[i];
|
||||
print(i + ' => ' + val + "\n");
|
||||
}
|
||||
}
|
||||
function foo()
|
||||
{
|
||||
var bar = 'bar';
|
||||
var foo = 'foo';
|
||||
return foo + bar;
|
||||
}
|
||||
function test()
|
||||
{
|
||||
var a = 'PHP version: ' + PHP.phpver;
|
||||
phpver = 'changed in JS!';
|
||||
return a;
|
||||
}
|
||||
function loop()
|
||||
{
|
||||
var foo = 'foo';
|
||||
while(true)
|
||||
{
|
||||
foo + 'bar';
|
||||
}
|
||||
}
|
||||
function output()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
print("output:foo\n");
|
||||
sleep(5);
|
||||
exit();
|
||||
}
|
||||
};
|
||||
function simplearray()
|
||||
{
|
||||
print(myarray.a + "\n");
|
||||
print(myarray.b + "\n");
|
||||
print(myarray.c + "\n");
|
||||
print(myarray.d + "\n");
|
||||
}
|
||||
function bigarray()
|
||||
{
|
||||
print(PHP.$_SERVER['HOSTNAME'] + "\n");
|
||||
print(PHP.$_SERVER.argv + "\n");
|
||||
}
|
||||
EOT;
|
||||
|
||||
$jsontemplate = <<< EOT
|
||||
var t = jsontemplate.Template("{# This is a comment and will be removed from the output.}{.section songs}<h2>Songs in '{playlist-name}'</h2><table width=\"100%\">{.repeated section @}<tr><td><a href=\"{url-base|htmltag}{url|htmltag}\">Play</a><td><i>{title}</i></td><td>{artist}</td></tr>{.end}</table>{.or}<p><em>(No page content matches)</em></p>{.end}");
|
||||
t.expand({
|
||||
"url-base": "http://example.com/music/",
|
||||
"playlist-name": "Epic Playlist",
|
||||
"songs": [
|
||||
{
|
||||
"url": "1.mp3",
|
||||
"artist": "Grayceon",
|
||||
"title": "Sounds Like Thunder"
|
||||
},
|
||||
{
|
||||
"url": "2.mp3",
|
||||
"artist": "Thou",
|
||||
"title": "Their Hooves Carve Craters in the Earth"
|
||||
}]});
|
||||
EOT;
|
||||
|
||||
class tester
|
||||
{
|
||||
public $foo = 'bar';
|
||||
private $my_private = 'arf';
|
||||
protected $my_protected = 'argh';
|
||||
|
||||
function mytest() { echo 'Here be monsters..', "\n"; }
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->obj = new tester();
|
||||
$a->phpver = phpversion();
|
||||
$a->argv = $_SERVER['argv'];
|
||||
$a->integer = 1;
|
||||
$a->float = 3.14;
|
||||
$a->{'$'._SERVER} = $_SERVER;
|
||||
$a->GLOBALS = $GLOBALS;
|
||||
$a->myarray = array(
|
||||
'a' => 'Array value for key A',
|
||||
'b' => 'Array value for key B',
|
||||
'c' => 'Array value for key C',
|
||||
'd' => 'Array value for key D',
|
||||
);
|
||||
$a->arr = array("first", "second", "third");
|
||||
|
||||
$a->executeString($JS, "test1.js");
|
||||
$a->executeString("bigarray()", "test1.js");
|
||||
|
||||
try {
|
||||
echo($a->executeString($jstparser, "test2.js")), "\n";
|
||||
var_dump($a->executeString($jsontemplate, "test1.js"));
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
// Test for context handling
|
||||
|
||||
$a->executeString($JS, "test1.js");
|
||||
$a->executeString("bigarray();");
|
||||
|
||||
echo '$a->obj: ', "\n"; $a->executeString("dump(PHP.obj);");
|
||||
echo '$a->arr: ', "\n"; $a->executeString("dump(PHP.arr);");
|
||||
echo '$a->argv: ', "\n"; $a->executeString("dump(PHP.argv);");
|
||||
|
||||
var_dump($a->argv);
|
||||
var_dump($a->executeString("test();"));
|
||||
var_dump($a->executeString("test();"));
|
||||
|
||||
$b = new V8Js();
|
||||
|
||||
var_dump($a->phpver, $a->executeString("test();"));
|
||||
|
||||
$b->executeString($JS, "test2.js");
|
||||
var_dump($b->executeString("test();"));
|
||||
var_dump($b->executeString("print('foobar\\n');"));
|
||||
|
||||
// Exception methods
|
||||
|
||||
try {
|
||||
$b->executeString("foobar; foo();", "extest.js");
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e, $e->getJsFileName(), $e->getJsLineNumber(), $e->getJsSourceLine(), $e->getJsTrace());
|
||||
}
|
25
tests/basic.phpt
Normal file
25
tests/basic.phpt
Normal file
@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Simple test
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
len = print('Hello' + ' ' + 'World!' + "\\n");
|
||||
len;
|
||||
EOT;
|
||||
|
||||
$v8 = new V8Js();
|
||||
|
||||
try {
|
||||
var_dump($v8->executeString($JS, 'basic.js'));
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
Hello World!
|
||||
int(13)
|
||||
===EOF===
|
48
tests/callbacks.phpt
Normal file
48
tests/callbacks.phpt
Normal file
@ -0,0 +1,48 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Call JS from PHP
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$a = new V8Js();
|
||||
|
||||
// Should not work with closure
|
||||
$a->test = function ($params) { return (method_exists($params, 'cb1')) ? $params->cb1("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test(function (foo) { return foo + " world"; });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// Test is_a()
|
||||
$a->test = function ($params) { return (is_a($params, 'V8Object')) ? $params->cb1("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// Test is_a()
|
||||
$a->test = function ($params) { return (is_a($params, 'V8Function')) ? $params("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test(function (foo) { return foo + " world"; });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// Should not work with object
|
||||
$a->test = function ($params) { return (is_a($params, 'Closure')) ? $params("hello") : false; };
|
||||
$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
// Works
|
||||
$a->test = function ($params) { return $params->cb1("hello"); };
|
||||
$ret = $a->executeString('PHP.test({ "cb1" : function (foo) { return foo + " world"; } });');
|
||||
var_dump(__LINE__, $ret);
|
||||
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
int(8)
|
||||
bool(false)
|
||||
int(13)
|
||||
string(11) "hello world"
|
||||
int(18)
|
||||
string(11) "hello world"
|
||||
int(23)
|
||||
bool(false)
|
||||
int(28)
|
||||
string(11) "hello world"
|
||||
===EOF===
|
21
tests/closures_basic.phpt
Normal file
21
tests/closures_basic.phpt
Normal file
@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Simple test
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$a = new V8Js();
|
||||
$a->func = function ($arg) { echo "Hello {$arg}, I'm Closure!\n"; };
|
||||
|
||||
try {
|
||||
$a->executeString('print(PHP.func + "\n"); PHP.func("foobar");', "closure_test.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
[object Closure]
|
||||
Hello foobar, I'm Closure!
|
||||
===EOF===
|
30
tests/closures_dynamic.phpt
Normal file
30
tests/closures_dynamic.phpt
Normal file
@ -0,0 +1,30 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Dynamic closure call test
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo
|
||||
{
|
||||
public static function bar($s)
|
||||
{
|
||||
return 'Hello ' . $s;
|
||||
}
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$b = array('Foo', 'bar');
|
||||
$a->func = function ($arg) use ($b) { return call_user_func($b, $arg); };
|
||||
|
||||
try {
|
||||
$a->executeString('print(PHP.func + "\n"); print(PHP.func("world") + "\n");', "closure_test.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
[object Closure]
|
||||
Hello world
|
||||
===EOF===
|
24
tests/construct.phpt
Normal file
24
tests/construct.phpt
Normal file
@ -0,0 +1,24 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Calling construct twice
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
print('Hello' + ' ' + 'World!' + "\\n");
|
||||
EOT;
|
||||
|
||||
$v8 = new V8Js("myObj");
|
||||
$v8->__construct();
|
||||
|
||||
try {
|
||||
$v8->executeString($JS, 'basic.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
Hello World!
|
||||
===EOF===
|
78
tests/context_preserving.phpt
Normal file
78
tests/context_preserving.phpt
Normal file
@ -0,0 +1,78 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : test context preserving
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS_set = <<< EOT
|
||||
var preserved = "ORIGINAL";
|
||||
print("Set variable (" + PHP.ctx + ")\\n");
|
||||
EOT;
|
||||
|
||||
$JS_change = <<< EOT
|
||||
preserved = "CHANGED";
|
||||
print("Change variable (" + PHP.ctx + ")\\n");
|
||||
EOT;
|
||||
|
||||
$JS_read = <<< EOT
|
||||
print("Read variable (" + PHP.ctx + ")\\n");
|
||||
print("Result: " + preserved + "\\n");
|
||||
EOT;
|
||||
|
||||
// First context: Set variable
|
||||
$a = new V8Js();
|
||||
$a->ctx = '#1';
|
||||
|
||||
try {
|
||||
echo '1. ';
|
||||
$a->executeString($JS_set, 'set.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
// Second context: Change variable
|
||||
$b = new V8Js();
|
||||
$b->ctx = '#2';
|
||||
|
||||
try {
|
||||
echo '2. ';
|
||||
$b->executeString($JS_change, 'change.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
// First context: Read variable
|
||||
try {
|
||||
echo '3. ';
|
||||
$a->executeString($JS_read, 'read.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
// First context: Change variable
|
||||
try {
|
||||
echo '4. ';
|
||||
$a->executeString($JS_change, 'change.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
// First context: Read variable again
|
||||
try {
|
||||
echo '5. ';
|
||||
$a->executeString($JS_read, 'read.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
1. Set variable (#1)
|
||||
2. Change variable (#2)
|
||||
3. Read variable (#1)
|
||||
Result: ORIGINAL
|
||||
4. Change variable (#1)
|
||||
5. Read variable (#1)
|
||||
Result: CHANGED
|
||||
===EOF===
|
38
tests/context_separation.phpt
Normal file
38
tests/context_separation.phpt
Normal file
@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : test context separation
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
print(PHP.foo);
|
||||
EOT;
|
||||
|
||||
$a = new V8Js();
|
||||
$a->foo = 'from first.js';
|
||||
|
||||
try {
|
||||
$a->executeString($JS, 'first.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
$b = new V8Js();
|
||||
$b->foo = 'from second.js';
|
||||
|
||||
try {
|
||||
$b->executeString($JS, 'second.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECTF--
|
||||
from first.js
|
||||
from second.js
|
||||
===EOF===
|
68
tests/exception.phpt
Normal file
68
tests/exception.phpt
Normal file
@ -0,0 +1,68 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : V8JsException
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
this_function_does_not_exist();
|
||||
EOT;
|
||||
|
||||
$v8 = new V8Js();
|
||||
|
||||
try {
|
||||
$v8->executeString($JS, 'exception.js');
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECTF--
|
||||
object(V8JsException)#2 (11) {
|
||||
["message":protected]=>
|
||||
string(75) "exception.js:1: ReferenceError: this_function_does_not_exist is not defined"
|
||||
["string":"Exception":private]=>
|
||||
string(0) ""
|
||||
["code":protected]=>
|
||||
int(0)
|
||||
["file":protected]=>
|
||||
string(%d) "%s"
|
||||
["line":protected]=>
|
||||
int(10)
|
||||
["trace":"Exception":private]=>
|
||||
array(1) {
|
||||
[0]=>
|
||||
array(6) {
|
||||
["file"]=>
|
||||
string(%d) "%s"
|
||||
["line"]=>
|
||||
int(10)
|
||||
["function"]=>
|
||||
string(13) "executeString"
|
||||
["class"]=>
|
||||
string(4) "V8Js"
|
||||
["type"]=>
|
||||
string(2) "->"
|
||||
["args"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(31) "this_function_does_not_exist();"
|
||||
[1]=>
|
||||
string(12) "exception.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
["previous":"Exception":private]=>
|
||||
NULL
|
||||
["JsFileName":protected]=>
|
||||
string(12) "exception.js"
|
||||
["JsLineNumber":protected]=>
|
||||
int(1)
|
||||
["JsSourceLine":protected]=>
|
||||
string(31) "this_function_does_not_exist();"
|
||||
["JsTrace":protected]=>
|
||||
string(83) "ReferenceError: this_function_does_not_exist is not defined
|
||||
at exception.js:1:1"
|
||||
}
|
||||
===EOF===
|
37
tests/exception_propagation_1.phpt
Normal file
37
tests/exception_propagation_1.phpt
Normal file
@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Exception propagation test 1
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
private $v8 = NULL;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->v8 = new V8Js();
|
||||
$this->v8->foo = $this;
|
||||
$this->v8->executeString('fooobar', 'throw_0');
|
||||
var_dump($this->v8->getPendingException());
|
||||
$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " caught!\n"); }', 'trycatch1');
|
||||
$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " caught!\n"); }', 'trycatch2');
|
||||
}
|
||||
|
||||
public function bar()
|
||||
{
|
||||
echo "To Bar!\n";
|
||||
$this->v8->executeString('throw new Error();', 'throw_1');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECTF--
|
||||
PHP Exception: throw_0:1: ReferenceError: fooobar is not defined
|
||||
===EOF===
|
101
tests/exception_propagation_2.phpt
Normal file
101
tests/exception_propagation_2.phpt
Normal file
@ -0,0 +1,101 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Exception propagation test 2
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
private $v8 = NULL;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->v8 = new V8Js(null, array(), array(), false);
|
||||
$this->v8->foo = $this;
|
||||
$this->v8->executeString('fooobar', 'throw_0');
|
||||
var_dump($this->v8->getPendingException());
|
||||
$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " caught!\n"); }', 'trycatch1');
|
||||
$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " caught!\n"); }', 'trycatch2');
|
||||
}
|
||||
|
||||
public function bar()
|
||||
{
|
||||
echo "To Bar!\n";
|
||||
$this->v8->executeString('throw new Error();', 'throw_1');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n"; //var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECTF--
|
||||
object(V8JsException)#3 (11) {
|
||||
["message":protected]=>
|
||||
string(49) "throw_0:1: ReferenceError: fooobar is not defined"
|
||||
["string":"Exception":private]=>
|
||||
string(0) ""
|
||||
["code":protected]=>
|
||||
int(0)
|
||||
["file":protected]=>
|
||||
string(%d) "%s"
|
||||
["line":protected]=>
|
||||
int(10)
|
||||
["trace":"Exception":private]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
array(6) {
|
||||
["file"]=>
|
||||
string(%d) "%s"
|
||||
["line"]=>
|
||||
int(10)
|
||||
["function"]=>
|
||||
string(13) "executeString"
|
||||
["class"]=>
|
||||
string(4) "V8Js"
|
||||
["type"]=>
|
||||
string(2) "->"
|
||||
["args"]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(7) "fooobar"
|
||||
[1]=>
|
||||
string(7) "throw_0"
|
||||
}
|
||||
}
|
||||
[1]=>
|
||||
array(6) {
|
||||
["file"]=>
|
||||
string(%d) "%s"
|
||||
["line"]=>
|
||||
int(24)
|
||||
["function"]=>
|
||||
string(11) "__construct"
|
||||
["class"]=>
|
||||
string(3) "Foo"
|
||||
["type"]=>
|
||||
string(2) "->"
|
||||
["args"]=>
|
||||
array(0) {
|
||||
}
|
||||
}
|
||||
}
|
||||
["previous":"Exception":private]=>
|
||||
NULL
|
||||
["JsFileName":protected]=>
|
||||
string(7) "throw_0"
|
||||
["JsLineNumber":protected]=>
|
||||
int(1)
|
||||
["JsSourceLine":protected]=>
|
||||
string(7) "fooobar"
|
||||
["JsTrace":protected]=>
|
||||
string(57) "ReferenceError: fooobar is not defined
|
||||
at throw_0:1:1"
|
||||
}
|
||||
To Bar!
|
||||
Error caught!
|
||||
PHP Exception: throw_0:1: ReferenceError: fooobar is not defined
|
||||
===EOF===
|
38
tests/exception_propagation_3.phpt
Normal file
38
tests/exception_propagation_3.phpt
Normal file
@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Exception propagation test 3
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
private $v8 = NULL;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->v8 = new V8Js(null, array(), array(), false);
|
||||
$this->v8->foo = $this;
|
||||
$this->v8->executeString('function foobar() { throw new SyntaxError(); }', 'throw_1');
|
||||
$this->v8->executeString('try { foobar(); } catch (e) { print(e + " caught in JS!\n"); }', 'trycatch1');
|
||||
$this->v8->executeString('try { PHP.foo.bar(); } catch (e) { print(e + " caught via PHP callback!\n"); }', 'trycatch2');
|
||||
}
|
||||
|
||||
public function bar()
|
||||
{
|
||||
echo "To Bar!\n";
|
||||
$this->v8->executeString('throw new Error();', 'throw_2');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$foo = new Foo();
|
||||
} catch (V8JsException $e) {
|
||||
echo "PHP Exception: ", $e->getMessage(), "\n";
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
SyntaxError caught in JS!
|
||||
To Bar!
|
||||
Error caught via PHP callback!
|
||||
===EOF===
|
33
tests/execute_flags.phpt
Normal file
33
tests/execute_flags.phpt
Normal file
@ -0,0 +1,33 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Forcing to arrays
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$js = <<<'EOT'
|
||||
var a = { 'hello' : 'world' }; a;
|
||||
EOT;
|
||||
|
||||
$v8 = new V8Js();
|
||||
|
||||
try {
|
||||
var_dump($v8->executeString($js, 'assoc_no_flags.js'));
|
||||
echo "---\n";
|
||||
var_dump($v8->executeString($js, 'assoc_force_to_array.js', V8Js::FLAG_FORCE_ARRAY));
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
object(V8Object)#2 (1) {
|
||||
["hello"]=>
|
||||
string(5) "world"
|
||||
}
|
||||
---
|
||||
array(1) {
|
||||
["hello"]=>
|
||||
string(5) "world"
|
||||
}
|
||||
===EOF===
|
75
tests/execute_flags_args.phpt
Normal file
75
tests/execute_flags_args.phpt
Normal file
@ -0,0 +1,75 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Forcing to arrays
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$js = <<<'EOT'
|
||||
String.prototype.test = function(){ return PHP.test(this, arguments); };
|
||||
"Foobar".test("foo", "bar");
|
||||
EOT;
|
||||
|
||||
$v8 = new V8Js();
|
||||
$v8->test = function ($value) { var_dump(func_get_args()); };
|
||||
|
||||
try {
|
||||
$v8->executeString($js, 'no_flags.js');
|
||||
echo "---\n";
|
||||
$v8->executeString($js, 'force_to_array.js', V8Js::FLAG_FORCE_ARRAY);
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
array(2) {
|
||||
[0]=>
|
||||
object(V8Object)#3 (6) {
|
||||
["0"]=>
|
||||
string(1) "F"
|
||||
["1"]=>
|
||||
string(1) "o"
|
||||
["2"]=>
|
||||
string(1) "o"
|
||||
["3"]=>
|
||||
string(1) "b"
|
||||
["4"]=>
|
||||
string(1) "a"
|
||||
["5"]=>
|
||||
string(1) "r"
|
||||
}
|
||||
[1]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["0"]=>
|
||||
string(3) "foo"
|
||||
["1"]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
}
|
||||
---
|
||||
array(2) {
|
||||
[0]=>
|
||||
array(6) {
|
||||
[0]=>
|
||||
string(1) "F"
|
||||
[1]=>
|
||||
string(1) "o"
|
||||
[2]=>
|
||||
string(1) "o"
|
||||
[3]=>
|
||||
string(1) "b"
|
||||
[4]=>
|
||||
string(1) "a"
|
||||
[5]=>
|
||||
string(1) "r"
|
||||
}
|
||||
[1]=>
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
}
|
||||
===EOF===
|
35
tests/extensions_basic.phpt
Normal file
35
tests/extensions_basic.phpt
Normal file
@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
Test V8::registerExtension() : Basic extension registering
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
V8Js::registerExtension('a', 'print("world!\n");', array('b'));
|
||||
V8Js::registerExtension('b', 'print("Hello ");');
|
||||
|
||||
var_dump(V8JS::getExtensions());
|
||||
|
||||
$a = new V8Js('myobj', array(), array('a'));
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
array(2) {
|
||||
["a"]=>
|
||||
array(2) {
|
||||
["auto_enable"]=>
|
||||
bool(false)
|
||||
["deps"]=>
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(1) "b"
|
||||
}
|
||||
}
|
||||
["b"]=>
|
||||
array(1) {
|
||||
["auto_enable"]=>
|
||||
bool(false)
|
||||
}
|
||||
}
|
||||
Hello world!
|
||||
===EOF===
|
39
tests/extensions_circular_dependency.phpt
Normal file
39
tests/extensions_circular_dependency.phpt
Normal file
@ -0,0 +1,39 @@
|
||||
--TEST--
|
||||
Test V8::registerExtension() : Circular dependencies
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
V8Js::registerExtension('a', 'print("A");', array('b'));
|
||||
V8Js::registerExtension('b', 'print("B");', array('a'));
|
||||
|
||||
var_dump(V8JS::getExtensions());
|
||||
|
||||
$a = new V8Js('myobj', array(), array('a'));
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(2) {
|
||||
["a"]=>
|
||||
array(2) {
|
||||
["auto_enable"]=>
|
||||
bool(false)
|
||||
["deps"]=>
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(1) "b"
|
||||
}
|
||||
}
|
||||
["b"]=>
|
||||
array(2) {
|
||||
["auto_enable"]=>
|
||||
bool(false)
|
||||
["deps"]=>
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(1) "a"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Fatal error: v8::Context::New() Circular extension dependency in %s on line 8
|
41
tests/get_accessor.phpt
Normal file
41
tests/get_accessor.phpt
Normal file
@ -0,0 +1,41 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : PHP variables via get accessor
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--INI--
|
||||
display_errors=off
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<<'EOT'
|
||||
print(typeof $foobar + "\n"); // Undefined
|
||||
print(myobj.$foobar + "\n"); // Undefined (in 1st run!)
|
||||
print(myobj.$_SERVER['REQUEST_TIME'] + "\n");
|
||||
myobj.$foobar = 'CHANGED'; // should be read only!
|
||||
print(myobj.$foobar + "\n"); // Undefined (in 1st run!)
|
||||
EOT;
|
||||
|
||||
$a = new V8Js("myobj", array('$_SERVER' => '_SERVER', '$foobar' => 'myfoobar'));
|
||||
$a->executeString($JS, "test1.js");
|
||||
|
||||
$myfoobar = 'myfoobarfromphp';
|
||||
|
||||
$a->executeString($JS, "test2.js");
|
||||
|
||||
// Check that variables do not get in object ..
|
||||
var_dump($a->myfoobar, $a->foobar);
|
||||
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECTF--
|
||||
undefined
|
||||
undefined
|
||||
%d
|
||||
undefined
|
||||
undefined
|
||||
myfoobarfromphp
|
||||
%d
|
||||
myfoobarfromphp
|
||||
NULL
|
||||
NULL
|
||||
===EOF===
|
50
tests/object.phpt
Normal file
50
tests/object.phpt
Normal file
@ -0,0 +1,50 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Object passed from PHP
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
function dump(a)
|
||||
{
|
||||
for (var i in a) {
|
||||
var val = a[i];
|
||||
print(i + ' => ' + val + "\\n");
|
||||
}
|
||||
}
|
||||
function test()
|
||||
{
|
||||
dump(PHP.myobj);
|
||||
PHP.myobj.foo = 'CHANGED';
|
||||
PHP.myobj.mytest();
|
||||
}
|
||||
test();
|
||||
print(PHP.myobj.foo + "\\n");
|
||||
EOT;
|
||||
|
||||
// Test class
|
||||
class Testing
|
||||
{
|
||||
public $foo = 'ORIGINAL';
|
||||
private $my_private = 'arf'; // Should not show in JS side
|
||||
protected $my_protected = 'argh'; // Should not show in JS side
|
||||
|
||||
function mytest() { echo 'Here be monsters..', "\n"; }
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->myobj = new Testing();
|
||||
$a->executeString($JS, "test.js");
|
||||
|
||||
// Check that variable has not been modified
|
||||
var_dump($a->myobj->foo);
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
mytest => function () { [native code] }
|
||||
foo => ORIGINAL
|
||||
Here be monsters..
|
||||
ORIGINAL
|
||||
string(8) "ORIGINAL"
|
||||
===EOF===
|
4
tests/object_method_call.diff
Normal file
4
tests/object_method_call.diff
Normal file
@ -0,0 +1,4 @@
|
||||
037+ Mon Sep 08 1975 09:00:00 GMT+0000 (UTC)
|
||||
037- Mon Sep 08 1975 09:00:00 GMT+0200 (EET)
|
||||
046+ string(6) "+00:00"
|
||||
046- string(6) "+02:00"
|
85
tests/object_method_call.exp
Normal file
85
tests/object_method_call.exp
Normal file
@ -0,0 +1,85 @@
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
bool(true)
|
||||
[1]=>
|
||||
bool(false)
|
||||
[2]=>
|
||||
int(1234567890)
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
float(3.14)
|
||||
[1]=>
|
||||
int(42)
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
test4.js:1: TypeError: Testing::mytest() expects at least 2 parameters, 0 given
|
||||
array(4) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
[2]=>
|
||||
string(4) "arg3"
|
||||
[3]=>
|
||||
string(9) "extra_arg"
|
||||
}
|
||||
|
||||
TEST: Javascript Date -> PHP DateTime
|
||||
======================================
|
||||
Mon Sep 08 1975 09:00:00 GMT+0200 (EET)
|
||||
array(2) {
|
||||
[0]=>
|
||||
object(DateTime)#4 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+02:00"
|
||||
}
|
||||
[1]=>
|
||||
string(3) "foo"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
[1]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
[2]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
[2]=>
|
||||
object(V8Object)#5 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
}
|
||||
}
|
||||
===EOF===
|
174
tests/object_method_call.log
Normal file
174
tests/object_method_call.log
Normal file
@ -0,0 +1,174 @@
|
||||
|
||||
---- EXPECTED OUTPUT
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
bool(true)
|
||||
[1]=>
|
||||
bool(false)
|
||||
[2]=>
|
||||
int(1234567890)
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
float(3.14)
|
||||
[1]=>
|
||||
int(42)
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
test4.js:1: TypeError: Testing::mytest() expects at least 2 parameters, 0 given
|
||||
array(4) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
[2]=>
|
||||
string(4) "arg3"
|
||||
[3]=>
|
||||
string(9) "extra_arg"
|
||||
}
|
||||
|
||||
TEST: Javascript Date -> PHP DateTime
|
||||
======================================
|
||||
Mon Sep 08 1975 09:00:00 GMT+0200 (EET)
|
||||
array(2) {
|
||||
[0]=>
|
||||
object(DateTime)#4 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+02:00"
|
||||
}
|
||||
[1]=>
|
||||
string(3) "foo"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
[1]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
[2]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
[2]=>
|
||||
object(V8Object)#5 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
}
|
||||
}
|
||||
===EOF===
|
||||
---- ACTUAL OUTPUT
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
bool(true)
|
||||
[1]=>
|
||||
bool(false)
|
||||
[2]=>
|
||||
int(1234567890)
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
float(3.14)
|
||||
[1]=>
|
||||
int(42)
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
test4.js:1: TypeError: Testing::mytest() expects at least 2 parameters, 0 given
|
||||
array(4) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
[2]=>
|
||||
string(4) "arg3"
|
||||
[3]=>
|
||||
string(9) "extra_arg"
|
||||
}
|
||||
|
||||
TEST: Javascript Date -> PHP DateTime
|
||||
======================================
|
||||
Mon Sep 08 1975 09:00:00 GMT+0000 (UTC)
|
||||
array(2) {
|
||||
[0]=>
|
||||
object(DateTime)#4 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+00:00"
|
||||
}
|
||||
[1]=>
|
||||
string(3) "foo"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
[1]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
[2]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
[2]=>
|
||||
object(V8Object)#5 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
}
|
||||
}
|
||||
===EOF===
|
||||
---- FAILED
|
85
tests/object_method_call.out
Normal file
85
tests/object_method_call.out
Normal file
@ -0,0 +1,85 @@
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
bool(true)
|
||||
[1]=>
|
||||
bool(false)
|
||||
[2]=>
|
||||
int(1234567890)
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
float(3.14)
|
||||
[1]=>
|
||||
int(42)
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
test4.js:1: TypeError: Testing::mytest() expects at least 2 parameters, 0 given
|
||||
array(4) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
[2]=>
|
||||
string(4) "arg3"
|
||||
[3]=>
|
||||
string(9) "extra_arg"
|
||||
}
|
||||
|
||||
TEST: Javascript Date -> PHP DateTime
|
||||
======================================
|
||||
Mon Sep 08 1975 09:00:00 GMT+0000 (UTC)
|
||||
array(2) {
|
||||
[0]=>
|
||||
object(DateTime)#4 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+00:00"
|
||||
}
|
||||
[1]=>
|
||||
string(3) "foo"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
[1]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
[2]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
[2]=>
|
||||
object(V8Object)#5 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
}
|
||||
}
|
||||
===EOF===
|
53
tests/object_method_call.php
Normal file
53
tests/object_method_call.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
// Test class
|
||||
class Testing
|
||||
{
|
||||
public $foo = 'ORIGINAL';
|
||||
private $my_private = 'arf'; // Should not show in JS side
|
||||
protected $my_protected = 'argh'; // Should not show in JS side
|
||||
|
||||
function mytest($a, $b, $c = NULL)
|
||||
{
|
||||
var_dump(func_get_args());
|
||||
}
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->myobj = new Testing();
|
||||
|
||||
$a->executeString("PHP.myobj.mytest('arg1', 'arg2');", "test1.js");
|
||||
$a->executeString("PHP.myobj.mytest(true, false, 1234567890);", "test2.js");
|
||||
$a->executeString("PHP.myobj.mytest(3.14, 42, null);", "test3.js");
|
||||
|
||||
// Invalid parameters
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest();", "test4.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest('arg1', 'arg2', 'arg3', 'extra_arg');", "test5.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
date_default_timezone_set("UTC");
|
||||
echo "\nTEST: Javascript Date -> PHP DateTime\n";
|
||||
echo "======================================\n";
|
||||
$a->executeString("date = new Date('September 8, 1975 09:00:00'); print(date + '\\n'); PHP.myobj.mytest(date, 'foo');", "test6.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
// Array / Object
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest(PHP.myobj, new Array(1,2,3), new Array('foo', 'bar', PHP.myobj));", "test7.js");
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
?>
|
||||
===EOF===
|
144
tests/object_method_call.phpt
Normal file
144
tests/object_method_call.phpt
Normal file
@ -0,0 +1,144 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Calling methods of object passed from PHP
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// Test class
|
||||
class Testing
|
||||
{
|
||||
public $foo = 'ORIGINAL';
|
||||
private $my_private = 'arf'; // Should not show in JS side
|
||||
protected $my_protected = 'argh'; // Should not show in JS side
|
||||
|
||||
function mytest($a, $b, $c = NULL)
|
||||
{
|
||||
var_dump(func_get_args());
|
||||
}
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->myobj = new Testing();
|
||||
|
||||
$a->executeString("PHP.myobj.mytest('arg1', 'arg2');", "test1.js");
|
||||
$a->executeString("PHP.myobj.mytest(true, false, 1234567890);", "test2.js");
|
||||
$a->executeString("PHP.myobj.mytest(3.14, 42, null);", "test3.js");
|
||||
|
||||
// Invalid parameters
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest();", "test4.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest('arg1', 'arg2', 'arg3', 'extra_arg');", "test5.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
date_default_timezone_set("UTC");
|
||||
echo "\nTEST: Javascript Date -> PHP DateTime\n";
|
||||
echo "======================================\n";
|
||||
$a->executeString("date = new Date('September 8, 1975 09:00:00'); print(date + '\\n'); PHP.myobj.mytest(date, 'foo');", "test6.js");
|
||||
} catch (V8JsException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
// Array / Object
|
||||
try {
|
||||
$a->executeString("PHP.myobj.mytest(PHP.myobj, new Array(1,2,3), new Array('foo', 'bar', PHP.myobj));", "test7.js");
|
||||
} catch (V8JsException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
bool(true)
|
||||
[1]=>
|
||||
bool(false)
|
||||
[2]=>
|
||||
int(1234567890)
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
float(3.14)
|
||||
[1]=>
|
||||
int(42)
|
||||
[2]=>
|
||||
NULL
|
||||
}
|
||||
test4.js:1: TypeError: Testing::mytest() expects at least 2 parameters, 0 given
|
||||
array(4) {
|
||||
[0]=>
|
||||
string(4) "arg1"
|
||||
[1]=>
|
||||
string(4) "arg2"
|
||||
[2]=>
|
||||
string(4) "arg3"
|
||||
[3]=>
|
||||
string(9) "extra_arg"
|
||||
}
|
||||
|
||||
TEST: Javascript Date -> PHP DateTime
|
||||
======================================
|
||||
Mon Sep 08 1975 09:00:00 GMT+0200 (EET)
|
||||
array(2) {
|
||||
[0]=>
|
||||
object(DateTime)#4 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+02:00"
|
||||
}
|
||||
[1]=>
|
||||
string(3) "foo"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
[1]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
[2]=>
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
[2]=>
|
||||
object(V8Object)#5 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#6 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
}
|
||||
}
|
||||
===EOF===
|
82
tests/object_prototype.phpt
Normal file
82
tests/object_prototype.phpt
Normal file
@ -0,0 +1,82 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Prototype with PHP callbacks
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
$js = <<<'EOT'
|
||||
|
||||
String.prototype.test = function(){ return PHP.test(this.toString(), arguments); };
|
||||
String.prototype.test_two = function(){ return PHP.test_two.func(this.toString(), arguments); };
|
||||
Array.prototype.test = function(){ return PHP.test(this.toString(), arguments); };
|
||||
Array.prototype.test_two = function(){ return PHP.test_two.func(this.toString(), arguments); };
|
||||
|
||||
"Foobar".test("foo", "bar");
|
||||
"Foobar".test_two("foo", "bar");
|
||||
|
||||
["a","b","c"].test("foo", "bar");
|
||||
["a","b","c"].test_two("foo", "bar");
|
||||
|
||||
EOT;
|
||||
|
||||
class A
|
||||
{
|
||||
public function __call($name, $args)
|
||||
{
|
||||
var_dump($args);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->test = function ($value) { var_dump(func_get_args()); return 'HELLO: ' . md5($value); };
|
||||
$a->test_two = new A();
|
||||
$a->executeString($js, 'foo');
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(6) "Foobar"
|
||||
[1]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["0"]=>
|
||||
string(3) "foo"
|
||||
["1"]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(6) "Foobar"
|
||||
[1]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["0"]=>
|
||||
string(3) "foo"
|
||||
["1"]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(5) "a,b,c"
|
||||
[1]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["0"]=>
|
||||
string(3) "foo"
|
||||
["1"]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(5) "a,b,c"
|
||||
[1]=>
|
||||
object(V8Object)#4 (2) {
|
||||
["0"]=>
|
||||
string(3) "foo"
|
||||
["1"]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
}
|
||||
===EOF===
|
22
tests/object_reuse.phpt
Normal file
22
tests/object_reuse.phpt
Normal file
@ -0,0 +1,22 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Test PHP object reusage
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$v8 = new V8Js();
|
||||
|
||||
// Reusing should work..
|
||||
|
||||
$v8 = new V8Js();
|
||||
$foo = array('foo' => 'Destructor did not mess anything!');
|
||||
$v8->foobar = $foo;
|
||||
|
||||
$v8->executeString("print(PHP.foobar['foo'] + '\\n');", "dtor.js");
|
||||
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
Destructor did not mess anything!
|
||||
===EOF===
|
2
tests/return_value.diff
Normal file
2
tests/return_value.diff
Normal file
@ -0,0 +1,2 @@
|
||||
035+ string(6) "+00:00"
|
||||
035- string(6) "+02:00"
|
42
tests/return_value.exp
Normal file
42
tests/return_value.exp
Normal file
@ -0,0 +1,42 @@
|
||||
NULL
|
||||
object(V8Object)#3 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#4 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
object(DateTime)#3 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+02:00"
|
||||
}
|
||||
int(1234567890)
|
||||
float(123.456789)
|
||||
string(11) "some string"
|
||||
bool(true)
|
||||
bool(false)
|
||||
===EOF===
|
88
tests/return_value.log
Normal file
88
tests/return_value.log
Normal file
@ -0,0 +1,88 @@
|
||||
|
||||
---- EXPECTED OUTPUT
|
||||
NULL
|
||||
object(V8Object)#3 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#4 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
object(DateTime)#3 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+02:00"
|
||||
}
|
||||
int(1234567890)
|
||||
float(123.456789)
|
||||
string(11) "some string"
|
||||
bool(true)
|
||||
bool(false)
|
||||
===EOF===
|
||||
---- ACTUAL OUTPUT
|
||||
NULL
|
||||
object(V8Object)#3 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#4 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
object(DateTime)#3 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+00:00"
|
||||
}
|
||||
int(1234567890)
|
||||
float(123.456789)
|
||||
string(11) "some string"
|
||||
bool(true)
|
||||
bool(false)
|
||||
===EOF===
|
||||
---- FAILED
|
42
tests/return_value.out
Normal file
42
tests/return_value.out
Normal file
@ -0,0 +1,42 @@
|
||||
NULL
|
||||
object(V8Object)#3 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#4 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
object(DateTime)#3 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+00:00"
|
||||
}
|
||||
int(1234567890)
|
||||
float(123.456789)
|
||||
string(11) "some string"
|
||||
bool(true)
|
||||
bool(false)
|
||||
===EOF===
|
34
tests/return_value.php
Normal file
34
tests/return_value.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
function test(passed)
|
||||
{
|
||||
return passed;
|
||||
}
|
||||
EOT;
|
||||
|
||||
// Test class
|
||||
class Testing
|
||||
{
|
||||
public $foo = 'ORIGINAL';
|
||||
private $my_private = 'arf'; // Should not show in JS side
|
||||
protected $my_protected = 'argh'; // Should not show in JS side
|
||||
|
||||
function mytest() { echo 'Here be monsters..', "\n"; }
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->myobj = new Testing();
|
||||
var_dump($a->executeString($JS, "test.js"));
|
||||
var_dump($a->executeString("test(PHP.myobj);", "test1.js"));
|
||||
var_dump($a->executeString("test(new Array(1,2,3));", "test2.js"));
|
||||
var_dump($a->executeString("test(new Array('foo', 'bar'));", "test3.js"));
|
||||
var_dump($a->executeString("test(new Array('foo', 'bar'));", "test3.js"));
|
||||
var_dump($a->executeString("test(new Date('September 8, 1975 09:00:00'));", "test4.js"));
|
||||
var_dump($a->executeString("test(1234567890);", "test5.js"));
|
||||
var_dump($a->executeString("test(123.456789);", "test6.js"));
|
||||
var_dump($a->executeString("test('some string');", "test7.js"));
|
||||
var_dump($a->executeString("test(true);", "test8.js"));
|
||||
var_dump($a->executeString("test(false);", "test9.js"));
|
||||
?>
|
||||
===EOF===
|
82
tests/return_value.phpt
Normal file
82
tests/return_value.phpt
Normal file
@ -0,0 +1,82 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : Return values
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
function test(passed)
|
||||
{
|
||||
return passed;
|
||||
}
|
||||
EOT;
|
||||
|
||||
// Test class
|
||||
class Testing
|
||||
{
|
||||
public $foo = 'ORIGINAL';
|
||||
private $my_private = 'arf'; // Should not show in JS side
|
||||
protected $my_protected = 'argh'; // Should not show in JS side
|
||||
|
||||
function mytest() { echo 'Here be monsters..', "\n"; }
|
||||
}
|
||||
|
||||
$a = new V8Js();
|
||||
$a->myobj = new Testing();
|
||||
var_dump($a->executeString($JS, "test.js"));
|
||||
var_dump($a->executeString("test(PHP.myobj);", "test1.js"));
|
||||
var_dump($a->executeString("test(new Array(1,2,3));", "test2.js"));
|
||||
var_dump($a->executeString("test(new Array('foo', 'bar'));", "test3.js"));
|
||||
var_dump($a->executeString("test(new Array('foo', 'bar'));", "test3.js"));
|
||||
var_dump($a->executeString("test(new Date('September 8, 1975 09:00:00'));", "test4.js"));
|
||||
var_dump($a->executeString("test(1234567890);", "test5.js"));
|
||||
var_dump($a->executeString("test(123.456789);", "test6.js"));
|
||||
var_dump($a->executeString("test('some string');", "test7.js"));
|
||||
var_dump($a->executeString("test(true);", "test8.js"));
|
||||
var_dump($a->executeString("test(false);", "test9.js"));
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
NULL
|
||||
object(V8Object)#3 (2) {
|
||||
["mytest"]=>
|
||||
object(V8Function)#4 (0) {
|
||||
}
|
||||
["foo"]=>
|
||||
string(8) "ORIGINAL"
|
||||
}
|
||||
array(3) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
[2]=>
|
||||
int(3)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(3) "foo"
|
||||
[1]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
object(DateTime)#3 (3) {
|
||||
["date"]=>
|
||||
string(19) "1975-09-08 09:00:00"
|
||||
["timezone_type"]=>
|
||||
int(1)
|
||||
["timezone"]=>
|
||||
string(6) "+02:00"
|
||||
}
|
||||
int(1234567890)
|
||||
float(123.456789)
|
||||
string(11) "some string"
|
||||
bool(true)
|
||||
bool(false)
|
||||
===EOF===
|
5
tests/skipif.inc
Normal file
5
tests/skipif.inc
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
if (!extension_loaded('v8js')) {
|
||||
die("skip");
|
||||
}
|
58
tests/variable_passing.phpt
Normal file
58
tests/variable_passing.phpt
Normal file
@ -0,0 +1,58 @@
|
||||
--TEST--
|
||||
Test V8::executeString() : simple variables passed from PHP
|
||||
--SKIPIF--
|
||||
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$JS = <<< EOT
|
||||
function dump(a)
|
||||
{
|
||||
for (var i in a) {
|
||||
var val = a[i];
|
||||
print(i + ' => ' + val + "\\n");
|
||||
}
|
||||
}
|
||||
function test()
|
||||
{
|
||||
var a = 'From PHP: ' + PHP.somevar;
|
||||
PHP.somevar = 'changed in JS!'; // Should not change..
|
||||
|
||||
dump(PHP.myarray);
|
||||
|
||||
return a;
|
||||
}
|
||||
print(test() + "\\n");
|
||||
print(PHP.myinteger + "\\n");
|
||||
print(PHP.myfloat + "\\n");
|
||||
EOT;
|
||||
|
||||
$a = new V8Js();
|
||||
$a->somevar = "From PHP with love!";
|
||||
$a->myinteger = 123;
|
||||
$a->myfloat = 3.14;
|
||||
$a->_SERVER = $_SERVER;
|
||||
$a->GLOBALS = $GLOBALS;
|
||||
$a->myarray = array(
|
||||
'a' => 'value for key A',
|
||||
'b' => 'value for key B',
|
||||
'c' => 'value for key C',
|
||||
'd' => 'value for key D',
|
||||
);
|
||||
|
||||
$a->executeString($JS, "test.js");
|
||||
|
||||
// Check that variable has not been modified
|
||||
var_dump($a->somevar);
|
||||
?>
|
||||
===EOF===
|
||||
--EXPECT--
|
||||
a => value for key A
|
||||
b => value for key B
|
||||
c => value for key C
|
||||
d => value for key D
|
||||
From PHP: From PHP with love!
|
||||
123
|
||||
3.14
|
||||
string(19) "From PHP with love!"
|
||||
===EOF===
|
595
v8js_convert.cc
Normal file
595
v8js_convert.cc
Normal file
@ -0,0 +1,595 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2010 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id: v8js_convert.cc 306926 2010-12-31 14:15:54Z felipe $ */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include "php.h"
|
||||
#include "ext/date/php_date.h"
|
||||
#include "zend_interfaces.h"
|
||||
#include "zend_closures.h"
|
||||
}
|
||||
|
||||
#include "php_v8js_macros.h"
|
||||
#include <v8.h>
|
||||
|
||||
/* Callback for PHP methods and functions */
|
||||
static v8::Handle<v8::Value> php_v8js_php_callback(const v8::Arguments &args) /* {{{ */
|
||||
{
|
||||
v8::Handle<v8::Value> return_value;
|
||||
zval *value = reinterpret_cast<zval *>(args.This()->GetPointerFromInternalField(0));
|
||||
zend_function *method_ptr;
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
zval fname, *retval_ptr = NULL, **argv = NULL;
|
||||
TSRMLS_FETCH();
|
||||
zend_class_entry *ce = Z_OBJCE_P(value);
|
||||
zend_uint argc = args.Length(), min_num_args = 0, max_num_args = 0;
|
||||
char *error;
|
||||
int error_len, i, flags = V8JS_FLAG_NONE;
|
||||
|
||||
/* Set method_ptr from v8::External or fetch the closure invoker */
|
||||
if (!args.Data().IsEmpty() && args.Data()->IsExternal()) {
|
||||
method_ptr = static_cast<zend_function *>(v8::External::Unwrap(args.Data()));
|
||||
} else {
|
||||
method_ptr = zend_get_closure_invoke_method(value TSRMLS_CC);
|
||||
}
|
||||
|
||||
/* Set parameter limits */
|
||||
min_num_args = method_ptr->common.required_num_args;
|
||||
max_num_args = method_ptr->common.num_args;
|
||||
|
||||
/* Function name to call */
|
||||
ZVAL_STRING(&fname, method_ptr->common.function_name, 0);
|
||||
|
||||
/* zend_fcall_info */
|
||||
fci.size = sizeof(fci);
|
||||
fci.function_table = &ce->function_table;
|
||||
fci.function_name = &fname;
|
||||
fci.symbol_table = NULL;
|
||||
fci.object_ptr = value;
|
||||
fci.retval_ptr_ptr = &retval_ptr;
|
||||
fci.param_count = 0;
|
||||
|
||||
/* Check for passed vs required number of arguments */
|
||||
if (argc < min_num_args)
|
||||
{
|
||||
error_len = spprintf(&error, 0,
|
||||
"%s::%s() expects %s %d parameter%s, %d given",
|
||||
ce->name,
|
||||
method_ptr->common.function_name,
|
||||
min_num_args == max_num_args ? "exactly" : argc < min_num_args ? "at least" : "at most",
|
||||
argc < min_num_args ? min_num_args : max_num_args,
|
||||
(argc < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s",
|
||||
argc);
|
||||
|
||||
return_value = V8JS_THROW(TypeError, error, error_len);
|
||||
if (ce == zend_ce_closure) {
|
||||
efree(method_ptr->internal_function.function_name);
|
||||
efree(method_ptr);
|
||||
}
|
||||
efree(error);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/* Convert parameters passed from V8 */
|
||||
if (argc) {
|
||||
flags = V8JS_GLOBAL_GET_FLAGS();
|
||||
fci.params = (zval ***) safe_emalloc(argc, sizeof(zval **), 0);
|
||||
argv = (zval **) safe_emalloc(argc, sizeof(zval *), 0);
|
||||
for (i = 0; i < argc; i++) {
|
||||
MAKE_STD_ZVAL(argv[i]);
|
||||
if (v8js_to_zval(args[i], argv[i], flags TSRMLS_CC) == FAILURE) {
|
||||
fci.param_count++;
|
||||
error_len = spprintf(&error, 0, "converting parameter #%d passed to %s() failed", i + 1, method_ptr->common.function_name);
|
||||
return_value = V8JS_THROW(Error, error, error_len);
|
||||
efree(error);
|
||||
goto failure;
|
||||
}
|
||||
fci.params[fci.param_count++] = &argv[i];
|
||||
}
|
||||
} else {
|
||||
fci.params = NULL;
|
||||
}
|
||||
fci.no_separation = 1;
|
||||
|
||||
/* zend_fcall_info_cache */
|
||||
fcc.initialized = 1;
|
||||
fcc.function_handler = method_ptr;
|
||||
fcc.calling_scope = ce;
|
||||
fcc.called_scope = ce;
|
||||
fcc.object_ptr = value;
|
||||
|
||||
/* Call the method */
|
||||
zend_call_function(&fci, &fcc TSRMLS_CC);
|
||||
|
||||
failure:
|
||||
/* Cleanup */
|
||||
if (argc) {
|
||||
for (i = 0; i < fci.param_count; i++) {
|
||||
zval_ptr_dtor(&argv[i]);
|
||||
}
|
||||
efree(argv);
|
||||
efree(fci.params);
|
||||
}
|
||||
|
||||
if (retval_ptr != NULL) {
|
||||
return_value = zval_to_v8js(retval_ptr TSRMLS_CC);
|
||||
zval_ptr_dtor(&retval_ptr);
|
||||
} else {
|
||||
return_value = V8JS_NULL;
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int _php_v8js_is_assoc_array(HashTable *myht TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
int i;
|
||||
char *key;
|
||||
ulong index, idx = 0;
|
||||
uint key_len;
|
||||
HashPosition pos;
|
||||
|
||||
zend_hash_internal_pointer_reset_ex(myht, &pos);
|
||||
for (;; zend_hash_move_forward_ex(myht, &pos)) {
|
||||
i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
|
||||
if (i == HASH_KEY_NON_EXISTANT)
|
||||
break;
|
||||
if (i == HASH_KEY_IS_STRING || index != idx) {
|
||||
return 1;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static v8::Handle<v8::Value> php_v8js_property_caller(const v8::Arguments &args) /* {{{ */
|
||||
{
|
||||
v8::Local<v8::Object> self = args.Holder();
|
||||
v8::Local<v8::String> cname = args.Callee()->GetName()->ToString();
|
||||
v8::Local<v8::Value> value;
|
||||
v8::Local<v8::String> cb_func = v8::Local<v8::String>::Cast(args.Data());
|
||||
|
||||
value = self->GetHiddenValue(cb_func);
|
||||
|
||||
if (!value.IsEmpty() && value->IsFunction())
|
||||
{
|
||||
int argc = args.Length(), i = 0;
|
||||
v8::Local<v8::Value> *argv = new v8::Local<v8::Value>[argc];
|
||||
v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(value);
|
||||
|
||||
if (cb_func->Equals(V8JS_SYM(ZEND_INVOKE_FUNC_NAME))) {
|
||||
for (; i < argc; ++i) {
|
||||
argv[i] = args[i];
|
||||
}
|
||||
value = cb->Call(self, argc, argv);
|
||||
}
|
||||
else /* __call() */
|
||||
{
|
||||
v8::Local<v8::Array> argsarr = v8::Array::New(argc);
|
||||
for (; i < argc; ++i) {
|
||||
argsarr->Set(i, args[i]);
|
||||
}
|
||||
v8::Local<v8::Value> argsv[2] = { cname, argsarr };
|
||||
value = cb->Call(self, 2, argsv);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.IsConstructCall()) {
|
||||
if (!value.IsEmpty() && !value->IsNull()) {
|
||||
return value;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static v8::Handle<v8::Value> php_v8js_property_getter(v8::Local<v8::String> property, const v8::AccessorInfo &info) /* {{{ */
|
||||
{
|
||||
v8::Local<v8::Object> self = info.Holder();
|
||||
v8::Local<v8::Value> value;
|
||||
v8::Local<v8::Function> cb;
|
||||
|
||||
/* Check first if JS object has the named property */
|
||||
value = self->GetRealNamedProperty(property);
|
||||
|
||||
if (!value.IsEmpty()) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/* If __get() is set for PHP object, call it */
|
||||
value = self->GetHiddenValue(V8JS_SYM(ZEND_GET_FUNC_NAME));
|
||||
if (!value.IsEmpty() && value->IsFunction()) {
|
||||
cb = v8::Local<v8::Function>::Cast(value);
|
||||
v8::Local<v8::Value> argv[1] = {property};
|
||||
value = cb->Call(self, 1, argv);
|
||||
}
|
||||
|
||||
/* If __get() does not exist or returns NULL, create new function with callback for __call() */
|
||||
if ((value.IsEmpty() || value->IsNull()) && info.Data()->IsTrue()) {
|
||||
v8::Local<v8::FunctionTemplate> cb_t = v8::FunctionTemplate::New(php_v8js_property_caller, V8JS_SYM(ZEND_CALL_FUNC_NAME));
|
||||
cb = cb_t->GetFunction();
|
||||
cb->SetName(property);
|
||||
return cb;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static v8::Handle<v8::Integer> php_v8js_property_query(v8::Local<v8::String> property, const v8::AccessorInfo &info) /* {{{ */
|
||||
{
|
||||
v8::Local<v8::Object> self = info.Holder();
|
||||
v8::Local<v8::Value> value;
|
||||
|
||||
/* Return early if property is set in JS object */
|
||||
if (self->HasRealNamedProperty(property)) {
|
||||
return V8JS_INT(v8::ReadOnly);
|
||||
}
|
||||
|
||||
value = self->GetHiddenValue(V8JS_SYM(ZEND_ISSET_FUNC_NAME));
|
||||
if (!value.IsEmpty() && value->IsFunction()) {
|
||||
v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(value);
|
||||
v8::Local<v8::Value> argv[1] = {property};
|
||||
value = cb->Call(self, 1, argv);
|
||||
}
|
||||
|
||||
return (!value.IsEmpty() && value->IsTrue()) ? V8JS_INT(v8::ReadOnly) : v8::Local<v8::Integer>();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* These are not defined by Zend */
|
||||
#define ZEND_WAKEUP_FUNC_NAME "__wakeup"
|
||||
#define ZEND_SLEEP_FUNC_NAME "__sleep"
|
||||
#define ZEND_SET_STATE_FUNC_NAME "__set_state"
|
||||
|
||||
#define IS_MAGIC_FUNC(mname) \
|
||||
((key_len == sizeof(mname)) && \
|
||||
!strncasecmp(key, mname, key_len - 1))
|
||||
|
||||
#define PHP_V8JS_CALLBACK(mptr) \
|
||||
v8::FunctionTemplate::New(php_v8js_php_callback, v8::External::New(mptr))->GetFunction()
|
||||
|
||||
static v8::Handle<v8::Value> php_v8js_hash_to_jsobj(zval *value TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
v8::Local<v8::FunctionTemplate> new_tpl = v8::FunctionTemplate::New();
|
||||
v8::Local<v8::Object> newobj;
|
||||
int i;
|
||||
char *key = NULL;
|
||||
ulong index;
|
||||
uint key_len;
|
||||
HashTable *myht;
|
||||
HashPosition pos;
|
||||
zend_class_entry *ce = NULL;
|
||||
zend_function *method_ptr, *call_ptr = NULL, *get_ptr = NULL, *invoke_ptr = NULL, *isset_ptr = NULL;
|
||||
|
||||
if (Z_TYPE_P(value) == IS_ARRAY) {
|
||||
myht = HASH_OF(value);
|
||||
} else {
|
||||
myht = Z_OBJPROP_P(value);
|
||||
ce = Z_OBJCE_P(value);
|
||||
}
|
||||
|
||||
/* Prevent recursion */
|
||||
if (myht && myht->nApplyCount > 1) {
|
||||
return V8JS_NULL;
|
||||
}
|
||||
|
||||
/* Object methods */
|
||||
if (ce) {
|
||||
new_tpl->SetClassName(V8JS_STRL(ce->name, ce->name_length));
|
||||
new_tpl->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
|
||||
if (ce == zend_ce_closure) {
|
||||
new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_php_callback);
|
||||
newobj = new_tpl->InstanceTemplate()->NewInstance();
|
||||
} else {
|
||||
zend_hash_internal_pointer_reset_ex(&ce->function_table, &pos);
|
||||
for (;; zend_hash_move_forward_ex(&ce->function_table, &pos)) {
|
||||
if (zend_hash_get_current_key_ex(&ce->function_table, &key, &key_len, &index, 0, &pos) != HASH_KEY_IS_STRING ||
|
||||
zend_hash_get_current_data_ex(&ce->function_table, (void **) &method_ptr, &pos) == FAILURE
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((method_ptr->common.fn_flags & ZEND_ACC_PUBLIC) && /* Allow only public methods */
|
||||
(method_ptr->common.fn_flags & ZEND_ACC_CTOR) == 0 && /* ..and no __construct() */
|
||||
(method_ptr->common.fn_flags & ZEND_ACC_DTOR) == 0 && /* ..or __destruct() */
|
||||
(method_ptr->common.fn_flags & ZEND_ACC_CLONE) == 0 /* ..or __clone() functions */
|
||||
) {
|
||||
/* Override native toString() with __tostring() if it is set in passed object */
|
||||
if (IS_MAGIC_FUNC(ZEND_TOSTRING_FUNC_NAME)) {
|
||||
new_tpl->InstanceTemplate()->Set(V8JS_SYM("toString"), PHP_V8JS_CALLBACK(method_ptr));
|
||||
/* TODO: __set(), __unset() disabled as JS is not allowed to modify the passed PHP object yet.
|
||||
* __sleep(), __wakeup(), __set_state() are always ignored */
|
||||
} else if (
|
||||
IS_MAGIC_FUNC(ZEND_CALLSTATIC_FUNC_NAME)|| /* TODO */
|
||||
IS_MAGIC_FUNC(ZEND_SLEEP_FUNC_NAME) ||
|
||||
IS_MAGIC_FUNC(ZEND_WAKEUP_FUNC_NAME) ||
|
||||
IS_MAGIC_FUNC(ZEND_SET_STATE_FUNC_NAME) ||
|
||||
IS_MAGIC_FUNC(ZEND_SET_FUNC_NAME) ||
|
||||
IS_MAGIC_FUNC(ZEND_UNSET_FUNC_NAME)
|
||||
) {
|
||||
/* Register all magic function as hidden with lowercase name */
|
||||
} else if (IS_MAGIC_FUNC(ZEND_GET_FUNC_NAME)) {
|
||||
get_ptr = method_ptr;
|
||||
} else if (IS_MAGIC_FUNC(ZEND_CALL_FUNC_NAME)) {
|
||||
call_ptr = method_ptr;
|
||||
} else if (IS_MAGIC_FUNC(ZEND_INVOKE_FUNC_NAME)) {
|
||||
invoke_ptr = method_ptr;
|
||||
} else if (IS_MAGIC_FUNC(ZEND_ISSET_FUNC_NAME)) {
|
||||
isset_ptr = method_ptr;
|
||||
} else {
|
||||
new_tpl->InstanceTemplate()->Set(V8JS_STR(method_ptr->common.function_name), PHP_V8JS_CALLBACK(method_ptr), v8::ReadOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Only register getter, etc. when they're set in PHP side */
|
||||
if (call_ptr || get_ptr || isset_ptr)
|
||||
{
|
||||
/* Set __get() handler which acts also as __call() proxy */
|
||||
new_tpl->InstanceTemplate()->SetNamedPropertyHandler(
|
||||
php_v8js_property_getter, /* getter */
|
||||
0, /* setter */
|
||||
isset_ptr ? php_v8js_property_query : 0, /* query */
|
||||
0, /* deleter */
|
||||
0, /* enumerator */
|
||||
V8JS_BOOL(call_ptr ? true : false)
|
||||
);
|
||||
}
|
||||
|
||||
/* __invoke() handler */
|
||||
if (invoke_ptr) {
|
||||
new_tpl->InstanceTemplate()->SetCallAsFunctionHandler(php_v8js_property_caller, V8JS_SYM(ZEND_INVOKE_FUNC_NAME));
|
||||
}
|
||||
|
||||
newobj = new_tpl->InstanceTemplate()->NewInstance();
|
||||
|
||||
if (call_ptr) {
|
||||
newobj->SetHiddenValue(V8JS_SYM(ZEND_CALL_FUNC_NAME), PHP_V8JS_CALLBACK(call_ptr));
|
||||
}
|
||||
if (get_ptr) {
|
||||
newobj->SetHiddenValue(V8JS_SYM(ZEND_GET_FUNC_NAME), PHP_V8JS_CALLBACK(get_ptr));
|
||||
}
|
||||
if (invoke_ptr) {
|
||||
newobj->SetHiddenValue(V8JS_SYM(ZEND_INVOKE_FUNC_NAME), PHP_V8JS_CALLBACK(invoke_ptr));
|
||||
}
|
||||
if (isset_ptr) {
|
||||
newobj->SetHiddenValue(V8JS_SYM(ZEND_ISSET_FUNC_NAME), PHP_V8JS_CALLBACK(isset_ptr));
|
||||
}
|
||||
}
|
||||
newobj->SetPointerInInternalField(0, (void *) value);
|
||||
} else {
|
||||
new_tpl->SetClassName(V8JS_SYM("Array"));
|
||||
newobj = new_tpl->InstanceTemplate()->NewInstance();
|
||||
}
|
||||
|
||||
/* Object properties */
|
||||
i = myht ? zend_hash_num_elements(myht) : 0;
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
zval **data;
|
||||
HashTable *tmp_ht;
|
||||
|
||||
zend_hash_internal_pointer_reset_ex(myht, &pos);
|
||||
for (;; zend_hash_move_forward_ex(myht, &pos)) {
|
||||
i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
|
||||
if (i == HASH_KEY_NON_EXISTANT)
|
||||
break;
|
||||
|
||||
if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS)
|
||||
{
|
||||
tmp_ht = HASH_OF(*data);
|
||||
|
||||
if (tmp_ht) {
|
||||
tmp_ht->nApplyCount++;
|
||||
}
|
||||
|
||||
if (i == HASH_KEY_IS_STRING)
|
||||
{
|
||||
if (key[0] == '\0' && Z_TYPE_P(value) == IS_OBJECT) {
|
||||
/* Skip protected and private members. */
|
||||
if (tmp_ht) {
|
||||
tmp_ht->nApplyCount--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
newobj->Set(V8JS_STRL(key, key_len - 1), zval_to_v8js(*data TSRMLS_CC), v8::ReadOnly);
|
||||
} else {
|
||||
newobj->Set(index, zval_to_v8js(*data TSRMLS_CC));
|
||||
}
|
||||
|
||||
if (tmp_ht) {
|
||||
tmp_ht->nApplyCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return newobj;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static v8::Handle<v8::Value> php_v8js_hash_to_jsarr(zval *value TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
HashTable *myht = HASH_OF(value);
|
||||
int i = myht ? zend_hash_num_elements(myht) : 0;
|
||||
|
||||
/* Return object if dealing with assoc array */
|
||||
if (i > 0 && _php_v8js_is_assoc_array(myht TSRMLS_CC)) {
|
||||
return php_v8js_hash_to_jsobj(value TSRMLS_CC);
|
||||
}
|
||||
|
||||
v8::Local<v8::Array> newarr;
|
||||
|
||||
/* Prevent recursion */
|
||||
if (myht && myht->nApplyCount > 1) {
|
||||
return V8JS_NULL;
|
||||
}
|
||||
|
||||
newarr = v8::Array::New(i);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
zval **data;
|
||||
ulong index = 0;
|
||||
HashTable *tmp_ht;
|
||||
HashPosition pos;
|
||||
|
||||
for (zend_hash_internal_pointer_reset_ex(myht, &pos);
|
||||
SUCCESS == zend_hash_get_current_data_ex(myht, (void **) &data, &pos);
|
||||
zend_hash_move_forward_ex(myht, &pos)
|
||||
) {
|
||||
tmp_ht = HASH_OF(*data);
|
||||
|
||||
if (tmp_ht) {
|
||||
tmp_ht->nApplyCount++;
|
||||
}
|
||||
|
||||
newarr->Set(index++, zval_to_v8js(*data TSRMLS_CC));
|
||||
|
||||
if (tmp_ht) {
|
||||
tmp_ht->nApplyCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return newarr;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
v8::Handle<v8::Value> zval_to_v8js(zval *value TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
v8::Handle<v8::Value> jsValue;
|
||||
|
||||
switch (Z_TYPE_P(value))
|
||||
{
|
||||
case IS_ARRAY:
|
||||
jsValue = php_v8js_hash_to_jsarr(value TSRMLS_CC);
|
||||
break;
|
||||
|
||||
case IS_OBJECT:
|
||||
jsValue = php_v8js_hash_to_jsobj(value TSRMLS_CC);
|
||||
break;
|
||||
|
||||
case IS_STRING:
|
||||
jsValue = V8JS_STRL(Z_STRVAL_P(value), Z_STRLEN_P(value));
|
||||
break;
|
||||
|
||||
case IS_LONG:
|
||||
jsValue = V8JS_INT(Z_LVAL_P(value));
|
||||
break;
|
||||
|
||||
case IS_DOUBLE:
|
||||
jsValue = V8JS_FLOAT(Z_DVAL_P(value));
|
||||
break;
|
||||
|
||||
case IS_BOOL:
|
||||
jsValue = V8JS_BOOL(Z_BVAL_P(value));
|
||||
break;
|
||||
|
||||
default:
|
||||
case IS_NULL:
|
||||
jsValue = V8JS_NULL;
|
||||
break;
|
||||
}
|
||||
return jsValue;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
if (jsValue->IsString())
|
||||
{
|
||||
v8::String::Utf8Value str(jsValue);
|
||||
const char *cstr = ToCString(str);
|
||||
RETVAL_STRING(cstr, 1);
|
||||
}
|
||||
else if (jsValue->IsBoolean())
|
||||
{
|
||||
RETVAL_BOOL(jsValue->Uint32Value());
|
||||
}
|
||||
else if (jsValue->IsInt32() || jsValue->IsUint32())
|
||||
{
|
||||
RETVAL_LONG((long) jsValue->IntegerValue());
|
||||
}
|
||||
else if (jsValue->IsNumber())
|
||||
{
|
||||
RETVAL_DOUBLE(jsValue->NumberValue());
|
||||
}
|
||||
else if (jsValue->IsDate()) /* Return as a PHP DateTime object */
|
||||
{
|
||||
v8::String::Utf8Value str(jsValue);
|
||||
const char *cstr = ToCString(str);
|
||||
zend_class_entry *ce = php_date_get_date_ce();
|
||||
#if PHP_VERSION_ID < 50304
|
||||
zval *param;
|
||||
|
||||
MAKE_STD_ZVAL(param);
|
||||
ZVAL_STRING(param, cstr, 1);
|
||||
|
||||
object_init_ex(return_value, ce TSRMLS_CC);
|
||||
zend_call_method_with_1_params(&return_value, ce, &ce->constructor, "__construct", NULL, param);
|
||||
zval_ptr_dtor(¶m);
|
||||
|
||||
if (EG(exception)) {
|
||||
return FAILURE;
|
||||
}
|
||||
#else
|
||||
php_date_instantiate(ce, return_value TSRMLS_CC);
|
||||
if (!php_date_initialize((php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC), (char *) cstr, strlen(cstr), NULL, NULL, 0 TSRMLS_CC)) {
|
||||
return FAILURE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (jsValue->IsObject())
|
||||
{
|
||||
if ((flags & V8JS_FLAG_FORCE_ARRAY) || jsValue->IsArray()) {
|
||||
array_init(return_value);
|
||||
return php_v8js_v8_get_properties_hash(jsValue, Z_ARRVAL_P(return_value), flags TSRMLS_CC);
|
||||
} else {
|
||||
php_v8js_create_v8(return_value, jsValue, flags TSRMLS_CC);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
else /* types External, RegExp, Undefined and Null are considered NULL */
|
||||
{
|
||||
RETVAL_NULL();
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
183
v8js_methods.cc
Normal file
183
v8js_methods.cc
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2010 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id: v8js_methods.cc 306926 2010-12-31 14:15:54Z felipe $ */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include "php.h"
|
||||
}
|
||||
|
||||
#include "php_v8js_macros.h"
|
||||
#include <v8.h>
|
||||
|
||||
/* global.exit - terminate execution */
|
||||
V8JS_METHOD(exit) /* {{{ */
|
||||
{
|
||||
v8::V8::TerminateExecution();
|
||||
return v8::Undefined();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* global.sleep - sleep for passed seconds */
|
||||
V8JS_METHOD(sleep) /* {{{ */
|
||||
{
|
||||
php_sleep(args[0]->Int32Value());
|
||||
return v8::Undefined();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* global.print - php print() */
|
||||
V8JS_METHOD(print) /* {{{ */
|
||||
{
|
||||
int ret = 0;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
for (int i = 0; i < args.Length(); i++) {
|
||||
v8::String::Utf8Value str(args[i]);
|
||||
const char *cstr = ToCString(str);
|
||||
ret = PHPWRITE(cstr, strlen(cstr));
|
||||
}
|
||||
return V8JS_INT(ret);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void _php_v8js_dumper(v8::Local<v8::Value> var, int level TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
v8::String::Utf8Value str(var->ToDetailString());
|
||||
const char *valstr = ToCString(str);
|
||||
size_t valstr_len = (valstr) ? strlen(valstr) : 0;
|
||||
|
||||
if (level > 1) {
|
||||
php_printf("%*c", (level - 1) * 2, ' ');
|
||||
}
|
||||
|
||||
if (var->IsString())
|
||||
{
|
||||
php_printf("string(%d) \"%s\"\n", valstr_len, valstr);
|
||||
}
|
||||
else if (var->IsBoolean())
|
||||
{
|
||||
php_printf("bool(%s)\n", valstr);
|
||||
}
|
||||
else if (var->IsInt32() || var->IsUint32())
|
||||
{
|
||||
php_printf("int(%s)\n", valstr);
|
||||
}
|
||||
else if (var->IsNumber())
|
||||
{
|
||||
php_printf("float(%s)\n", valstr);
|
||||
}
|
||||
else if (var->IsDate())
|
||||
{
|
||||
php_printf("Date(%s)\n", valstr);
|
||||
}
|
||||
else if (var->IsRegExp())
|
||||
{
|
||||
php_printf("RegExp(%s)\n", valstr);
|
||||
}
|
||||
else if (var->IsArray())
|
||||
{
|
||||
v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(var);
|
||||
uint32_t length = array->Length();
|
||||
|
||||
php_printf("array(%d) {\n", length);
|
||||
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
php_printf("%*c[%d] =>\n", level * 2, ' ', i);
|
||||
_php_v8js_dumper(array->Get(i), level + 1 TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (level > 1) {
|
||||
php_printf("%*c", (level - 1) * 2, ' ');
|
||||
}
|
||||
|
||||
ZEND_PUTS("}\n");
|
||||
}
|
||||
else if (var->IsObject())
|
||||
{
|
||||
v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(var);
|
||||
V8JS_GET_CLASS_NAME(cname, object);
|
||||
|
||||
if (var->IsFunction())
|
||||
{
|
||||
v8::String::Utf8Value csource(object->ToString());
|
||||
php_printf("object(%s)#%d {\n%*c%s\n", ToCString(cname), object->GetIdentityHash(), level * 2 + 2, ' ', ToCString(csource));
|
||||
}
|
||||
else
|
||||
{
|
||||
v8::Local<v8::Array> keys = object->GetPropertyNames();
|
||||
uint32_t length = keys->Length();
|
||||
|
||||
php_printf("object(%s)#%d (%d) {\n", ToCString(cname), object->GetIdentityHash(), length);
|
||||
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
v8::Local<v8::String> key = keys->Get(i)->ToString();
|
||||
v8::String::Utf8Value kname(key);
|
||||
php_printf("%*c[\"%s\"] =>\n", level * 2, ' ', ToCString(kname));
|
||||
_php_v8js_dumper(object->Get(key), level + 1 TSRMLS_CC);
|
||||
}
|
||||
}
|
||||
|
||||
if (level > 1) {
|
||||
php_printf("%*c", (level - 1) * 2, ' ');
|
||||
}
|
||||
|
||||
ZEND_PUTS("}\n");
|
||||
}
|
||||
else /* null, undefined, etc. */
|
||||
{
|
||||
php_printf("<%s>\n", valstr);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* global.var_dump - Dump JS values */
|
||||
V8JS_METHOD(var_dump) /* {{{ */
|
||||
{
|
||||
int i;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
for (int i = 0; i < args.Length(); i++) {
|
||||
_php_v8js_dumper(args[i], 1 TSRMLS_CC);
|
||||
}
|
||||
|
||||
return V8JS_NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate> global) /* {{{ */
|
||||
{
|
||||
global->Set(V8JS_SYM("exit"), v8::FunctionTemplate::New(V8JS_MN(exit)), v8::ReadOnly);
|
||||
global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly);
|
||||
global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);
|
||||
global->Set(V8JS_SYM("var_dump"), v8::FunctionTemplate::New(V8JS_MN(var_dump)), v8::ReadOnly);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
91
v8js_variables.cc
Normal file
91
v8js_variables.cc
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2010 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Jani Taskinen <jani.taskinen@iki.fi> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id: v8js_variables.cc 306926 2010-12-31 14:15:54Z felipe $ */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include "php.h"
|
||||
}
|
||||
|
||||
#include "php_v8js_macros.h"
|
||||
#include <v8.h>
|
||||
|
||||
static v8::Handle<v8::Value> php_v8js_fetch_php_variable(v8::Local<v8::String> name, const v8::AccessorInfo &info) /* {{{ */
|
||||
{
|
||||
v8::String::Utf8Value variable_name(info.Data()->ToString());
|
||||
const char *variable_name_string = ToCString(variable_name);
|
||||
uint variable_name_string_len = strlen(variable_name_string);
|
||||
zval **variable;
|
||||
|
||||
TSRMLS_FETCH();
|
||||
|
||||
zend_is_auto_global(variable_name_string, variable_name_string_len TSRMLS_CC);
|
||||
|
||||
if (zend_hash_find(&EG(symbol_table), variable_name_string, variable_name_string_len + 1, (void **) &variable) == SUCCESS) {
|
||||
return zval_to_v8js(*variable TSRMLS_CC);
|
||||
}
|
||||
return v8::Undefined();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate> php_obj, zval *array TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
char *property_name;
|
||||
uint property_name_len;
|
||||
ulong index;
|
||||
zval **item;
|
||||
|
||||
for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(array));
|
||||
zend_hash_get_current_data(Z_ARRVAL_P(array), (void **) &item) != FAILURE;
|
||||
zend_hash_move_forward(Z_ARRVAL_P(array))
|
||||
) {
|
||||
switch (Z_TYPE_PP(item))
|
||||
{
|
||||
/*
|
||||
case IS_OBJECT:
|
||||
case IS_ARRAY:
|
||||
*/
|
||||
case IS_STRING:
|
||||
break;
|
||||
|
||||
default:
|
||||
continue; /* Ignore invalid values */
|
||||
}
|
||||
|
||||
if (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &property_name, &property_name_len, &index, 0, NULL) != HASH_KEY_IS_STRING) {
|
||||
continue; /* Ignore invalid property names */
|
||||
}
|
||||
|
||||
/* Set the variable fetch callback for given symbol on named property */
|
||||
php_obj->SetAccessor(V8JS_STRL(property_name, property_name_len - 1), php_v8js_fetch_php_variable, NULL, V8JS_STRL(Z_STRVAL_PP(item), Z_STRLEN_PP(item)), v8::PROHIBITS_OVERWRITING, v8::ReadOnly);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
Loading…
Reference in New Issue
Block a user