【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;
}
相关推荐
郑州光合科技余经理1 小时前
PHP技术栈:上门系统海外版开发与源码解析
java·开发语言·javascript·git·uni-app·php·uniapp
2501_915921431 小时前
Fiddler抓包工具详解,HTTPHTTPS调试、代理配置与接口分析实战教程
服务器·ios·小程序·fiddler·uni-app·php·webview
高级盘丝洞1 小时前
如何通过Powerlink协议读取PLC数据
开发语言·数据库·php
JaguarJack3 小时前
FrankenPHP 是否是 PHP 的未来?
后端·php
xixixi777773 小时前
解析常见的通信流量和流量分析
运维·开发语言·网络·安全·php·通信·流量
游戏开发爱好者83 小时前
Charles抓包工具怎么用 Charles抓包教程、网络调试技巧与HTTPS配置全流程
网络·ios·小程序·https·uni-app·php·webview
万岳软件开发小城3 小时前
在线教育系统源码开发指南:功能、架构、部署全解析(2025最新版)
架构·开源·php·在线教育系统源码·教育app开发·教育软件开发
万岳科技程序员小金3 小时前
在线教育系统源码选型对比:PHP、Java、Flutter、UniApp谁更适合?
java·uni-app·php·软件开发·在线教育系统源码·教育app开发·教育软件开发
catchadmin17 小时前
PHP Fiber 优雅协作式多任务
后端·php
DanB2417 小时前
Java(网络编程)
java·网络·php