【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;
}
相关推荐
黑客Ela几秒前
网络安全中常用浏览器插件、拓展
网络·安全·web安全·网络安全·php
蝶开三月2 小时前
php:使用socket函数创建WebSocket服务
网络·websocket·网络协议·php·socket
Ai 编码助手3 小时前
使用php和Xunsearch提升音乐网站的歌曲搜索效果
开发语言·php
wkj0015 小时前
php操作redis
开发语言·redis·php
黑客Ash14 小时前
【D01】网络安全概论
网络·安全·web安全·php
->yjy14 小时前
计算机网络(第一章)
网络·计算机网络·php
阳光帅气男孩15 小时前
PhpSpreadsheet导出图片
php
周全全16 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
Mr.Pascal17 小时前
刚学php序列化/反序列化遇到的坑(攻防世界:Web_php_unserialize)
开发语言·安全·web安全·php