php5對象
將php5的對象和它的先輩php4對象進行比較實在有些不公平, 不過php5對象使用的API函數還是遵循php4的API構建的. 如果你已經閱讀了第10章"php4對象", 你將會對本章內容多少有些熟悉. 在開始本章之前, 可以像第10章開始時一樣, 重命名擴展為sample3並清理多余的代碼, 只保留擴展的骨架代碼.
進化史
在php5對象變量中有兩個關鍵的組件. 第一個是一個數值的標識, 它和第9章"資源數據類型"中介紹的數值資源ID非常相似, 扮演了一個用來在對應表中查找對象實例的key的角色. 在這個實例表中的元素包含了到zend_class_entry的引用以及內部的屬性表.
第二個元素是對象變量的句柄表, 使用它可以自定義Zend引擎對實例的處理方式. 在本章後面你將看到這個句柄表.
zend_class_entry
類條目是你在用戶空間定義的類的內部表示. 正如你在前一章所見, 這個結構通過調用INIT_CLASS_ENTRY()初始化, 參數為類名和它的函數表. 接著在MINIT階段使用zend_register_internal_class()注冊.
zend_class_entry *php_sample3_sc_entry; #define PHP_SAMPLE3_SC_NAME "Sample3_SecondClass" static function_entry php_sample3_sc_functions[] = { { NULL, NULL, NULL } }; PHP_MINIT_FUNCTION(sample3) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, PHP_SAMPLE3_SC_NAME, php_sample3_sc_functions); php_sample3_sc_entry = zend_register_internal_class(&ce TSRMLS_CC); return SUCCESS; }
方法
如果你已經閱讀了上一章, 你可能就會想"到現在為止看起來幾乎一樣啊?", 到現在為止, 你是對的. 現在我們開始定義一些對象方法. 你將開始看到一些非常確定的並且大受歡迎的不同.
PHP_METHOD(Sample3_SecondClass, helloWorld) { php_printf("Hello World\n"); }
在Zend引擎2中引入了PHP_METHOD()宏, 它是對PHP_FUNCTION()宏的封裝, 將類名和方法名聯合起來, 不用像php4中手動定義方法名了. 通過使用這個宏, 在擴展中你的代碼和其他維護者的代碼的名字空間解析規范就保持一致了.
定義
定義一個方法的實現, 和其他函數一樣, 只不過是將它連接到類的函數表中. 除了用於實現的PHP_METHOD()宏, 還有一些新的宏可以用在函數列表的定義中.
PHP_ME(classname, methodname, arg_info, flags)
PHP_ME()相比於第5章"你的第一個擴展"中介紹的PHP_FE()宏, 增加了一個classname參數, 以及末尾的一個flags參數(用來提供public, protected, private, static等訪問控制, 以及abstract和其他一些選項). 比如要定義helloWorld方法, 就可以如下定義:
PHP_ME(Sample3_SecondClass,helloWorld,NULL,ZEND_ACC_PUBLIC)
PHP_MALIAS(classname, name, alias, arg_info, flags)
和PHP_FALIAS()宏很像, 這個宏允許你給alias參數描述的方法(同一個類中的)實現提供一個name指定的新名字. 例如, 要復制你的helloWorld方法則可以如下定義
PHP_MALIAS(Sample3_SecondClass, sayHi, helloWorld, NULL, ZEND_ACC_PUBLIC)
PHP_ABSTRACT_ME(classname, methodname, arg_info)
內部類中的抽象方法很像用戶空間的抽象方法. 在父類中它只是一個占位符, 期望它的子類提供真正的實現. 你將在接口一節中使用這個宏, 接口是一種特殊的class_entry.
PHP_ME_MAPPING(methodname, functionname, arg_info)
最後一種方法定義的宏是針對同時暴露OOP和非OOP接口的擴展(比如mysqli既有過程化的mysqli_query(), 也有面向對象的MySQLite::query(), 它們都使用了相同的實現.)的. 假定你已經有了一個過程化函數, 比如第5章寫的sample_hello_world(), 你就可以使用這個宏以下面的方式將它附加為一個類的方法(要注意, 映射的方法總是public, 非static, 非final的):
PHP_ME_MAPPING(hello, sample_hello_world, NULL)
現在為止, 你看到的方法定義都使用了ZEND_ACC_PUBLIC作為它的flags參數. 實際上, 這個值可以是下面兩張表的任意值的位域運算組合, 並且它還可以和本章後面"特殊方法"一節中要介紹的一個特殊方法標記使用位域運算組合.
比如, 由於你前面定義的Sample3_SecondClass::helloWorld()方法不需要對象實例, 你就可以將它的定義從簡單的ZEND_ACC_PUBLIC修改為ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 這樣引擎知道了就不會去提供(實例)了.