【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;
}
相关推荐
会Tk矩阵群控的小木10 小时前
云控系统在TikTok多账号管理中的核心应用与技术实现
开发语言·php·开源软件·个人开发·tk矩阵
傻啦嘿哟11 小时前
降低首字延迟(TTFB):专线节点与TCP Fast Open的配置
开发语言·php
介一安全11 小时前
【漏洞学习】PHP+Windows环境通用文件上传漏洞深度剖析
windows·web安全·php·文件上传·安全性测试
郝学胜-神的一滴17 小时前
系统设计 013:高并发系统缓存:从原理到实践全解析
java·开发语言·python·缓存·系统架构·php·软件构建
程序二次开发20 小时前
wordpress 文章页,文章分类,单页,woocommerc 产品页,分类页添加.html后缀
大数据·前端·html·php
M1582276905521 小时前
工业级 CAN 转以太网网关|SG-CANET-210/410,打通 CAN 与以太网,工业通信无边界
单片机·嵌入式硬件·php
热爱Liunx的丘丘人21 小时前
搭建一个 Web + 数据库系统(Nginx+PHP+MySQL)
数据库·nginx·php
AC赳赳老秦21 小时前
OpenClaw多Agent分工协作:按工作模块拆分Agent,实现全流程自动化闭环
java·大数据·数据库·python·自动化·php·openclaw
handler011 天前
【Linux】五种IO模型详解
linux·运维·服务器·c语言·网络·笔记·php
xingyuzhisuan1 天前
网络 Token 常见故障原理,基础排查科普
运维·服务器·网络·php