commit 5534b3252d1036a122c4f105b1b64ee598036529 Author: laruence Date: Tue Dec 13 17:04:44 2011 +0800 initial commit diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..1d43974 --- /dev/null +++ b/CREDITS @@ -0,0 +1,4 @@ +lua +Johannes Schlueter +Marcelo Araujo +Xinchen Hui diff --git a/EXPERIMENTAL b/EXPERIMENTAL new file mode 100644 index 0000000..6c9c970 --- /dev/null +++ b/EXPERIMENTAL @@ -0,0 +1,2 @@ +This extension embeds lua interpreter into PHP. We should emphasize that is +still under development and is completely experimental. diff --git a/README b/README new file mode 100644 index 0000000..2825319 --- /dev/null +++ b/README @@ -0,0 +1,2 @@ +"Lua is a powerful, fast, light-weight, embeddable scripting language." +This extension embeds the lua interpreter and offers an OO-API to lua variables and functions. diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..6194583 --- /dev/null +++ b/config.m4 @@ -0,0 +1,48 @@ +dnl $Id: config.m4 315592 2011-08-27 00:58:41Z johannes $ +PHP_ARG_WITH(lua, for lua support, +[ --with-lua=[DIR] Include php lua support]) + +if test "$PHP_LUA" != "no"; then + if test -r $PHP_LUA/include/lua.h; then + LUA_DIR=$PHP_LUA + else + AC_MSG_CHECKING(for lua in default path) + for i in /usr/local /usr; do + if test -r $i/include/lua/include.h; then + LUA_DIR=$i + AC_MSG_RESULT(found in $i) + break + fi + done + fi + + if test -z "$LUA_DIR"; then + AC_MSG_RESULT(not found) + AC_MSG_ERROR(Please reinstall the lua distribution - lua.h should be in /include/) + fi + + LUA_LIB_NAME=lua + + if test -r $PHP_LUA/$PHP_LIBDIR/lib${LUA_LIB_NAME}.a; then + LUA_LIB_DIR=$PHP_LUA/$PHP_LIBDIR + else + AC_MSG_CHECKING(for lua library in default path) + for i in /usr/lib /usr/lib64; do + if test -r $i/$PHP_LIBDIR/${LUA_LIB_DIR}.a; then + LUA_LIB_DIR=$i + AC_MSG_RESULT(found in $i) + break + fi + done + fi + + if test -z "$LUA_LIB_DIR"; then + AC_MSG_RESULT(not found) + AC_MSG_ERROR(Please reinstall the lua distribution - lua library should be in /lib/) + fi + + PHP_ADD_INCLUDE($LUA_DIR/include) + PHP_ADD_LIBRARY_WITH_PATH(lua, $LUA_LIB_DIR, LUA_SHARED_LIBADD) + PHP_SUBST(LUA_SHARED_LIBADD) + PHP_NEW_EXTENSION(lua, lua.c lua_closure.c, $ext_shared) +fi diff --git a/config.w32 b/config.w32 new file mode 100644 index 0000000..9f7375a --- /dev/null +++ b/config.w32 @@ -0,0 +1,15 @@ +// $Id: config.w32 314169 2011-08-03 14:05:16Z laruence $ +// vim:ft=javascript + +ARG_WITH("lua", "lua support", "no"); + +if (PHP_LUA != "no") { + if (CHECK_LIB("liblua.lib", "lua", PHP_LUA) && + CHECK_HEADER_ADD_INCLUDE("lua.h", "CFLAGS_LUA", + PHP_LUA + "\\include;" + PHP_PHP_BUILD + "\\include\\lua;" + PHP_LUA)) { + EXTENSION("lua", "lua.c"); + AC_DEFINE('HAVE_LUA', 1, 'Have LUA library'); + } else { + WARNING("lua not enabled; libraries and headers not found"); + } +} diff --git a/lua.c b/lua.c new file mode 100644 index 0000000..191027c --- /dev/null +++ b/lua.c @@ -0,0 +1,908 @@ +/* + +----------------------------------------------------------------------+ + | Lua | + +----------------------------------------------------------------------+ + | 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 : Johannes Schlueter | + | Xinchen Hui | + | Marcelo Araujo | + +----------------------------------------------------------------------+ + $Id: lua.c 319740 2011-11-24 08:06:48Z laruence $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "Zend/zend_exceptions.h" +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include "php_lua.h" +#include "lua_closure.h" + +zend_class_entry *lua_ce; +zend_class_entry *lua_exception_ce; +static zend_object_handlers lua_object_handlers; + +/** {{{ ARG_INFO + * + */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_lua_call, 0, 0, 2) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, args) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lua_assign, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lua_register, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, function) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lua_include, 0, 0, 1) + ZEND_ARG_INFO(0, file) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lua_eval, 0, 0, 1) + ZEND_ARG_INFO(0, statements) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_lua_free, 0, 0, 1) + ZEND_ARG_INFO(0, closure) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ lua_module_entry +*/ +zend_module_entry lua_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "lua", + NULL, + PHP_MINIT(lua), + PHP_MSHUTDOWN(lua), + NULL, + NULL, + PHP_MINFO(lua), +#if ZEND_MODULE_API_NO >= 20010901 + PHP_LUA_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_LUA +ZEND_GET_MODULE(lua) +#endif + +/** {{{ static void php_lua_stack_dump(lua_State* L) + * just for debug + */ +#ifdef PHP_LUA_DEBUG +static void php_lua_stack_dump(lua_State* L) { + int i = 1; + int n = lua_gettop(L); + printf("The Length of stack is %d\n", n); + for (; i <= n; ++i) { + int t = lua_type(L, i); + printf("%s:", lua_typename(L, t)); + switch(t) { + case LUA_TNUMBER: + printf("%f", lua_tonumber(L, i)); + break; + case LUA_TSTRING: + printf("%s", lua_tostring(L, i)); + break; + case LUA_TTABLE: + break; + case LUA_TFUNCTION: + break; + case LUA_TNIL: + printf("NULL"); + break; + case LUA_TBOOLEAN: + printf("%s", lua_toboolean(L, i) ? "TRUE" : "FALSE"); + break; + default: + break; + } + printf("\n"); + } +} +#endif +/* }}} */ + +/** {{{ static int php_lua_atpanic(lua_State *L) +*/ +static int php_lua_atpanic(lua_State *L) { + TSRMLS_FETCH(); + php_error_docref(NULL TSRMLS_CC, E_ERROR, "lua panic (%s)", lua_tostring(L, 1)); + lua_pop(L, 1); + zend_bailout(); + return 0; +} +/* }}} */ + +/** {{{ static int php_lua_print(lua_State *L) +*/ +static int php_lua_print(lua_State *L) { + int i = 0; + + TSRMLS_FETCH(); + for (i = -lua_gettop(L) ; i<0; i++) { + zval *tmp = php_lua_get_zval_from_lua(L, i, NULL TSRMLS_CC); + zend_print_zval_r(tmp, 1 TSRMLS_CC); + zval_ptr_dtor(&tmp); + } + return 0; +} +/* }}} */ + +/** {{{ static void * php_lua_alloc_function(void *ud, void *ptr, size_t osize, size_t nsize) + * the memory-allocation function used by Lua states. + */ +static void * php_lua_alloc_function(void *ud, void *ptr, size_t osize, size_t nsize) { + (void) osize; + (void) nsize; + + if (nsize) { + if (ptr) { + return erealloc(ptr, nsize); + } + return emalloc(nsize); + } else { + if (ptr) { + efree(ptr); + } + } + + return NULL; +} +/* }}} */ + +/** {{{ static void php_lua_dtor_object(void *object, zend_object_handle handle TSRMLS_DC) + * the dtor function for lua object + */ +static void php_lua_dtor_object(void *object, zend_object_handle handle TSRMLS_DC) { + php_lua_object *lua_obj = (php_lua_object *)object; + + zend_object_std_dtor(&(lua_obj->obj) TSRMLS_CC); + + if (lua_obj->L) { + lua_close(lua_obj->L); + } + + efree(lua_obj); +} +/* }}} */ + +/** {{{ static zend_object_value php_lua_create_object(zend_class_entry *ce TSRMLS_DC) + * + * the create object handler for lua + */ +static zend_object_value php_lua_create_object(zend_class_entry *ce TSRMLS_DC) { + zend_object_value obj = {0}; + php_lua_object *lua_obj = NULL; + lua_State *L = NULL; + + L = lua_newstate(php_lua_alloc_function, NULL); + + lua_atpanic(L, php_lua_atpanic); + + lua_obj = emalloc(sizeof(php_lua_object)); + + if (!lua_obj) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "alloc memory for lua object failed"); + } + + lua_obj->L = L; + zend_object_std_init(&(lua_obj->obj), ce TSRMLS_CC); + +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 4) + zend_hash_copy(lua_obj->obj.properties, &ce->default_properties, +#if (PHP_MINOR_VERSION < 3) + (copy_ctor_func_t) zval_add_ref, +#else + zval_copy_property_ctor(ce), +#endif + (void *)0, sizeof(zval *)); +#elif (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 3) + object_properties_init(&(lua_obj->obj), ce); +#endif + + obj.handle = zend_objects_store_put(lua_obj, php_lua_dtor_object, NULL, NULL TSRMLS_CC); + obj.handlers = &lua_object_handlers; + + return obj; +} +/* }}} */ + +/** {{{ static zval * php_lua_read_property(zval *object, zval *member, int type TSRMLS_DC) +*/ +#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 4)) || (PHP_MAJOR_VERSION < 5) +static zval * php_lua_read_property(zval *object, zval *member, int type TSRMLS_DC) { +#else +static zval * php_lua_read_property(zval *object, zval *member, int type, const struct _zend_literal *key TSRMLS_DC) { + (void)key; +#endif + zval *retval = NULL; + lua_State *L = NULL; + zval *tmp_member = NULL; + + if (type != BP_VAR_R) { + MAKE_STD_ZVAL(retval); + ZVAL_NULL(retval); + return retval; + } + + if (Z_TYPE_P(member) != IS_STRING) { + ALLOC_ZVAL(tmp_member); + *tmp_member = *member; + INIT_PZVAL(tmp_member); + zval_copy_ctor(tmp_member); + convert_to_string(tmp_member); + member = tmp_member; + } + + L = Z_LUAVAL_P(object); + + lua_getfield(L, LUA_GLOBALSINDEX, Z_STRVAL_P(member)); + retval = php_lua_get_zval_from_lua(L, -1, object TSRMLS_CC); + Z_DELREF_P(retval); + lua_pop(L, 1); + + if (tmp_member) { + zval_ptr_dtor(&tmp_member); + } + + return retval; +} +/* }}} */ + +/** {{{ static void php_lua_write_property(zval *object, zval *member, zval *value TSRMLS_DC) +*/ +#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 4)) || (PHP_MAJOR_VERSION < 5) +static void php_lua_write_property(zval *object, zval *member, zval *value TSRMLS_DC) { +#else +static void php_lua_write_property(zval *object, zval *member, zval *value, const struct _zend_literal *key TSRMLS_DC) { + (void)key; +#endif + lua_State *L = NULL; + zval *tmp_member = NULL; + + if (Z_TYPE_P(member) != IS_STRING) { + ALLOC_ZVAL(tmp_member); + *tmp_member = *member; + INIT_PZVAL(tmp_member); + zval_copy_ctor(tmp_member); + convert_to_string(tmp_member); + member = tmp_member; + } + + L = Z_LUAVAL_P(object); + + php_lua_send_zval_to_lua(L, member TSRMLS_CC); + php_lua_send_zval_to_lua(L, value TSRMLS_CC); + + lua_settable(L, LUA_GLOBALSINDEX); + + if (tmp_member) { + zval_ptr_dtor(&tmp_member); + } +} +/* }}} */ + +/** {{{ zval * php_lua_get_zval_from_lua(lua_State *L, int index, zval *lua_obj TSRMLS_DC) +*/ +zval * php_lua_get_zval_from_lua(lua_State *L, int index, zval *lua_obj TSRMLS_DC) { + zval *retval; + + MAKE_STD_ZVAL(retval); + ZVAL_NULL(retval); + + switch (lua_type(L, index)) { + case LUA_TNIL: + ZVAL_NULL(retval); + break; + case LUA_TBOOLEAN: + ZVAL_BOOL(retval, lua_toboolean(L, index)); + break; + case LUA_TNUMBER: + ZVAL_DOUBLE(retval, lua_tonumber(L, index)); + break; + case LUA_TSTRING: + { + char *val = NULL; + size_t len = 0; + + val = (char *)lua_tolstring(L, index, &len); + ZVAL_STRINGL(retval, val, len, 1); + } + break; + case LUA_TTABLE: + array_init(retval); + lua_pushnil(L); /* first key */ + while (lua_next(L, index-1) != 0) { + zval *key = NULL; + zval *val = NULL; + + /* uses 'key' (at index -2) and 'value' (at index -1) */ + key = php_lua_get_zval_from_lua(L, -2, lua_obj TSRMLS_CC); + val = php_lua_get_zval_from_lua(L, -1, lua_obj TSRMLS_CC); + + if (!key || !val) { + /* there is a warning already in php_lua_get_zval_from_lua */ + break; + } + + switch(Z_TYPE_P(key)) { + case IS_DOUBLE: + case IS_LONG: + add_index_zval(retval, Z_DVAL_P(key), val); + break; + case IS_STRING: + add_assoc_zval(retval, Z_STRVAL_P(key), val); + break; + case IS_ARRAY: + case IS_OBJECT: + default: + break; + } + lua_pop(L, 1); + zval_ptr_dtor(&key); + } + break; + case LUA_TFUNCTION: + { + long ref_id = 0; + if (!lua_obj) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "corrupted Lua object"); + break; + } + + lua_pushvalue(L, index); + ref_id = luaL_ref(L, LUA_REGISTRYINDEX); + + if (!php_lua_closure_instance(retval, ref_id, lua_obj TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to initialize closure object"); + } + } + break; + case LUA_TUSERDATA: + case LUA_TTHREAD: + case LUA_TLIGHTUSERDATA: + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unsupported type '%s' for php", + lua_typename(L, lua_type(L, index))); + } + + return retval; +} +/* }}} */ + +/** {{{ int php_lua_send_zval_to_lua(lua_State *L, zval *val TSRMLS_DC) +*/ +int php_lua_send_zval_to_lua(lua_State *L, zval *val TSRMLS_DC) { + + switch (Z_TYPE_P(val)) { + case IS_BOOL: + lua_pushboolean(L, Z_BVAL_P(val)); + break; + case IS_NULL: + lua_pushnil(L); + break; + case IS_DOUBLE: + lua_pushnumber(L, Z_DVAL_P(val)); + break; + case IS_LONG: + lua_pushnumber(L, Z_LVAL_P(val)); + break; + case IS_STRING: + lua_pushlstring(L, Z_STRVAL_P(val), Z_STRLEN_P(val)); + break; + case IS_OBJECT: + case IS_ARRAY: + { + HashTable *ht = NULL; + zval **ppzval = NULL; + + ht = HASH_OF(val); + + if (++ht->nApplyCount > 1) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "recursion found"); + --ht->nApplyCount; + break; + } + + lua_newtable(L); + for(zend_hash_internal_pointer_reset(ht); + zend_hash_get_current_data(ht, (void **)&ppzval) == SUCCESS; + zend_hash_move_forward(ht)) { + char *key = NULL; + int len = 0; + long idx = 0; + zval *zkey= NULL; + + switch(zend_hash_get_current_key_ex(ht, &key, &len, &idx, 0, NULL)) { + case HASH_KEY_IS_STRING : + MAKE_STD_ZVAL(zkey); + ZVAL_STRINGL(zkey, key, len - 1, 1); + break; + case HASH_KEY_IS_LONG: + if (idx == 0) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE + , "attempt to pass an array index begin with 0 to lua, the index 0 will be discarded"); + continue; + } + MAKE_STD_ZVAL(zkey); + ZVAL_LONG(zkey, idx); + break; + } + + php_lua_send_zval_to_lua(L, zkey TSRMLS_CC); + php_lua_send_zval_to_lua(L, *ppzval TSRMLS_CC); + lua_settable(L, -3); + + zval_ptr_dtor(&zkey); + } + } + break; + default: + php_error_docref(NULL TSRMLS_CC, E_ERROR, "unsupported type `%s' for lua" + , zend_zval_type_name(val)); + lua_pushnil(L); + return 1; + } + + return 0; +} +/* }}} */ + +/*** {{{ static int php_lua_arg_apply_func(void *data, void *L TSRMLS_DC) +*/ +static int php_lua_arg_apply_func(void *data, void *L TSRMLS_DC) { + php_lua_send_zval_to_lua((lua_State*)L, *(zval**)data TSRMLS_CC); + return ZEND_HASH_APPLY_KEEP; +} /* }}} */ + +/** {{{ static int php_lua_call_callback(lua_State *L) +*/ +static int php_lua_call_callback(lua_State *L) { + int order = 0; + zval *return_value = NULL; + zval **func = NULL; + zval *callbacks = NULL; + order = lua_tonumber(L, lua_upvalueindex(1)); + + TSRMLS_FETCH(); + callbacks = zend_read_static_property(lua_ce, ZEND_STRL("_callbacks"), 1 TSRMLS_CC); + + if (ZVAL_IS_NULL(callbacks)) { + return 0; + } + + MAKE_STD_ZVAL(return_value); + + if (zend_hash_index_find(Z_ARRVAL_P(callbacks), order, (void **)&func) == FAILURE) { + return 0; + } + + if (!zend_is_callable(*func, 0, NULL TSRMLS_CC)) { + return 0; + } else { + zval **params = NULL; + int i = 0; + int arg_num = lua_gettop(L); + + params = ecalloc(arg_num, sizeof(zval)); + + for (; i 1) { + array_init(return_value); + for (i = -ret_count; i<0; i++) { + tmp = php_lua_get_zval_from_lua(L, i, getThis() TSRMLS_CC); + add_next_index_zval(return_value, tmp); + } + } else if (ret_count) { + zval *tmp = php_lua_get_zval_from_lua(L, -1, getThis() TSRMLS_CC); + RETURN_ZVAL(tmp, 1, 1); + } + lua_pop(L, ret_count); + } +} +/* }}} */ + +/** {{{ proto Lua::include(string $file) + * run a lua script file + */ +PHP_METHOD(lua, include) { + lua_State *L = NULL; + char *file = NULL; + long bp, len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &len) == FAILURE) { + return; + } + + if (php_check_open_basedir(file TSRMLS_CC) +#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 4)) || (PHP_MAJOR_VERSION < 5) + || (PG(safe_mode) + && !php_checkuid(file, "rb+", CHECKUID_CHECK_MODE_PARAM)) +#endif + ){ + RETURN_FALSE; + } + + L = Z_LUAVAL_P(getThis()); + + bp = lua_gettop(L); + if (luaL_dofile(L, file)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "lua error: %s", lua_tostring(L, -1)); + lua_pop(L, 1); + RETURN_FALSE; + } else { + zval *tmp = NULL; + int ret_count = 0; + int i = 0; + + ret_count = lua_gettop(L) - bp; + if (ret_count > 1) { + array_init(return_value); + + for (i = -ret_count; i<0; i++) { + tmp = php_lua_get_zval_from_lua(L, i, getThis() TSRMLS_CC); + add_next_index_zval(return_value, tmp); + } + + } else if (ret_count) { + zval *tmp = php_lua_get_zval_from_lua(L, -1, getThis() TSRMLS_CC); + RETURN_ZVAL(tmp, 1, 1); + } + + lua_pop(L, ret_count); + } +} +/* }}} */ + +/** {{{ proto Lua::call(mixed $function, array $args, bool $use_self) +*/ +PHP_METHOD(lua, call) { + long u_self = 0; + zval *args = NULL; + zval *func = NULL; + zval *ret = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|al", &func, &args, &u_self) == FAILURE) { + return; + } + + if ((ret = php_lua_call_lua_function(getThis(), func, args, u_self TSRMLS_CC))) { + RETURN_ZVAL(ret, 1, 1); + } + + RETURN_FALSE; +} +/* }}} */ + +/** {{{ proto Lua::assign(string $name, mix $value) +*/ +PHP_METHOD(lua, assign) { + char *name = NULL; + zval *value = NULL; + lua_State *L = NULL; + int len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &len, &value) == FAILURE) { + return; + } + + L = Z_LUAVAL_P(getThis()); + + php_lua_send_zval_to_lua(L, value TSRMLS_CC); + lua_setfield(L, LUA_GLOBALSINDEX, name); + + RETURN_ZVAL(getThis(), 1, 0); +} +/* }}} */ + +/** {{{ proto Lua::registerCallback(string $name, mix $value) +*/ +PHP_METHOD(lua, registerCallback) { + char *name = NULL; + long len = 0; + zval *func = NULL; + lua_State *L = NULL; + zval* callbacks = NULL; + L = Z_LUAVAL_P(getThis()); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"sz", &name, &len, &func) == FAILURE) { + return; + } + + callbacks = zend_read_static_property(lua_ce, ZEND_STRL("_callbacks"), 1 TSRMLS_CC); + + if (ZVAL_IS_NULL(callbacks)) { + array_init(callbacks); + } + + if (zend_is_callable(func, 0, NULL TSRMLS_CC)) { + lua_pushnumber(L, zend_hash_num_elements(Z_ARRVAL_P(callbacks))); + lua_pushcclosure(L, php_lua_call_callback, 1); + lua_setglobal(L, name); + } else { + zend_throw_exception_ex(lua_exception_ce, 0 TSRMLS_CC, "invalid php callback"); + RETURN_FALSE; + } + + zval_add_ref(&func); + add_next_index_zval(callbacks, func); + + RETURN_ZVAL(getThis(), 1, 0); +} +/* }}} */ + +/** {{{ proto Lua::getVersion() +*/ +PHP_METHOD(lua, getVersion) { + RETURN_STRING(LUA_RELEASE, 1); +} +/* }}} */ + +/** {{{ proto Lua::__construct() +*/ +PHP_METHOD(lua, __construct) { + lua_State *L = Z_LUAVAL_P(getThis()); + luaL_openlibs(L); + lua_register(L, "print", php_lua_print); + if (ZEND_NUM_ARGS()) { + PHP_MN(lua_include)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } +} +/* }}} */ + +/* {{{ lua_class_methods[] + * + */ +zend_function_entry lua_class_methods[] = { + PHP_ME(lua, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(lua, eval, arginfo_lua_eval, ZEND_ACC_PUBLIC) + PHP_ME(lua, include, arginfo_lua_include, ZEND_ACC_PUBLIC) + PHP_ME(lua, call, arginfo_lua_call, ZEND_ACC_PUBLIC) + PHP_ME(lua, assign, arginfo_lua_assign, ZEND_ACC_PUBLIC) + PHP_ME(lua, getVersion, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_ALLOW_STATIC) + PHP_ME(lua, registerCallback, arginfo_lua_register, ZEND_ACC_PUBLIC) + PHP_MALIAS(lua, __call, call, arginfo_lua_call, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION +*/ +PHP_MINIT_FUNCTION(lua) { + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "Lua", lua_class_methods); + + lua_ce = zend_register_internal_class(&ce TSRMLS_CC); + lua_ce->create_object = php_lua_create_object; + memcpy(&lua_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + + lua_object_handlers.write_property = php_lua_write_property; + lua_object_handlers.read_property = php_lua_read_property; + + lua_ce->ce_flags |= ZEND_ACC_FINAL; + + zend_declare_property_null(lua_ce, ZEND_STRL("_callbacks"), ZEND_ACC_STATIC|ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_class_constant_string(lua_ce, ZEND_STRL("LUA_VERSION"), LUA_RELEASE TSRMLS_CC); + + php_lua_closure_register(TSRMLS_C); + + INIT_CLASS_ENTRY(ce, "LuaException", NULL); + lua_exception_ce = zend_register_internal_class_ex(&ce, +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2) + zend_exception_get_default(), +#else + zend_exception_get_default(TSRMLS_C), +#endif + NULL TSRMLS_CC); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION +*/ +PHP_MSHUTDOWN_FUNCTION(lua) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION +*/ +PHP_MINFO_FUNCTION(lua) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "lua support", "enabled"); + php_info_print_table_row(2, "lua extension version", PHP_LUA_VERSION); + php_info_print_table_row(2, "lua release", LUA_RELEASE); + php_info_print_table_row(2, "lua copyright", LUA_COPYRIGHT); + php_info_print_table_row(2, "lua authors", LUA_AUTHORS); + php_info_print_table_end(); +} +/* }}} */ + +/* + * 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 + */ diff --git a/lua.php b/lua.php new file mode 100644 index 0000000..c5f8f20 --- /dev/null +++ b/lua.php @@ -0,0 +1,21 @@ +"; + +if(!extension_loaded('lua')) { + dl('lua.' . PHP_SHLIB_SUFFIX); +} +$module = 'lua'; +$functions = get_extension_funcs($module); +echo "Functions available in the test extension:$br\n"; +foreach($functions as $func) { + echo $func."$br\n"; +} +echo "$br\n"; +$function = 'confirm_' . $module . '_compiled'; +if (extension_loaded($module)) { + $str = $function($module); +} else { + $str = "Module $module is not compiled into PHP"; +} +echo "$str\n"; +?> diff --git a/lua_closure.c b/lua_closure.c new file mode 100644 index 0000000..ac17206 --- /dev/null +++ b/lua_closure.c @@ -0,0 +1,249 @@ +/* + +----------------------------------------------------------------------+ + | Lua | + +----------------------------------------------------------------------+ + | 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 : Johannes Schlueter | + | Xinchen Hui | + | Marcelo Araujo | + +----------------------------------------------------------------------+ + $Id: lua_closure.c 319740 2011-11-24 08:06:48Z laruence $ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "Zend/zend_exceptions.h" + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "php_lua.h" + +static zend_class_entry *lua_closure_ce; +extern zend_class_entry *lua_ce; +extern zend_class_entry *lua_exception_ce; + +/** {{{ ARG_INFO + * + */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_lua_invoke, 0, 0, 1) + ZEND_ARG_INFO(0, arg) + ZEND_ARG_INFO(0, ...) +ZEND_END_ARG_INFO() +/* }}} */ + +/** {{{ zval * php_lua_closure_instance(zval *instance, long ref_id, zval *lua_obj TSRMLS_DC) + */ +zval * php_lua_closure_instance(zval *instance, long ref_id, zval *lua_obj TSRMLS_DC) { + object_init_ex(instance, lua_closure_ce); + zend_update_property_long(lua_closure_ce, instance, ZEND_STRL("_closure"), ref_id TSRMLS_CC); + zend_update_property(lua_closure_ce, instance, ZEND_STRL("_lua_object"), lua_obj TSRMLS_CC); + + return instance; +} +/** }}} */ + +/** {{{ proto LuaClosure::__construct() +*/ +PHP_METHOD(lua_closure, __construct) { +} +/* }}} */ + +/** {{{ proto LuaClosure::__destruct() +*/ +PHP_METHOD(lua_closure, __destruct) { + zval *lua_obj, *closure; + + lua_obj = zend_read_property(lua_closure_ce, getThis(), ZEND_STRL("_lua_object"), 1 TSRMLS_CC); + if (ZVAL_IS_NULL(lua_obj) + || Z_TYPE_P(lua_obj) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(lua_obj), lua_ce TSRMLS_CC)) { + RETURN_FALSE; + } + + closure = zend_read_property(lua_closure_ce, getThis(), ZEND_STRL("_closure"), 1 TSRMLS_CC); + if (!Z_LVAL_P(closure)) { + RETURN_FALSE; + } + + luaL_unref(Z_LUAVAL_P(lua_obj), LUA_REGISTRYINDEX, Z_LVAL_P(closure)); +} +/* }}} */ + +/** {{{ proto LuaClosure::invoke(mxied $args) +*/ +PHP_METHOD(lua_closure, invoke) { + int bp, sp; + zval ***arguments = NULL; + zval *lua_obj = NULL; + lua_State *L = NULL; + zval *closure = NULL; + + if (ZEND_NUM_ARGS()) { + arguments = emalloc(sizeof(zval**) * ZEND_NUM_ARGS()); + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) { + efree(arguments); + zend_throw_exception_ex(lua_exception_ce, 0 TSRMLS_CC, "cannot get arguments for calling closure"); + return; + } + } + + lua_obj = zend_read_property(lua_closure_ce, getThis(), ZEND_STRL("_lua_object"), 1 TSRMLS_CC); + + if (ZVAL_IS_NULL(lua_obj) + || Z_TYPE_P(lua_obj) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(lua_obj), lua_ce TSRMLS_CC)) { + zend_throw_exception_ex(lua_exception_ce, 0 TSRMLS_CC, "corrupted Lua object"); + return; + } + + closure = zend_read_property(lua_closure_ce, getThis(), ZEND_STRL("_closure"), 1 TSRMLS_CC); + if (!Z_LVAL_P(closure)) { + zend_throw_exception_ex(lua_exception_ce, 0 TSRMLS_CC, "invalid lua closure"); + return; + } + + L = Z_LUAVAL_P(lua_obj); + + bp = lua_gettop(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, Z_LVAL_P(closure)); + if (LUA_TFUNCTION != lua_type(L, lua_gettop(L))) { + lua_pop(L, -1); + zend_throw_exception_ex(lua_exception_ce, 0 TSRMLS_CC, "call to lua closure failed"); + return; + } + + if (ZEND_NUM_ARGS()) { + int i = 0; + for(;iproperties, &ce->default_properties, +#if (PHP_MINOR_VERSION < 3) + (copy_ctor_func_t) zval_add_ref, +#else + zval_copy_property_ctor(ce), +#endif + (void *)0, sizeof(zval *)); +#elif (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 3) + object_properties_init(lua_closure_obj, ce); +#endif + + obj.handle = zend_objects_store_put(lua_closure_obj, php_lua_closure_dtor_object, NULL, NULL TSRMLS_CC); + obj.handlers = zend_get_std_object_handlers(); + + return obj; +} /* }}} */ + +void php_lua_closure_register(TSRMLS_D) /* {{{ */ +{ + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "LuaClosure", lua_closure_methods); + lua_closure_ce = zend_register_internal_class(&ce TSRMLS_CC); + lua_closure_ce->create_object = php_lua_closure_create_object; + lua_closure_ce->ce_flags |= ZEND_ACC_FINAL; + + zend_declare_property_long(lua_closure_ce, ZEND_STRL("_closure"), 0, ZEND_ACC_PRIVATE TSRMLS_CC); + zend_declare_property_null(lua_closure_ce, ZEND_STRL("_lua_object"), ZEND_ACC_PRIVATE TSRMLS_CC); + + +} /* }}} */ + +zend_class_entry *php_lua_get_closure_ce() /* {{{ */ +{ + return lua_closure_ce; +} /* }}} */ + +/* + * 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 + */ diff --git a/lua_closure.h b/lua_closure.h new file mode 100644 index 0000000..dba173f --- /dev/null +++ b/lua_closure.h @@ -0,0 +1,31 @@ +/* + +----------------------------------------------------------------------+ + | Lua | + +----------------------------------------------------------------------+ + | 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 : Johannes Schlueter | + | Xinchen Hui | + | Marcelo Araujo | + +----------------------------------------------------------------------+ + $Id: lua_closure.h 319733 2011-11-24 07:13:56Z laruence $ +*/ + +void php_lua_closure_register(TSRMLS_D); +zend_class_entry *php_lua_get_closure_ce(); +zval * php_lua_closure_instance(zval *instance, long ref_id, zval *lua_obj TSRMLS_DC); + +/* + * 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 + */ diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..a2961a7 --- /dev/null +++ b/package.xml @@ -0,0 +1,66 @@ + + + + lua + Embedded lua interpreter + "Lua is a powerful, fast, light-weight, embeddable scripting language." + This extension embeds the lua interpreter and offers an OO-API to lua variables and functions. + + + + johannes + Johannes Schlüter + johannes@php.net + lead + + + laruence + Xinchen Hui + laruence@php.net + lead + + + msaraujo + Marcelo Araujo + msaraujo@php.net + developer + + + + 0.9.1 + 2011-11-24 + PHP + beta + - Compatible with PHP5.3+ +- Fix ZTS build +- Fix mem leak when get value from lua + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/package2.xml b/package2.xml new file mode 100644 index 0000000..321374c --- /dev/null +++ b/package2.xml @@ -0,0 +1,119 @@ + + + lua + pecl.php.net + Embedded lua interpreter + "Lua is a powerful, fast, light-weight, embeddable scripting language." + This extension embeds the lua interpreter and offers an OO-API to lua variables and functions. + + Johannes Schlüter + johannes + johannes@php.net + yes + + + Xinchen Hui + laruence + laruence@php.net + yes + + + Marcelo Araujo + msaraujo + msaraujo@php.net + yes + + 2011-11-24 + + + 0.9.1 + 0.9.1 + + + beta + beta + + PHP + +- Compatible with PHP5.3+ +- Fix ZTS build +- Fix mem leak when get value from lua + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.1.0 + 5.4.0 + + + 1.4.0 + + + + lua + + + + 2011-11-24 + + 0.9.1 + 0.9.1 + + + beta + beta + + PHP License + + - Compatible with PHP5.3+ + - Fix ZTS build + - Fix mem leak when get value from lua + + + + 2011-08-03 + + 0.9.0 + 0.9.0 + + + beta + beta + + PHP License + + - Merge Plua into Lua + - Api changes + - Add supporting for LUA_TFUNCTION + + + + + diff --git a/php_lua.h b/php_lua.h new file mode 100644 index 0000000..46662d7 --- /dev/null +++ b/php_lua.h @@ -0,0 +1,78 @@ +/* + +----------------------------------------------------------------------+ + | Lua | + +----------------------------------------------------------------------+ + | 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 : Johannes Schlueter | + | Xinchen Hui | + | Marcelo Araujo | + +----------------------------------------------------------------------+ + $Id: php_lua.h 319733 2011-11-24 07:13:56Z laruence $ +*/ + +#ifndef PHP_LUA_H +#define PHP_LUA_H + +extern zend_module_entry lua_module_entry; +#define phpext_lua_ptr &lua_module_entry + +#ifdef PHP_WIN32 +#define PHP_LUA_API __declspec(dllexport) +#else +#define PHP_LUA_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#ifdef ZTS +#define LUA_G(v) TSRMG(lua_globals_id, zend_lua_globals *, v) +#else +#define LUA_G(v) (lua_globals.v) +#endif + +#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 3)) || (PHP_MAJOR_VERSION < 5) +#define Z_ADDREF_P ZVAL_ADDREF +#define Z_REFCOUNT_P ZVAL_REFCOUNT +#define Z_DELREF_P ZVAL_DELREF +#endif + +#define PHP_LUA_VERSION "0.9.0" +#define Z_LUAVAL_P(obj) ((php_lua_object*)(zend_object_store_get_object(obj TSRMLS_CC)))->L + +struct _php_lua_object { + zend_object obj; + lua_State *L; +}; + +typedef struct _php_lua_object php_lua_object; + +zval * php_lua_get_zval_from_lua(lua_State *L, int index, zval *lua_obj TSRMLS_DC); +int php_lua_send_zval_to_lua(lua_State *L, zval *val TSRMLS_DC); + +PHP_MINIT_FUNCTION(lua); +PHP_MSHUTDOWN_FUNCTION(lua); +PHP_MINFO_FUNCTION(lua); + +PHP_METHOD(lua, __construct); +PHP_METHOD(lua, eval); +PHP_METHOD(lua, require); +#endif /* PHP_LUA_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 + */ diff --git a/tests/001.phpt b/tests/001.phpt new file mode 100644 index 0000000..9619a82 --- /dev/null +++ b/tests/001.phpt @@ -0,0 +1,16 @@ +--TEST-- +Basic lua check +--SKIPIF-- + +--FILE-- +assign("b", 12); +$l->eval("print(b)"); +$l->eval('print("\n")'); +$l->eval("print(math.sin(b))"); +$l->eval("invalid code"); +--EXPECTF-- +12 +-0.53657291800043 +Warning: Lua::eval(): lua error: [string "line"]:1: '=' expected near 'code' in %s on line %d diff --git a/tests/002.phpt b/tests/002.phpt new file mode 100644 index 0000000..7d125e1 --- /dev/null +++ b/tests/002.phpt @@ -0,0 +1,19 @@ +--TEST-- +Set and read properties +--SKIPIF-- + +--FILE-- +a = "foobar"; +$l->b = 12; +var_dump($l->b); +var_dump($l->a); + +$l->b = null; +var_dump($l->b); +?> +--EXPECT-- +float(12) +string(6) "foobar" +NULL diff --git a/tests/003.phpt b/tests/003.phpt new file mode 100644 index 0000000..af50e46 --- /dev/null +++ b/tests/003.phpt @@ -0,0 +1,95 @@ +--TEST-- +Call lua functions +--SKIPIF-- + +--FILE-- +eval(<<print("Hello world!\n"); +$l->phptest_no_param(); + +$l->phptest_1_param(); +$l->phptest_1_param("foobar"); + +$l->phptest_2_params(); +$l->phptest_2_params("foo"); +$l->phptest_2_params("foo", "bar"); + +echo "\n"; + +var_dump($l->call(array("math", "sin"), array(3/2*pi()))); +var_dump($l->call(array("math", "cos"), array(pi()))); + +echo "\n"; + +try { +$l->notexisting(2423); +$l->call(array("math", "sin", "function"), array(432, 342)); + +} catch (LuaException $e) { + echo $e->getMessage(); +} + +echo "\n"; + +var_dump($l->type("fobar")); +var_dump($l->multiret()); + +echo "\n"; + +var_dump($l->__call("multiret", array())); + +$l->__call("print"); +$l->__call("print", array("foo")); +?> +--EXPECTF-- +Hello world! +No parameters! +Parameter: `'! +Parameter: `foobar'! +Parameter 1: `', Parameter 2: `'! +Parameter 1: `foo', Parameter 2: `'! +Parameter 1: `foo', Parameter 2: `bar'! + +float(-1) +float(-1) + +invalid lua function 'notexisting' +string(6) "string" +array(3) { + [0]=> + string(1) "a" + [1]=> + string(1) "b" + [2]=> + string(1) "c" +} + +array(3) { + [0]=> + string(1) "a" + [1]=> + string(1) "b" + [2]=> + string(1) "c" +} +foo diff --git a/tests/004.phpt b/tests/004.phpt new file mode 100644 index 0000000..dafcfbb --- /dev/null +++ b/tests/004.phpt @@ -0,0 +1,35 @@ +--TEST-- +Type conversion from lua to PHP +--SKIPIF-- + +--FILE-- +eval(<<{$i}); +} +?> +--EXPECT-- +NULL +bool(false) +float(23) +float(-432) +float(432.432) +float(0) +float(1.43E+25) +string(6) "foobar" +string(0) "" diff --git a/tests/005.phpt b/tests/005.phpt new file mode 100644 index 0000000..5b03c17 --- /dev/null +++ b/tests/005.phpt @@ -0,0 +1,17 @@ +--TEST-- +Lua phpinfo() block +--SKIPIF-- + +--FILE-- +info(); +?> +--EXPECTF-- +lua + +lua support => enabled +lua extension version => %s +lua release => %s +lua copyright => %s +lua authors => %s diff --git a/tests/006.phpt b/tests/006.phpt new file mode 100644 index 0000000..39bbac9 --- /dev/null +++ b/tests/006.phpt @@ -0,0 +1,39 @@ +--TEST-- +Lua::include() +--SKIPIF-- + +--FILE-- + 'print "Hello PHP"', + 'broken' => 'dgdr fdrg erb rxdgre tews< df hxfdxgfc gsdgxvrsgrg.ve4w', + 'return' => 'return "php", "lua", "extension"', +); + +$l = new lua(); + +foreach ($code as $n => $c) { + echo "\nTesting $n\n"; + file_put_contents($filename, $c); + $ret = $l->include($filename); + if ($ret) print_r($ret); + @unlink($filename); +} +?> +--EXPECTF-- + +Testing fine +Hello PHP +Testing broken + +Warning: Lua::include(): lua error: %s '=' expected near 'fdrg' in %s on line %d + +Testing return +Array +( + [0] => php + [1] => lua + [2] => extension +) diff --git a/tests/007.phpt b/tests/007.phpt new file mode 100644 index 0000000..d382dda --- /dev/null +++ b/tests/007.phpt @@ -0,0 +1,30 @@ +--TEST-- +Lua return function +--SKIPIF-- + +--FILE-- +eval(<<closure(); + +$l->call($func); +unset($func); +$func = $l->closure(); + +$l->call($func); +unset($func); +?> +--EXPECTF-- +lualua diff --git a/tests/008.phpt b/tests/008.phpt new file mode 100644 index 0000000..c18ae93 --- /dev/null +++ b/tests/008.phpt @@ -0,0 +1,41 @@ +--TEST-- +register php function to lua +--SKIPIF-- + +--FILE-- +registerCallback("callphp", array("A", "intro")); + +$l->eval(<<registerCallback("callphp2", array(new B, "intro")); + + +$l->registerCallback("echo", "var_dump"); + +$l->eval(<< +--EXPECTF-- +foobarfoo,barstring(5) "maybe" diff --git a/tests/009.phpt b/tests/009.phpt new file mode 100644 index 0000000..8749a99 --- /dev/null +++ b/tests/009.phpt @@ -0,0 +1,45 @@ +--TEST-- +Bug (eval and include compute wrong return value number) +--SKIPIF-- + +--FILE-- +eval(<<closure(); + +$arr = $l->eval(<<call($func); +unset($func); +$func = $l->closure(); + +$l->call($func); +unset($func); +?> +--EXPECTF-- +Array +( + [0] => 2 + [1] => 3 + [2] => 4 + [3] => 5 + [4] => 6 +) +lualua diff --git a/tests/010.phpt b/tests/010.phpt new file mode 100644 index 0000000..b6b95ba --- /dev/null +++ b/tests/010.phpt @@ -0,0 +1,34 @@ +--TEST-- +LuaClosure exception +--SKIPIF-- + +--FILE-- +eval(<<closure(); + +try { + $l->call($func); + if (version_compare(PHP_VERSION, "5.3.0") >= 0) { + $func(); + } else { + $func->invoke(); + } +} catch (LuaException $e) { + echo $e->getMessage(); +} +?> +--EXPECTF-- +lualua diff --git a/tests/011.phpt b/tests/011.phpt new file mode 100644 index 0000000..6e6723a --- /dev/null +++ b/tests/011.phpt @@ -0,0 +1,18 @@ +--TEST-- +register invalid php callback to lua +--SKIPIF-- + +--FILE-- +registerCallback("callphp", "print"); + echo "okey"; +} catch (Exception $e) { + echo $e->getMessage(); +} + +--EXPECT-- +invalid php callback