首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 擴展開發:bloader 自動載入器 Zend API 實作分析

     編輯:PHP 綜合知識

專案簡介

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');
?>

Zend API 核心實作解析 (節錄)

1. 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);

2. 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;
}

3. 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;
}

4. 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

適用場景與現代替代

  • **舊專案 (PHP 5.2/5.3)** 無 Composer、無命名空間、大量 require_once 時,bloader 可大幅簡化載入邏輯。
  • **現代專案 (PHP 7.1+)**:**強烈建議使用 Composer PSR-4 自動載入**,標準化、生態完整、效能足夠、無需編譯擴展。
  • **極高效能需求** (單機百萬 QPS):可考慮預載入 opcache.preload (PHP 7.4+) 或 Swoole 常駐記憶體,而非開發 Zend 擴展。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved