bloader 是一個基於 Zend Engine C API 開發的 PHP 擴展,提供比 spl_autoload_register 更簡單、高效、靈活的類別自動載入與物件管理功能。核心函數:ld() (載入/取得單例)、ld_new() (建立新實例)、ld_unset() (銷毀)。
./ClassName.class.php、_MODULES 常數定義路徑。ld('ClassName') 回傳單例物件,並自動在當前符號表註冊 $ClassName 變數。ld('User') 取得的物件,外部也能直接用 $User 存取。ld_new() 建立多個獨立實例不註冊變數。ld_unset() 移除符號表註冊與實例。<?php
// 可選:定義模組搜尋路徑
define('_MODULES', __DIR__ . '/class');
// 1. 取得/建立單例並操作
ld('User', ['arg1', 'arg2'])->name = 'John'; // 建構子傳參
ld('User')->save(); // 同一實例
// 2. 自動註冊 $User 變數
ld('User');
$User->method(); // 直接使用
// 3. 函數間共享
function doSomething() {
var_dump(ld('User')); // 取得同一單例
}
doSomething();
// 4. 建立多個獨立實例
$user1 = ld_new('User');
$user2 = ld_new('User');
// 5. 銷毀
ld_unset('User');
?>
ld() - 取得或建立單例// PHP_FUNCTION(ld) char *obj_name; int slen; zval **var, *para = NULL; zend_parse_parameters(ZEND_NUM_ARGS(), "s|z", &obj_name, &slen, ¶); zval_dtor(return_value); // 1. 檢查符號表是否已存在 if (zend_hash_find(&EG(symbol_table), obj_name, slen+1, (void**)&var) != SUCCESS) { // 2. 不存在:自動載入類別檔案 -> 實例化 -> 存入符號表 ld_autoload_path(obj_name); *return_value = *ld_new_class(obj_name, slen, para, 1); // is_set=1 註冊 $obj_name } else { // 3. 已存在:直接回傳 *return_value = **var; } zval_copy_ctor(return_value);
ld_new_class() - 實例化邏輯static zval *ld_new_class(char *obj_name, int slen, zval *para, int is_set) { zval *obj; zend_class_entry **ce; zend_function *ctor; MAKE_STD_ZVAL(obj); // 查找類別項目 if (zend_lookup_class(obj_name, slen, &ce TSRMLS_CC) == SUCCESS) { object_init_ex(obj, *ce); // 建立物件 ctor = Z_OBJ_HT_P(obj)->get_constructor(obj TSRMLS_CC); if (ctor) { int is_arg = para ? 1 : 0; zend_call_method(&obj, *ce, &ctor, "__construct", 11, NULL, is_arg, para, NULL TSRMLS_CC); } if (is_set) ZEND_SET_SYMBOL(&EG(symbol_table), obj_name, obj); // 註冊 $ClassName } else { ZVAL_FALSE(obj); } return obj; }
ld_autoload_path() - 自動載入路徑解析static int ld_autoload_path(char *class_name TSRMLS_DC) { char *file_path; spprintf(&file_path, 0, "%s.class.php", class_name); if (ld_autoload_file(file_path) == SUCCESS) return SUCCESS; // 嘗試 _MODULES 常數路徑 zval const_root; if (zend_get_constant("_MODULES", 8, &const_root TSRMLS_CC) && Z_TYPE(const_root) == IS_STRING) { char *root_path; spprintf(&root_path, 0, "%s/%s", Z_STRVAL(const_root), file_path); return ld_autoload_file(root_path); } return FAILURE; }
ld_autoload_file() - 編譯並執行 PHP 檔案static int ld_autoload_file(char *file_path, int len TSRMLS_DC) { zend_file_handle fh; if (php_stream_open_for_zend_ex(file_path, &fh, ENFORCE_SAFE_MODE|USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC) == SUCCESS) { zend_op_array *op = zend_compile_file(&fh, ZEND_REQUIRE TSRMLS_CC); zend_destroy_file_handle(&fh TSRMLS_CC); if (op) { zval *result; EG(return_value_ptr_ptr) = &result; EG(active_op_array) = op; if (!EG(active_symbol_table)) zend_rebuild_symbol_table(TSRMLS_C); zend_execute(op TSRMLS_CC); // 執行腳本,定義類別 destroy_op_array(op TSRMLS_CC); efree(op); return SUCCESS; } } return FAILURE; }
phpize ./configure --with-php-config=/usr/bin/php-config --enable-bloader make && sudo make install # php.ini 加入 extension=bloader.so
require_once 時,bloader 可大幅簡化載入邏輯。opcache.preload (PHP 7.4+) 或 Swoole 常駐記憶體,而非開發 Zend 擴展。