原創:PHP內核研究之類的實現

聲明:本文爲斯人原創,全部爲作者一一分析得之,有不對的地方望賜教。

博客地址:PHP技術博客 在CSDN也會同步更新的哦.

歡迎轉載,轉載請註明出處

這幾天比較忙哦..沒有時間寫..週末了多寫幾篇吧.

目前大部分語言都支持類.
類是什麼?類就是面向對象,簡稱OOP.英文名字 Object Oriented Programming.
面向對象是什麼?是一種編程架構.
OOP的一條基本原則是計算機程序是由單個能夠起到子程序作用的單元或對象組合而成,OOP達到了軟件工程的三個目標:重用性、靈活性和擴展性.
因爲我們講的不是這裏只簡單描述,如果你還不知道什麼是類,什麼是面向對象..那麼這篇文章目前不適合你哦.
[php]
class Person{


};
[/php]
上面是創建一個PHP類.class是PHP的關鍵字.通過它我們就能找到Zend是如何創建類的.
unticked_class_declaration_statement:
                class_entry_type T_STRING extends_from
                        { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); }
                        implements_list
                        '{'
                                class_statement_list
                        '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
        |       interface_entry T_STRING
                        { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); }
                        interface_extends_list
                        '{'
                                class_statement_list
                        '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
;
class_entry_type:
                T_CLASS                 { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = 0; } 
        |       T_ABSTRACT T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; }
        |       T_FINAL T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_FINAL_CLASS; }
;


T_CLASS,T_ABSTRACT T_CLASS和T_FINAL 是PHP的三種類的模式
T_CLASS:是一個標準類.
T_ABSTRACT:是聲明一個抽象類
T_FINAL:聲明一個不容許繼承和擴展的類.
當然還有interface
他們定義在Zend/zend_complie.h的文件中
#define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS    0x10    //沒有聲明爲抽象,但是內部有抽象方法
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS    0x20   //抽象
#define ZEND_ACC_FINAL_CLASS                0x40  //Final
#define ZEND_ACC_INTERFACE                  0x80 //接口





這三個規則 記錄當前行,並設置類的類型.
在定義類的時候調用了 zend_do_begin_class_declaration和zend_do_end_class_declaration兩個方法,
類的關鍵字 ,類的名稱和所繼承的父類作爲參數傳遞給這兩個函數.
zend_do_begin_class_declaration是用來聲明類,設置類型,創建一個
zend_do_end_class_declaration用來處理類中的屬性及方法.
在講到兩個函數之前一定先要說說 保存類的結構zend_class_entry
它定義在Zend/zend.h中
struct _zend_class_entry {
        char type;
        char *name;//類名稱
        zend_uint name_length;
        struct _zend_class_entry *parent; //所繼承的父類
        int refcount;  //引用數
        zend_bool constants_updated; //類的類型
        zend_uint ce_flags;//類的類型 抽象?接口?Final?
        HashTable function_table;  //函數表
        HashTable default_properties; //屬性
        HashTable properties_info;  //函數的訪問級別
        HashTable default_static_members; //靜態成員
        HashTable *static_members; //靜態成員,當是用戶聲明的類等於default_static_members,內置的類爲NULL
        HashTable constants_table;
        const struct _zend_function_entry *builtin_functions;
       //眼熟嗎???對的.魔術函數在這裏哦..
        union _zend_function *constructor;
        union _zend_function *destructor;
        union _zend_function *clone;
        union _zend_function *__get;
        union _zend_function *__set;
        union _zend_function *__unset;
        union _zend_function *__isset;
        union _zend_function *__call;
        union _zend_function *__callstatic;
        union _zend_function *__tostring;
        union _zend_function *serialize_func;
        union _zend_function *unserialize_func;


        zend_class_iterator_funcs iterator_funcs;


        /* handlers */
        zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC);
        zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
        int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* a class implements this interface */
        union _zend_function *(*get_static_method)(zend_class_entry *ce, char* method, int method_len TSRMLS_DC);


        /* serializer callbacks */
        int (*serialize)(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
        int (*unserialize)(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);


        zend_class_entry **interfaces;
        zend_uint num_interfaces;


        char *filename;//聲明類的文件地址
        zend_uint line_start;//類開始行
        zend_uint line_end;//類結束行
        char *doc_comment;
        zend_uint doc_comment_len;


        struct _zend_module_entry *module;
};




