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

來源:互聯網
上載者:User
這幾天比較忙哦..沒有時間寫..周末了多寫幾篇吧.
目前大部分語言都支援類.
類是什麼?類就是物件導向,簡稱OOP.英文名字 Object Oriented Programming.
物件導向是什麼?是一種編程架構.
OOP的一條基本原則是電腦程式是由單個能夠起到子程式作用的單元或對象組合而成,OOP達到了軟體工程的三個目標:重用性、靈活性和擴充性.
因為我們講的不是這裡只簡單描述,如果你還不知道什麼是類,什麼是物件導向..那麼這篇文章目前不適合你哦.

classPerson{

};

上面是建立一個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 {

chartype;

char*name;//類名稱

zend_uint name_length;

struct_zend_class_entry *parent;//所繼承的父類

intrefcount; //引用數

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;

conststruct_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,intby_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,intmethod_len TSRMLS_DC);

/* serializer callbacks */

int(*serialize)(zval *object, unsignedchar**buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);

int(*unserialize)(zval **object, zend_class_entry *ce,constunsignedchar*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函數

voidzend_do_begin_class_declaration(constznode *class_token, znode *class_name,constznode *parent_class_name TSRMLS_DC)/* {{{ */

{

zend_op *opline;

intdoing_inheritance = 0;

zend_class_entry *new_class_entry;

char*lcname;

interror = 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) {

caseZEND_FETCH_CLASS_SELF:

zend_error(E_COMPILE_ERROR,"Cannot use 'self' as class name as it is reserved");

break;

caseZEND_FETCH_CLASS_PARENT:

zend_error(E_COMPILE_ERROR,"Cannot use 'parent' as class name as it is reserved");

break;

caseZEND_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
如果存在 則會添加成功
那麼到這裡類就建立成功了.
下一張節就要深入到 類內部了哦…

以上就是原創:PHP核心研究之類的實現的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.