【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;
}