/* +----------------------------------------------------------------------+ | 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 | | Helmut Januschka | +----------------------------------------------------------------------+ */ #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 "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 */ static void php_lua_stack_dump(lua_State* L) { int i = 1; int n = lua_gettop(L); php_printf("The Length of stack is %d\n", n); for (; i <= n; ++i) { int t = lua_type(L, i); php_printf("%s:", lua_typename(L, t)); switch(t) { case LUA_TNUMBER: php_printf("%f", lua_tonumber(L, i)); break; case LUA_TSTRING: php_printf("%s", lua_tostring(L, i)); break; case LUA_TTABLE: break; case LUA_TFUNCTION: break; case LUA_TNIL: php_printf("NULL"); break; case LUA_TBOOLEAN: php_printf("%s", lua_toboolean(L, i) ? "TRUE" : "FALSE"); break; default: break; } php_printf("\n"); } } /* }}} */ /** {{{ static int php_lua_atpanic(lua_State *L) */ static int php_lua_atpanic(lua_State *L) { php_error_docref(NULL, 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) { zval rv; int i = 0; int nargs = lua_gettop(L); for (i = 1; i <= nargs; ++i) { php_lua_get_zval_from_lua(L, i, NULL, &rv); zend_print_zval_r(&rv, 1); zval_ptr_dtor(&rv); } 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(zend_object *object) /* {{{ */ { php_lua_object *lua_obj = php_lua_obj_from_obj(object); zend_object_std_dtor(&(lua_obj->obj)); } /* }}} */ static void php_lua_free_object(zend_object *object) /* {{{ */ { php_lua_object *lua_obj = php_lua_obj_from_obj(object); if (lua_obj->L) { lua_close(lua_obj->L); } } /* }}} */ /** {{{ static zend_object_value php_lua_create_object(zend_class_entry *ce) */ zend_object *php_lua_create_object(zend_class_entry *ce) { php_lua_object* intern; lua_State *L; L = luaL_newstate(); lua_atpanic(L, php_lua_atpanic); intern = emalloc(sizeof(php_lua_object) + sizeof(zval) * (ce->default_properties_count - 1)); if (!intern) { php_error_docref(NULL, E_ERROR, "alloc memory for lua object failed"); } intern->L = L; zend_object_std_init(&intern->obj, ce); object_properties_init(&intern->obj, ce); intern->obj.handlers = &lua_object_handlers; return &intern->obj; } /* }}} */ /** {{{ static zval * php_lua_read_property(zval *object, zval *member, int type) */ zval *php_lua_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv){ lua_State *L = NULL; zval *tmp_member = NULL; if (type != BP_VAR_R) { ZVAL_NULL(rv); return rv; } if (Z_TYPE_P(member) != IS_STRING) { *tmp_member = *member; zval_copy_ctor(tmp_member); convert_to_string(tmp_member); member = tmp_member; } L = (Z_LUAVAL_P(object))->L; #if (LUA_VERSION_NUM < 502) lua_getfield(L, LUA_GLOBALSINDEX, Z_STRVAL_P(member)); #else lua_getglobal(L, Z_STRVAL_P(member)); #endif php_lua_get_zval_from_lua(L, -1, object, rv); lua_pop(L, 1); return rv; } /* }}} */ /** {{{ static void php_lua_write_property(zval *object, zval *member, zval *value) */ static void php_lua_write_property(zval *object, zval *member, zval *value, void ** key) { lua_State *L = NULL; zval *tmp_member = NULL; if (Z_TYPE_P(member) != IS_STRING) { *tmp_member = *member; zval_copy_ctor(tmp_member); convert_to_string(tmp_member); member = tmp_member; } L = (Z_LUAVAL_P(object))->L; #if (LUA_VERSION_NUM < 502) php_lua_send_zval_to_lua(L, member); php_lua_send_zval_to_lua(L, value); lua_settable(L, LUA_GLOBALSINDEX); #else php_lua_send_zval_to_lua(L, value); lua_setglobal(L, Z_STRVAL_P(member)); #endif if (tmp_member) { zval_ptr_dtor(tmp_member); } } /* }}} */ /** {{{ static int php_lua_call_callback(lua_State *L) */ static int php_lua_call_callback(lua_State *L) { int order = 0; zval retval; zval *func = NULL; zval *callbacks = NULL; order = lua_tonumber(L, lua_upvalueindex(1)); callbacks = zend_read_static_property(lua_ce, ZEND_STRL("_callbacks"), 1); if (ZVAL_IS_NULL(callbacks)) { return 0; } func = zend_hash_index_find(Z_ARRVAL_P(callbacks), order); if (!zend_is_callable(func, 0, NULL)) { return 0; } else { zval *params; int i = 0; int arg_num = lua_gettop(L); params = safe_emalloc(sizeof(zval), arg_num, 0); for (i = 0; i < arg_num; i++) { php_lua_get_zval_from_lua(L, -(arg_num - i), NULL, ¶ms[i]); } call_user_function(EG(function_table), NULL, func, &retval, arg_num, params); php_lua_send_zval_to_lua(L, &retval); for (i = 0; i 1) { php_error_docref(NULL, E_ERROR, "recursion found"); ZEND_HASH_DEC_APPLY_COUNT(ht); break; } } lua_newtable(L); ZEND_HASH_FOREACH_KEY_VAL_IND(ht, longkey, key, v) { if (key) { ZVAL_STR(&zkey, key); } else { ZVAL_LONG(&zkey, longkey); } php_lua_send_zval_to_lua(L, &zkey); php_lua_send_zval_to_lua(L, v); lua_settable(L, -3); } ZEND_HASH_FOREACH_END(); if (ZEND_HASH_APPLY_PROTECTION(ht)) { ZEND_HASH_DEC_APPLY_COUNT(ht); } } } break; case IS_REFERENCE: ZVAL_DEREF(val); goto try_again; break; case IS_INDIRECT: val = Z_INDIRECT_P(val); goto try_again; break; default: php_error_docref(NULL, 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) */ static int php_lua_arg_apply_func(void *data, void *L) { php_lua_send_zval_to_lua((lua_State*)L, (zval*)data); return ZEND_HASH_APPLY_KEEP; } /* }}} */ static zval *php_lua_call_lua_function(zval *lua_obj, zval *func, zval *args, int use_self, zval *retval) /* {{{ */ { int bp = 0; int sp = 0; int arg_num = 0; zval rv; lua_State *L; L = (Z_LUAVAL_P(lua_obj))->L; if (IS_ARRAY == Z_TYPE_P(func)) { zval *t, *f; if ((t = zend_hash_index_find(Z_ARRVAL_P(func), 0)) == NULL || Z_TYPE_P(t) != IS_STRING || (f = zend_hash_index_find(Z_ARRVAL_P(func), 1)) == NULL || Z_TYPE_P(f) != IS_STRING) { /* as johannes suggesting use exceptioni to distinguish the error from a lua function return false php_error_docref(NULL, E_WARNING, "invalid lua function, argument must be an array which contain two elements: array('table', 'method')"); */ zend_throw_exception_ex(lua_exception_ce, 0, "invalid lua function, argument must be an array which contain two elements: array('table', 'method')"); return NULL; } #if (LUA_VERSION_NUM < 502) lua_getfield(L, LUA_GLOBALSINDEX, Z_STRVAL_P(t)); #else lua_getglobal(L, Z_STRVAL_P(t)); #endif if (LUA_TTABLE != lua_type(L, lua_gettop(L))) { lua_pop(L, -1); zend_throw_exception_ex(lua_exception_ce, 0, "invalid lua table '%s'", Z_STRVAL_P(t)); return NULL; } bp = lua_gettop(L); lua_getfield(L, -1, Z_STRVAL_P(f)); if (LUA_TFUNCTION != lua_type(L, lua_gettop(L))) { lua_pop(L, -2); zend_throw_exception_ex(lua_exception_ce, 0, "invalid lua table function '%s'.%s", Z_STRVAL_P(t), Z_STRVAL_P(f)); return NULL; } } else if (IS_STRING == Z_TYPE_P(func)) { bp = lua_gettop(L); #if (LUA_VERSION_NUM < 502) lua_getfield(L, LUA_GLOBALSINDEX, Z_STRVAL_P(func)); #else lua_getglobal(L, Z_STRVAL_P(func)); #endif if (LUA_TFUNCTION != lua_type(L, lua_gettop(L))) { lua_pop(L, -1); zend_throw_exception_ex(lua_exception_ce, 0, "invalid lua function '%s'", Z_STRVAL_P(func)); return NULL; } } else if (IS_OBJECT == Z_TYPE_P(func) && instanceof_function(Z_OBJCE_P(func), php_lua_get_closure_ce())) { zval *closure = zend_read_property(php_lua_get_closure_ce(), func, ZEND_STRL("_closure"), 1, &rv); if (!Z_LVAL_P(closure)) { zend_throw_exception_ex(lua_exception_ce, 0, "invalid lua closure"); return NULL; } 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, "call to lua closure failed"); return NULL; } } if (use_self) { lua_pushvalue(L, -2); lua_remove(L, -2); arg_num++; } if (args) { arg_num += zend_hash_num_elements(Z_ARRVAL_P(args)); zend_hash_apply_with_argument(Z_ARRVAL_P(args), (apply_func_arg_t)php_lua_arg_apply_func, (void *)L); } if (lua_pcall(L, arg_num, LUA_MULTRET, 0) != LUA_OK) { php_error_docref(NULL, E_WARNING, "call to lua function %s failed", lua_tostring(L, -1)); lua_pop(L, lua_gettop(L) - bp); return NULL; } sp = lua_gettop(L) - bp; if (!sp) { ZVAL_NULL(retval); } else if (sp == 1) { php_lua_get_zval_from_lua(L, -1, lua_obj, retval); } else { zval rv; int i = 0; array_init(retval); for (i = -sp; i < 0; i++) { php_lua_get_zval_from_lua(L, i, lua_obj, &rv); add_next_index_zval(retval, &rv); } } lua_pop(L, sp); if (IS_ARRAY == Z_TYPE_P(func)) { lua_pop(L, -1); } return retval; } /* }}} */ /** {{{ proto Lua::eval(string $lua_chunk) * eval a lua chunk */ PHP_METHOD(lua, eval) { lua_State *L; char *statements; long bp, len; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &statements, &len) == FAILURE) { return; } L = (Z_LUAVAL_P(getThis()))->L; bp = lua_gettop(L); if ((ret = luaL_loadbuffer(L, statements, len, "line")) != LUA_OK || (ret = lua_pcall(L, 0, LUA_MULTRET, 0) != LUA_OK)) { zend_throw_exception_ex(lua_exception_ce, ret, "%s", lua_tostring(L, -1)); lua_pop(L, 1); RETURN_FALSE; } else { int ret_count; ret_count = lua_gettop(L) - bp; if (ret_count > 1) { zval rv; int i = 0; array_init(return_value); for (i = -ret_count; i<0; i++) { php_lua_get_zval_from_lua(L, i, getThis(), &rv); add_next_index_zval(return_value, &rv); } } else if (ret_count) { php_lua_get_zval_from_lua(L, -1, getThis(), return_value); } lua_pop(L, ret_count); } } /* }}} */ /** {{{ proto Lua::include(string $file) * run a lua script file */ PHP_METHOD(lua, include) { lua_State *L; char *file; size_t bp, len; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &file, &len) == FAILURE) { return; } if (php_check_open_basedir(file)) { RETURN_FALSE; } L = (Z_LUAVAL_P(getThis()))->L; bp = lua_gettop(L); if ((ret = luaL_loadfile(L, file)) != LUA_OK || (ret = lua_pcall(L, 0, LUA_MULTRET, 0) != LUA_OK)) { zend_throw_exception_ex(lua_exception_ce, ret, "%s", lua_tostring(L, -1)); lua_pop(L, 1); RETURN_FALSE; } else { int ret_count; ret_count = lua_gettop(L) - bp; if (ret_count > 1) { zval rv; int i = 0; array_init(return_value); for (i = -ret_count; i<0; i++) { php_lua_get_zval_from_lua(L, i, getThis(), &rv); add_next_index_zval(return_value, &rv); } } else if (ret_count) { php_lua_get_zval_from_lua(L, -1, getThis(), return_value); } lua_pop(L, ret_count); } } /* }}} */ /** {{{ proto Lua::call(mixed $function, array $args, bool $use_self) */ PHP_METHOD(lua, call) { long u_self = 0; zval *func; zval *args = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|al", &func, &args, &u_self) == FAILURE) { return; } if (!(php_lua_call_lua_function(getThis(), func, args, u_self, return_value))) { RETURN_FALSE; } } /* }}} */ /** {{{ proto Lua::assign(string $name, mix $value) */ PHP_METHOD(lua, assign) { zval *name; zval *value; lua_State *L; if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &name, &value) == FAILURE) { return; } L = (Z_LUAVAL_P(getThis()))->L; php_lua_send_zval_to_lua(L, value); #if (LUA_VERSION_NUM < 502) lua_setfield(L, LUA_GLOBALSINDEX, Z_STRVAL_P(name)); #else lua_setglobal(L, Z_STRVAL_P(name)); #endif RETURN_ZVAL(getThis(), 1, 0); } /* }}} */ /** {{{ proto Lua::registerCallback(string $name, mix $value) */ PHP_METHOD(lua, registerCallback) { char *name; size_t len; zval *func; lua_State *L; zval* callbacks; if (zend_parse_parameters(ZEND_NUM_ARGS(),"sz", &name, &len, &func) == FAILURE) { return; } L = (Z_LUAVAL_P(getThis()))->L; callbacks = zend_read_static_property(lua_ce, ZEND_STRL("_callbacks"), 1); if (ZVAL_IS_NULL(callbacks)) { array_init(callbacks); } if (zend_is_callable(func, 0, NULL)) { 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, "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); } /* }}} */ /** {{{ proto Lua::__construct() */ PHP_METHOD(lua, __construct) { lua_State * L = (Z_LUAVAL_P(getThis()))->L; 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); REGISTER_LONG_CONSTANT("LUA_OK", LUA_OK, CONST_PERSISTENT | CONST_CS); REGISTER_LONG_CONSTANT("LUA_YIELD", LUA_YIELD, CONST_PERSISTENT | CONST_CS); REGISTER_LONG_CONSTANT("LUA_ERRRUN", LUA_ERRRUN, CONST_PERSISTENT | CONST_CS); REGISTER_LONG_CONSTANT("LUA_ERRSYNTAX", LUA_ERRSYNTAX, CONST_PERSISTENT | CONST_CS); REGISTER_LONG_CONSTANT("LUA_ERRMEM", LUA_ERRMEM, CONST_PERSISTENT | CONST_CS); #ifdef LUA_ERRGCMM REGISTER_LONG_CONSTANT("LUA_ERRGCMM", LUA_ERRGCMM, CONST_PERSISTENT | CONST_CS); #endif REGISTER_LONG_CONSTANT("LUA_ERRERR", LUA_ERRERR, CONST_PERSISTENT | CONST_CS); REGISTER_LONG_CONSTANT("LUA_ERRFILE", LUA_ERRFILE, CONST_PERSISTENT | CONST_CS); lua_ce = zend_register_internal_class(&ce); lua_ce->create_object = php_lua_create_object; memcpy(&lua_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); lua_object_handlers.offset = XtOffsetOf(php_lua_object, obj); lua_object_handlers.dtor_obj = php_lua_dtor_object; lua_object_handlers.free_obj = php_lua_free_object; lua_object_handlers.clone_obj = NULL; 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); zend_declare_class_constant_string(lua_ce, ZEND_STRL("LUA_VERSION"), LUA_RELEASE); php_lua_closure_register(); INIT_CLASS_ENTRY(ce, "LuaException", NULL); lua_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default()); 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 */