/* +----------------------------------------------------------------------+ | 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 324348 2012-03-19 03:12:15Z 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 "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) { #endif zval *retval = NULL; lua_State *L = NULL; zval *tmp_member = NULL; #if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 3)) (void)key; #endif 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); #if (LUA_VERSION_NUM < 502) lua_getfield(L, LUA_GLOBALSINDEX, Z_STRVAL_P(member)); #else lua_getglobal(L, Z_STRVAL_P(member)); #endif 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) { #endif lua_State *L = NULL; zval *tmp_member = NULL; #if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 3)) (void)key; #endif 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); #if (LUA_VERSION_NUM < 502) 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); #else php_lua_send_zval_to_lua(L, value TSRMLS_CC); lua_setglobal(L, Z_STRVAL_P(member)); #endif 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; TSRMLS_FETCH(); order = lua_tonumber(L, lua_upvalueindex(1)); 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); #if (LUA_VERSION_NUM < 502) lua_setfield(L, LUA_GLOBALSINDEX, name); #else lua_setglobal(L, name); #endif 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 */