#ifndef PHP_KISS_INNER_C #define PHP_KISS_INNER_C
// #include "php_kiss.h"
struct _kiss_status { zend_bool prefix_ctl; // 1,是否给控制器类添加模块名作为前缀
// zend_bool prefix_ls; // 0,是否给监听器类添加模块名作为前缀
zend_bool share_gp; // 1,_GET和_POST是否共享
zend_bool use_port; // 0,域名带端口
// zend_bool throw_ex; // 1,是否立刻抛出异常
zend_bool use_cache; // 0,是否缓存
uint dispatch_type; // KISS_DISPATCH_METHOD
char * cli_root; // 项目根目录
char * ext_proc; // PHP_EXT,程序文件扩展名
char * ext_path; // NULL,URL扩展名
char * default_mdl; // DEFAULT_MODULE,缺省模块
char * default_ctl; // DEFAULT_CONTROLLER,缺省控制器
char * default_act; // DEFAULT_ACTION,缺省Action
char * postfix_ctl; // "Controller",控制器名后缀
char * postfix_act; // "Action",Action名后缀
};
/* enum _kiss_req_mtd { head, get, post }; */
struct _kiss_request { // 请求的信息
// kiss_req_mtd method;
char * method; char * host; char * module; char * controller; char * action; zval * query; // $_GET拷贝和URL模式中解析出的参数
zval * post; // $_POST拷贝
char *fragment; };
struct _kiss_rule { // 分发的规则
char * controller_dir; // char * view_dir;
};
static inline kiss_status * kiss_status_init() { kiss_status * ks; ks = (kiss_status*)ecalloc(1, sizeof(kiss_status)); if(!ks) return NULL;
ks->prefix_ctl = DEFAULT_PREFIX_CTL; ks->share_gp = DEFAULT_SHARE_GP; ks->use_port = DEFAULT_USE_PORT; ks->use_cache = DEFAULT_USE_CACHE; ks->dispatch_type = DEFAULT_DISPATCH_TYPE;
ks->cli_root = NULL; ks->ext_proc = estrdup(PHP_EXT); ks->ext_path = NULL; ks->default_mdl = estrdup(DEFAULT_MODULE); ks->default_ctl = estrdup(DEFAULT_CONTROLLER); ks->default_act = estrdup(DEFAULT_ACTION); ks->postfix_ctl = estrdup(CLASS_EXT); ks->postfix_act = estrdup(ACTION_EXT); return ks; }
static inline void kiss_status_free(kiss_status * ks) { if(!ks) return; if(ks->cli_root) efree(ks->cli_root); if(ks->ext_proc) efree(ks->ext_proc); if(ks->ext_path) efree(ks->ext_path); if(ks->default_mdl) efree(ks->default_mdl); if(ks->default_ctl) efree(ks->default_ctl); if(ks->default_act) efree(ks->default_act); if(ks->postfix_ctl) efree(ks->postfix_ctl); if(ks->postfix_act) efree(ks->postfix_act); efree(ks); }
/** 也有助于分离代码(sizeof的缘故)。 * TODO: method的大小写? * TODO: 包装is_ref;terminal下的$_GET和$_POST的共享。 */ static inline zend_bool kiss_request_init(char * uri, char ** path TSRMLS_DC) { kiss_request * req; kiss_status * status; php_url * url; char * host; zval ** get = NULL, ** post = NULL;
req = (kiss_request*)ecalloc(1, sizeof(kiss_request)); if(!req) return 0; status = KISS_G(status);
if(uri) { // CLI
req->method = "GET"; } else { req->method = zend_getenv(REQUEST_METHOD, REQUEST_METHOD_LEN TSRMLS_CC); // for apache2, sapi_getenv
uri = zend_getenv(REQUEST_URI, REQUEST_URI_LEN TSRMLS_CC); } req->method = estrdup(req->method);
url = php_url_parse(uri); *path = estrdup(url->path ? url->path : "/"); if(url->fragment) req->fragment = estrdup(url->fragment);
// SERVER_NAME
req->host = (url->host ? url->host : zend_getenv(HTTP_HOST, HTTP_HOST_LEN TSRMLS_CC)); if(status->use_port && req->host) { host = req->host; if(!strchr(host, ':')) { spprintf(&(req->host), 0, "%s:%d", host, url->port); // efree(host);
} } else if(!status->use_port && req->host) { host = req->host; // 过滤端口号
while('/0' != *host && ':' != *host) host++; if(':' == *host) *host = '/0'; req->host = estrndup(req->host, host-req->host); }
// if(url->query)
// if(!kiss_parse_query(url->query, &req)) return 0;
zend_hash_find(&EG(symbol_table), _GET, _GET_SIZE, (void**)&get); /** 对于terminal,$_GET和$_POST为空数组,不加判断产生内存泄露。 */ if(get && Z_TYPE_PP(get)==IS_ARRAY /*&& zend_hash_num_elements(Z_ARRVAL_PP(get))*/) { req->query = *get; if(status->share_gp) { zval_ptr_dtor(&(req->query)); req->query->is_ref = 1; ZVAL_ADDREF(req->query); } } else { MAKE_STD_ZVAL(req->query); array_init(req->query); }
zend_hash_find(&EG(symbol_table), _POST, _POST_SIZE, (void**)&post); if(post && Z_TYPE_PP(post)==IS_ARRAY /*&& zend_hash_num_elements(Z_ARRVAL_PP(post))*/) { req->post = *post; if(status->share_gp) { zval_ptr_dtor(&(req->post)); req->post->is_ref = 1; ZVAL_ADDREF(req->post); } } else { MAKE_STD_ZVAL(req->post); array_init(req->post); }
KISS_G(request) = req; php_url_free(url);
return 1; }
/** NOTE: 需要根据kiss_request的结构修改该函数。 */ static void kiss_request_free(kiss_request * req) { if(!req) return; if(req->method) efree(req->method); if(req->host) efree(req->host); if(req->module) efree(req->module); if(req->controller) efree(req->controller); if(req->action) efree(req->action); if(req->query) zval_ptr_dtor(&(req->query)); if(req->post) zval_ptr_dtor(&(req->post)); if(req->fragment) efree(req->fragment); efree(req); }
/** 输出请求结构的内容。 * NOTE: 需要根据kiss_request的结构修改该函数。 */ static void kiss_request_dump(kiss_request * req TSRMLS_DC) { if(!req) return; #define KISS_REQUEST_DUMP_PREFIX "/n " /*char * mtd = (head == req->method ? "HEAD" : (get == req->method ? "GET" : "POST"));*/ php_printf("kiss_request: {"); if(req->method) php_printf(KISS_REQUEST_DUMP_PREFIX "method: %s", req->method/*mtd*/); if(req->host) php_printf(KISS_REQUEST_DUMP_PREFIX "host: %s", req->host); if(req->module) php_printf(KISS_REQUEST_DUMP_PREFIX "module: %s", req->module); if(req->controller) php_printf(KISS_REQUEST_DUMP_PREFIX "controller: %s", req->controller); if(req->action) php_printf(KISS_REQUEST_DUMP_PREFIX "action: %s", req->action); if(req->fragment) php_printf(KISS_REQUEST_DUMP_PREFIX "fragment: %s", req->fragment); if(req->query) { php_printf(KISS_REQUEST_DUMP_PREFIX "query: "); php_var_dump(&(req->query), 3 TSRMLS_CC); } if(req->post) { php_printf(" post: "); php_var_dump(&(req->post), 3 TSRMLS_CC); } php_printf("}"); }
/** 在数组(container)中查找某个键(key),返回其值(ret)。 * 如key==NULL则返回container;*ret可以预先初始化,其结果值可能为数组或其他。 * NOTE: 根据函数kiss_request_params的需要,不可破坏传入的*ret数组中的数据。 */ static zend_bool kiss_request_query(char * key, zval * container, zval ** ret TSRMLS_DC) { zval ** value = NULL;
if(!ret || IS_ARRAY != Z_TYPE_P(container)) return 0; if(key && zend_hash_find(Z_ARRVAL_P(container), key, strlen(key)+1, (void**)&value) == FAILURE) { return 0; }
if(!*ret) MAKE_STD_ZVAL(*ret);
if(!key) { // ZVAL_ADDREF(container);
// zval_ptr_dtor(&return_value);
// return_value = container; // RSHUTDOWN中释放了req->query,所以无法输出到页面???
if(IS_ARRAY != Z_TYPE_PP(ret)) array_init(*ret); php_array_merge(Z_ARRVAL_P(*ret), Z_ARRVAL_P(container), 0 TSRMLS_CC); } else if(IS_ARRAY == Z_TYPE_PP(value)) { if(IS_ARRAY != Z_TYPE_PP(ret)) array_init(*ret); php_array_merge(Z_ARRVAL_P(*ret), Z_ARRVAL_PP(value), 0 TSRMLS_CC); } else { **ret = **value; zval_copy_ctor(*ret); } return 1; }
/** NOTE: 需要根据kiss_response的结构修改该函数。 */ // static void kiss_response_free(kiss_response * resp) { }
/** 用于HashTable。*/ static inline void kiss_rule_free(kiss_rule * rule) { pefree(rule->controller_dir, 1); }
/** 用于HashTable;注意:可能返回NULL。 * TODO: 构建绝对路径? */ static inline kiss_rule * kiss_make_rule_value(char * ctl_dir, uint ctl_dir_len) { char * pctl; kiss_rule * rule;
if(!ctl_dir || !ctl_dir_len) return NULL; rule = (kiss_rule*)emalloc(sizeof(kiss_rule)); if(!rule) return NULL; pctl = (char*)pemalloc(ctl_dir_len+1, 1); if(!pctl) { efree(rule); return NULL; } memcpy(pctl, ctl_dir, ctl_dir_len+1); rule->controller_dir = pctl;
return rule; }
/** 仅用于在程序入口设置路由。 */ static zend_bool kiss_set_rule(char *mdl_name, char *ctl_dir, uint ctl_dir_len TSRMLS_DC) { char * domain = NULL, * key; kiss_request * req; kiss_rule * rule; uint dm_len, key_len; if(!ctl_dir || !ctl_dir_len) return 0; if(!mdl_name) mdl_name = "";
req = KISS_G(request); if(req) domain = req->host; if(!domain) domain = zend_getenv(HTTP_HOST, HTTP_HOST_LEN TSRMLS_CC); dm_len = strlen(domain);
// if(!kiss_make_rule_key(domain, dm_len, mdl_name, mdl_len, &key, &key_len)) return 0;
key_len = spprintf(&key, 0, RW_PREFIX ":%s/%s", domain, mdl_name);
rule = kiss_make_rule_value(ctl_dir, ctl_dir_len); if(!rule) { efree(key); return 0; }
zend_hash_update(&KISS_G(hosts_rw), key, key_len+1, (void *)rule, sizeof(kiss_rule), NULL);
efree(key); efree(rule); return 1; }
/** 用于HashTable。*/ static inline void kiss_ls_free(char * ls) { //pefree(*ls, 1);
}
/** 文件是否存在以及是否可读。 */ static inline zend_bool kiss_is_readable(char * path) {
FILE * fp; if(!path || '/0'==*path) return 0; fp = fopen(path, "r"); if(fp) fclose(fp); return fp != 0; }
/** path非绝对路径且abs_path非空则设之为绝对路径(据DOC_ROOT)。 */ static zend_bool kiss_absolute_path(char * path, char ** abs_path TSRMLS_DC) { char * root; if(IS_ABSOLUTE_PATH(path, strlen(path))) return 1; if(abs_path) { root = zend_getenv(DOC_ROOT, DOC_ROOT_LEN TSRMLS_CC); if(!root || '/0'==*root) root = KISS_G(status)->cli_root; if(root) spprintf(abs_path, 0, "%s%c%s", root, KISS_G(dir_sep), path); } return 0; }
/** 类名为空也返回0。 */ static zend_bool kiss_class_exists(char * name TSRMLS_DC) { char * lc_name; uint name_len, found; zend_class_entry **ce;
if(!name || '/0'==*name) return 0; name_len = strlen(name); lc_name = (char*)do_alloca(name_len + 1); zend_str_tolower_copy(lc_name, name, name_len);
found = zend_hash_find(EG(class_table), lc_name, name_len+1, (void **) &ce); free_alloca(lc_name); return (found == SUCCESS && !((*ce)->ce_flags & ZEND_ACC_INTERFACE)); }
/** 类名为空也返回0。 * TODO: 判断是否接口或抽象类? */ static zval * kiss_create_object(char * name TSRMLS_DC) { zend_class_entry ** ce; uint name_len; zval * ins, * func, * ret;
if(!name || '/0'==*name) return NULL; name_len = strlen(name); if (zend_lookup_class(name, name_len, &ce TSRMLS_CC) == SUCCESS) { // return (((*ce)->ce_flags & ZEND_ACC_INTERFACE) == 0);
MAKE_STD_ZVAL(ins); if(object_init_ex(ins, *ce) == SUCCESS) { MAKE_STD_ZVAL(func); MAKE_STD_ZVAL(ret); ZVAL_STRINGL(func, "__construct", sizeof("__construct")-1, 0); kiss_call_user_function(NULL, &ins, func, ret, 0, NULL TSRMLS_CC); efree(func); efree(ret); return ins; } else { zval_ptr_dtor(&ins); return NULL; } } else { return NULL; } }
/** 返回FAILURE或SUCCESS,如果之前有异常产生则立刻返回FAILURE。 * TODO: 如何处理调用前产生的异常?输出?记入日志? */
static int kiss_call_user_function( HashTable *function_table, zval **object_pp, zval *function_name, zval *retval_ptr, zend_uint param_count, zval *params[] TSRMLS_DC) { char * cls = NULL, * tip;
if(object_pp) cls = Z_OBJCE_PP(object_pp)->name;
if(EG(exception)) { /*if(cls) spprintf(&tip, 0, "调用%s::%s()方法前有异常发生,因此无法正确执行。", cls, Z_STRVAL_P(function_name)); else spprintf(&tip, 0, "调用函数%s()前有异常发生,因此无法正确执行。", Z_STRVAL_P(function_name)); zend_error(E_WARNING, tip); // E_ERROR: 终止运行 efree(tip);*/
return FAILURE;
}
if(FAILURE == call_user_function(function_table, object_pp, function_name, retval_ptr, param_count, params TSRMLS_CC)) { // 如果call_user_function抛出异常,则以下的异常被忽略。
if(cls) zend_throw_exception_ex(NULL, KISS_E_CALLMTD TSRMLS_CC, "调用%s::%s()方法失败。", cls, Z_STRVAL_P(function_name)); else zend_throw_exception_ex(NULL, KISS_E_CALLFUNC TSRMLS_CC, "调用函数%s()失败。", Z_STRVAL_P(function_name)); return FAILURE; } return SUCCESS;
}
/** 文件不存在则返回0。 */
static zend_bool kiss_eval(long type, zval * source, zval ** ret TSRMLS_DC) { char * file_name; uint file_name_len;
zval ** original_return_value, * tmp_ret = NULL;
zend_bool failure_retval = 0;
zend_op_array * new_op_array = NULL; zend_execute_data execute_data; file_name = Z_STRVAL_P(source); file_name_len = Z_STRLEN_P(source); if(!kiss_is_readable(file_name)) return 0; original_return_value = EG(return_value_ptr_ptr); execute_data = * EG(current_execute_data);
switch (type) {
case KISS_INCLUDE_ONCE:
case KISS_REQUIRE_ONCE: {
zend_file_handle file_handle;
if (IS_ABSOLUTE_PATH(file_name, file_name_len)) {
cwd_state state;
state.cwd_length = 0;
state.cwd = (char*)malloc(1);
state.cwd[0] = 0;
failure_retval = (!virtual_file_ex(&state, file_name, NULL, 1) &&
zend_hash_exists(&EG(included_files), state.cwd, state.cwd_length+1));
free(state.cwd);
}
if (failure_retval) {
/* do nothing */
} else if (SUCCESS == zend_stream_open(file_name, &file_handle TSRMLS_CC)) {
if (!file_handle.opened_path) {
file_handle.opened_path = estrndup(file_name, file_name_len);
}
if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1)==SUCCESS) {
new_op_array = zend_compile_file(&file_handle, (type==KISS_INCLUDE_ONCE?KISS_INCLUDE:KISS_REQUIRE) TSRMLS_CC);
zend_destroy_file_handle(&file_handle TSRMLS_CC);
} else {
zend_file_handle_dtor(&file_handle);
failure_retval=1;
}
} else {
if (type==KISS_INCLUDE_ONCE) {
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_name);
} else {
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_name);
}
}
}
break;
case KISS_INCLUDE:
case KISS_REQUIRE:
new_op_array = compile_filename(type, source TSRMLS_CC);
break;
case KISS_EVAL: {
char *eval_desc = zend_make_compiled_string_description("eval()'d code" TSRMLS_CC);
new_op_array = zend_compile_string(source, eval_desc TSRMLS_CC);
efree(eval_desc);
}
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
if (new_op_array) {
zval *saved_object;
zend_function *saved_function;
EG(return_value_ptr_ptr) = &tmp_ret;
EG(active_op_array) = new_op_array;
saved_object = EX(object);
saved_function = EX(function_state).function;
EX(function_state).function = (zend_function *) new_op_array;
EX(object) = NULL;
zend_execute(new_op_array TSRMLS_CC);
EX(function_state).function = saved_function;
EX(object) = saved_object;
if (!ret) {
if (tmp_ret) {
zval_ptr_dtor(&tmp_ret);
}
} else { /* return value is used */
if (!tmp_ret) { /* there was no return statement */
ALLOC_ZVAL(tmp_ret);
INIT_PZVAL(tmp_ret);
Z_LVAL_P(tmp_ret) = 1;
Z_TYPE_P(tmp_ret) = IS_BOOL;
} *ret = tmp_ret;
}
EG(active_op_array) = EX(op_array);
EG(function_state_ptr) = &EX(function_state);
destroy_op_array(new_op_array TSRMLS_CC);
efree(new_op_array);
if (EG(exception)) { kiss_e_throw(NULL, KISS_E_EVAL); // 显示实际文件路径
}
} else {
if (ret) {
ALLOC_ZVAL(tmp_ret);
INIT_ZVAL(*tmp_ret);
Z_LVAL_P(tmp_ret) = failure_retval;
Z_TYPE_P(tmp_ret) = IS_BOOL; *ret = tmp_ret;
}
}
EG(return_value_ptr_ptr) = original_return_value; return 1;
}
|