autoload在php中其實是一個魔術方法了,我們可以指定類目錄及規則可以自動加載類文件從而可以省去我們使用include來加載文件了,下面一直來看看關于autoload方法一些例子.
我們在寫web應用程序時通常對每個類都建立一個 PHP 源文件,為了使用這些源文件,我們就需要在每個腳本開頭寫大量的的包含語句(include,require),在 PHP 5 中,不再需要這樣了,我們可__autoload()函數和spl_autoload_register函數實現實現自己的加載源文件的機制,它們會在試圖使用尚未被定義的類時自動調用.
通過調用這些函數,腳本引擎在 PHP 出錯失敗前有了最后一個機會加載所需的類,本文的主要目標是講述如何在擴展中用C語言實現自動加載源文件的機制,但是在這之前我們先熟悉一下在PHP腳本中實現自動加載的方法.
在腳本中實現自動加載
在 PHP 5 中我們可以定義一個 __autoload() 函數,它會在試圖使用尚未被定義的類時自動調用,這樣我們就可以定義一些自己的加載規則了.
- <?php
- function __autoload($class_name) {
- require_once $class_name . '.php';
- }
- $obj = new MyClass1();
- $obj2 = new MyClass2();
- ?>
使用spl_autoload_register我們可以一次注冊多個加載函數,PHP會在試圖使用尚未被定義的類時按注冊順序調用.
- <?php
- function autoload_services($class_name)
- {
- $file = 'services/' . $class_name. '.php';
- if (file_exists($file))
- {
- require_once($file);
- }
- }
- function autoload_vos($class_name)
- {
- $file = 'vos/' . $class_name. '.php';
- if (file_exists($file))
- {
- require_once($file);
- }
- }
- spl_autoload_register('autoload_services');
- spl_autoload_register('autoload_vos');
- ?>
在php擴展中實現自動加載
最近在寫一個php擴展,其中一個功能就是實現類的自動加載,其實也是通過在內核中調用spl_autoload_register函數來實現,使用zend API調用spl_autoload_register函數還是相對簡單的,下面我們主要講一下如何在內核中實現inclue/require/include_once/require_once等指令的功能,其實inclue/require/include_once/require_once等指令主要是讀入文件編譯并執行,下面的方法就是完成了這些操作,代碼中有詳細的注釋.
- /*
- * loader_import首先將PHP源文件編譯成op_array,然后依次執行op_array中的opcode
- */
- int loader_import(char *path, int len TSRMLS_DC) {
- zend_file_handle file_handle;
- zend_op_array *op_array;
- char realpath[MAXPATHLEN];
- if (!VCWD_REALPATH(path, realpath)) {
- return 0;
- }
- file_handle.filename = path;
- file_handle.free_filename = 0;
- file_handle.type = ZEND_HANDLE_FILENAME;
- file_handle.opened_path = NULL;
- file_handle.handle.fp = NULL;
- //調用zend API編譯源文件
- op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
- if (op_array && file_handle.handle.stream.handle) {
- int dummy = 1;
- if (!file_handle.opened_path) {
- file_handle.opened_path = path;
- }
- //將源文件注冊到執行期間的全局變量(EG)的include_files列表中,這樣就標記了源文件已經包含過了
- zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy,
- sizeof(int), NULL);
- }
- zend_destroy_file_handle(&file_handle TSRMLS_CC);
- //開始執行op_array
- if (op_array) {
- zval *result = NULL;
- //保存原來的執行環境,包括active_op_array,opline_ptr等
- zval ** __old_return_value_pp = EG(return_value_ptr_ptr);
- zend_op ** __old_opline_ptr = EG(opline_ptr);
- zend_op_array * __old_op_array = EG(active_op_array);
- //保存環境完成后,初始化本次執行環境,替換op_array
- EG(return_value_ptr_ptr) = &result;
- EG(active_op_array) = op_array;
- #if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2)) || (PHP_MAJOR_VERSION > 5)
- if (!EG(active_symbol_table)) {
- zend_rebuild_symbol_table(TSRMLS_C);
- }
- #endif
- //調用zend API執行源文件的op_array
- zend_execute(op_array TSRMLS_CC);
- //op_array執行完成后銷毀,要不然就要內存泄露了,哈哈
- destroy_op_array(op_array TSRMLS_CC);
- efree(op_array);
- //通過檢查執行期間的全局變量(EG)的exception是否被標記來確定是否有異常
- if (!EG(exception)) {
- if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) {
- zval_ptr_dtor(EG(return_value_ptr_ptr));
- } //開源軟件:Vevb.com
- }
- //ok,執行到這里說明源文件的op_array已經執行完成了,我們要恢復原來的執行環境了
- EG(return_value_ptr_ptr) = __old_return_value_pp;
- EG(opline_ptr) = __old_opline_ptr;
- EG(active_op_array) = __old_op_array;
- return 1;
- }
- return 0;
- }
新聞熱點
疑難解答