【PECL】在扩展中实现 autoload

【PECL】在扩展中实现 autoload

摘要

php-8.3.x 用扩展写个框架。想实现类管理器,自动加载,上代码:

PHP代码想这么写

php 复制代码
$ws = new \Ziima\Applet();
$ws->import('Ziima', '../base/core');
$ws->runAutoload();

$t = new \Ziima\Template\Context();
print_r($t);

C 代码这么实现

没有文档,硬翻源代码一点点抠出来这么个思路,能用。

c 复制代码
ZEND_METHOD(Ziima_Applet, runAutoload) {
    zval *this       = getThis();
    zend_string *req = NULL;

    ZEND_PARSE_PARAMETERS_START(0, 1)
    Z_PARAM_OPTIONAL
    Z_PARAM_STR(req)
    ZEND_PARSE_PARAMETERS_END();

    //[1] 没有参数 , 或者 this._auto 未设置,判断是PHP调用 runAutoload 来注册
    zval *_auto = zend_read_property(ce_Ziima_Applet, Z_OBJ_P(this), ZEND_STRL("_auto"), true, NULL);
    if (_auto == NULL || Z_TYPE_P(_auto) != IS_OBJECT) {
        zval           cu, par_r;
        zend_function *fn = zend_hash_str_find_ptr_lc(&Z_OBJCE_P(this)->function_table, ZEND_STRL("runAutoload"));
        zend_create_closure(&cu, fn, ce_Ziima_Applet, ce_Ziima_Applet, this);

        zend_fcall_info fci;
        fci.size         = sizeof(zend_fcall_info);
        fci.object       = NULL;
        fci.named_params = NULL;
        fci.retval       = &par_r;
        fci.param_count  = 3;
        fci.params       = ecalloc(sizeof(zval), 3);
        ZVAL_COPY_VALUE(fci.params, &cu);
        ZVAL_TRUE(fci.params + 1);
        ZVAL_TRUE(fci.params + 2);
        ZVAL_STRING(&fci.function_name, "spl_autoload_register");
        if (zend_call_function(&fci, NULL) != SUCCESS) {
            zval_delref_p(&cu);
            RETURN_FALSE;
        }
        if (Z_TYPE(par_r) != IS_TRUE) {
            zval_delref_p(&cu);
            RETURN_FALSE;
        }
        zend_update_property(ce_Ziima_Applet, Z_OBJ_P(this), ZEND_STRL("_auto"), &cu);
        RETURN_TRUE;
    }

    // [2] 有参数进来,则认为是被 spl_autoload_register 调用的
    if (req == NULL) {
        RETURN_FALSE;
    }

    zval *_vendor = zend_read_property(ce_Ziima_Applet, Z_OBJ_P(this), ZEND_STRL("_vendor"), true, NULL);
    if (_vendor == NULL || Z_TYPE_P(_vendor) != IS_ARRAY)
        RETURN_FALSE;
    if (zend_array_count(Z_ARR_P(_vendor)) < 1)
        RETURN_FALSE;
    // [3] 类加载器只能处理有命名空间的
    if (!nstr_contains(req->val, req->len, '\\'))
        RETURN_FALSE;
    vector_object *vreq   = vector_split_ex(req->val, req->len, '\\', VCONF_AUTO_TRIM | VCONF_DENY_EMPTY);
    vector_item   *ve     = vector_first(vreq);
    zend_string   *ns_req = zend_string_init(ve->key.val, ve->key.len, false);
    zend_string   *dp_req = NULL;
    zend_long      h      = 0;
    zend_string   *k      = NULL;
    zval          *v      = NULL;
    // [4] 找登记路径
    ZEND_HASH_MAP_FOREACH_KEY_VAL(Z_ARR_P(_vendor), h, k, v)
    if (k->len != ns_req->len)
        continue;
    if (strncasecmp(k->val, ns_req->val, ns_req->len) == 0) {
        dp_req = zend_string_dup(Z_STR_P(v), false);
        break;
    }
    ZEND_HASH_FOREACH_END();
    if (dp_req == NULL) {
        vector_destroy(vreq);
        zend_string_release(ns_req);
        RETURN_FALSE;
    }

    // [5] 拼路径
    buf_string tmp;
    bstr_init(&tmp, dp_req->len + req->len + 64);

    bstr_append(&tmp, dp_req->val);
    if (tmp.val[dp_req->len - 1] == '/') {
        tmp.val[dp_req->len - 1] = 0;
        tmp.len--;
    }
    VECTOR_FOREACH_BEGIN(vreq, h, ve)
    if (h == 1)
        continue;
    bstr_append_char(&tmp, '/');
    bstr_append_ex(&tmp, ve->key.val, ve->key.len);
    VECTOR_FOREACH_END();
    vector_destroy(vreq);
    bstr_append(&tmp, ".php");

    // [6] 查文件
    zend_string_release(ns_req);
    zend_string_release(dp_req);
    if (access(tmp.val, F_OK) != 0) {
        bstr_free(&tmp);
        RETURN_FALSE;
    }
    // kdebug("load %s from %s", ns_req->val, tmp.val);
    // [7] 用 eval + include_once 包含
    bstr_append(&tmp, "');");
    const char *ps = "include_once('";
    uint32_t    ls = strlen(ps);
    memmove(tmp.val + ls, tmp.val, tmp.len);
    memcpy(tmp.val, ps, ls);
    if (zend_eval_string(tmp.val, NULL, "ziima_autoload_script") != SUCCESS) {
        bstr_free(&tmp);
        RETURN_FALSE;
    }
    bstr_free(&tmp);
    RETURN_TRUE;
}
相关推荐
YUJIANYUE43 分钟前
PHP将指定文件夹下多csv文件[即多表]导入到sqlite单文件
jvm·sqlite·php
龙哥·三年风水10 小时前
群控系统服务端开发模式-应用开发-个人资料
分布式·php·群控系统
Dingww101114 小时前
梧桐数据库中的网络地址类型使用介绍分享
数据库·oracle·php
Genius Kim18 小时前
SpringCloud Sentinel 服务治理详解
spring cloud·sentinel·php
原机小子21 小时前
城镇保障性住房管理:SpringBoot系统解决方案
数据库·spring boot·php
kali-Myon1 天前
NewStarCTF2024-Week5-Web&Misc-WP
前端·python·学习·mysql·web安全·php·web
DK七七1 天前
当今陪玩系统小程序趋势,陪玩系统源码搭建后的适用于哪些平台
小程序·php·uniapp
tekin1 天前
vscode php Launch built-in server and debug, PHP内置服务xdebug调试,自定义启动参数配置使用示例
ide·vscode·php·launch.json·runtimeargs·php内置服务自定义参数
The_Ticker1 天前
PHP查询实时股票行情
开发语言·php·学习方法
y0ungsheep2 天前
CTF中的phar反序列化 [SWPU 2018]SimplePHP
运维·web安全·网络安全·php·代码规范