用Php扩展实现的简单框架 - 4

kiss_inner.c: (代码太长,前半部分)


#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;

}

 

你可能感兴趣的:(PHP,框架,function,扩展,Path,Zend)