清楚了這個結構之後 下面來看看zend_do_begin_class_declaration函數
void zend_do_begin_class_declaration(const znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC) /* {{{ */
{
        zend_op *opline;
        int doing_inheritance = 0;
        zend_class_entry *new_class_entry;
        char *lcname;
        int error = 0;
        zval **ns_name;
        
        if (CG(active_class_entry)) {
                zend_error(E_COMPILE_ERROR, "Class declarations may not be nested");
                return;
        }


        lcname = zend_str_tolower_dup(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len);
        
        if (!(strcmp(lcname, "self") && strcmp(lcname, "parent"))) {
                efree(lcname);
                zend_error(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", class_name->u.constant.value.str.val);
        }


        /* Class name must not conflict with import names */
        if (CG(current_import) &&
                        zend_hash_find(CG(current_import), lcname, Z_STRLEN(class_name->u.constant)+1, (void**)&ns_name) == SUCCESS) {
                error = 1;
        }
       if (CG(current_namespace)) {
                /* Prefix class name with name of current namespace */
                znode tmp;


                tmp.u.constant = *CG(current_namespace);
                zval_copy_ctor(&tmp.u.constant);
                zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC);
                class_name = &tmp;
                efree(lcname);
                lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant));
        }


        if (error) {
                char *tmp = zend_str_tolower_dup(Z_STRVAL_PP(ns_name), Z_STRLEN_PP(ns_name));


                if (Z_STRLEN_PP(ns_name) != Z_STRLEN(class_name->u.constant) ||
                        memcmp(tmp, lcname, Z_STRLEN(class_name->u.constant))) {
                        zend_error(E_COMPILE_ERROR, "Cannot declare class %s because the name is already in use", Z_STRVAL(class_name->u.constant));
                }
                efree(tmp);
        }


        new_class_entry = emalloc(sizeof(zend_class_entry));
        new_class_entry->type = ZEND_USER_CLASS;
        new_class_entry->name = class_name->u.constant.value.str.val;
        new_class_entry->name_length = class_name->u.constant.value.str.len;


        zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC);
        new_class_entry->filename = zend_get_compiled_filename(TSRMLS_C);
        new_class_entry->line_start = class_token->u.opline_num;
        new_class_entry->ce_flags |= class_token->u.EA.type;
if (parent_class_name && parent_class_name->op_type != IS_UNUSED) {
                switch (parent_class_name->u.EA.type) {
                        case ZEND_FETCH_CLASS_SELF:
                                zend_error(E_COMPILE_ERROR, "Cannot use 'self' as class name as it is reserved");
                                break;
                        case ZEND_FETCH_CLASS_PARENT:
                                zend_error(E_COMPILE_ERROR, "Cannot use 'parent' as class name as it is reserved");
                                break;
                        case ZEND_FETCH_CLASS_STATIC:
                                zend_error(E_COMPILE_ERROR, "Cannot use 'static' as class name as it is reserved");
                                break;
                        default:
                                break;
                }
                doing_inheritance = 1;
        }


        opline = get_next_op(CG(active_op_array) TSRMLS_CC);
        opline->op1.op_type = IS_CONST;
        build_runtime_defined_function_key(&opline->op1.u.constant, lcname, new_class_entry->name_length TSRMLS_CC);


        opline->op2.op_type = IS_CONST;
        opline->op2.u.constant.type = IS_STRING;
        Z_SET_REFCOUNT(opline->op2.u.constant, 1);


        if (doing_inheritance) {
                opline->extended_value = parent_class_name->u.var;
                opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
        } else {
                opline->opcode = ZEND_DECLARE_CLASS;
        }
opline->op2.u.constant.value.str.val = lcname;
        opline->op2.u.constant.value.str.len = new_class_entry->name_length;


        zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &new_class_entry, sizeof(zend_class_entry *), NULL);
        CG(active_class_entry) = new_class_entry;


        opline->result.u.var = get_temporary_variable(CG(active_op_array));
        opline->result.op_type = IS_VAR;
        CG(implementing_class) = opline->result;


        if (CG(doc_comment)) {
                CG(active_class_entry)->doc_comment = CG(doc_comment);
                CG(active_class_entry)->doc_comment_len = CG(doc_comment_len);
                CG(doc_comment) = NULL;
                CG(doc_comment_len) = 0;
        }
}


lcname = zend_str_tolower_dup(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len);
把所有類全部轉換爲小寫處理.這就是爲什麼PHP大小寫不敏感的原因.
if (!(strcmp(lcname, "self") && strcmp(lcname, "parent"))) {
                efree(lcname);
                zend_error(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", class_name->u.constant.value.str.val);
        }
類的名字不能是self和parent.
第23-26行 用來檢測類名是否重複定義.
第27-37行 用來設置命名空間,這是PHP5.3的新特性
第39-47行 用來拋出重複定義的錯誤
第49-57行 初始化保存類的結構
zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC);函數是用來初始化結構裏面的HashTable,魔術方法.
這個函數裏面也有上面提到( HashTable *static_members; //靜態成員,當是用戶聲明的類等於default_static_members,內置的類爲NULL)的原因
第58-73行 同樣用來檢測父類的類名是否包含 保留關鍵字 self,parent,static
剩下的就是用來生成一個OP,
是內部類:那麼生成的OP中間代碼就是 ZEND_DECLARE_INHERITED_CLASS
是用戶類:OP中間代碼就是ZEND_DECLARE_CLASS
在這之後..Zend引擎會調用zend_execute函數執行OP的中間代碼ZEND_DECLARE_CLASS_SPEC_HANDLER
它定義在Zend/zend_vm_execute.h中.
這個函數將執行關鍵代碼
EX_T(opline->result.u.var).class_entry = do_bind_class(opline, EG(class_table), 0 TSRMLS_CC)      ;
do_bind_class會將此類放到class_table中.當然 ,在這個函數裏還會判斷該類是否存在.不存在會拋出錯誤
Internal Zend error - Missing class information for %s
如果存在 則會添加成功
那麼到這裏類就創建成功了.
下一張節就要深入到 類內部了哦...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章