1. 程式人生 > >PHP7擴充套件開發之常量定義

PHP7擴充套件開發之常量定義

前言

這次,我們將演示如何在PHP擴充套件中定義一個常量。要實現的PHP程式碼如下:

<?php
    define("__ARR__", array('2', 'site'=>"www.bo56.com"));  
    define("__SITE__", "www.bo56.com", true);
    define("say\__SITE__", "bo56.com");
    var_dump(__ARR__);
    var_dump(__site__);
    var_dump(say\__SITE__);
?>

我們將演示在PHP擴充套件中定義三個常量。如上面程式碼中的三個define。

程式碼

基礎程式碼

這個擴充套件,我們將在say擴充套件的 PHP_MINIT_FUNCTION(say) 方法上增加相應的程式碼。say擴充套件相關程式碼大家請看這篇博文。PHP7擴充套件開發之hello word 文中已經詳細介紹瞭如何建立一個擴充套件和提供了原始碼下載。

增加的程式碼如下

//增加兩個方法
//釋放hash
static void say_hash_destroy(HashTable *ht)
    zend_string *key;
    zval *element;
    if (((ht)->u.flags & HASH_FLAG_INITIALIZED))
{ ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, element) { if (key) { free(key); } switch (Z_TYPE_P(element)) { case IS_STRING: free(Z_PTR_P(element)); break; case IS_ARRAY: say_hash_destroy
(Z_ARRVAL_P(element)); break; } } ZEND_HASH_FOREACH_END(); free(HT_GET_DATA_ADDR(ht)); } free(ht); } //釋放陣列和字串 static void say_entry_dtor_persistent(zval *zvalue) if (Z_TYPE_P(zvalue) == IS_ARRAY) { say_hash_destroy(Z_ARRVAL_P(zvalue)); } else if (Z_TYPE_P(zvalue) == IS_STRING) { zend_string_release(Z_STR_P(zvalue)); } } //PHP_MINIT_FUNCTION(say)方法的PHP擴充套件原始碼: 擴充套件初始化的呼叫此方法 PHP_MINIT_FUNCTION(say) { zend_constant c; zend_string *key; zval value; ZVAL_NEW_PERSISTENT_ARR(&c.value); zend_hash_init(Z_ARRVAL(c.value), 0, NULL, (dtor_func_t)say_entry_dtor_persistent, 1); add_index_long(&c.value, 0, 2); key = zend_string_init("site", 4, 1); ZVAL_STR(&value, zend_string_init("www.bo56.com", 12, 1)); zend_hash_update(Z_ARRVAL(c.value), key, &value); c.flags = CONST_CS|CONST_PERSISTENT; c.name = zend_string_init("__ARR__", 7, 1); c.module_number = module_number; zend_register_constant(&c); REGISTER_STRINGL_CONSTANT("__SITE__", "www.bo56.com", 12, CONST_PERSISTENT); REGISTER_NS_STRINGL_CONSTANT("say", "__SITE__", "bo56.com", 8, CONST_CS|CONST_PERSISTENT); } //擴充套件解除安裝的時候呼叫此方法 PHP_MSHUTDOWN_FUNCTION(say) { zval *val; val = zend_get_constant_str("__ARR__", 7); say_hash_destroy(Z_ARRVAL_P(val)); ZVAL_NULL(val); return SUCCESS; }

程式碼說明

一般情況下,在擴充套件中只建議定義null,bool,long,double,string幾種型別的常量。因為核心只提供了這幾種型別的巨集方法。
常量定義的巨集方法在Zend/zend_constants.h檔案中。想定義一個常量,很簡單,只要呼叫對應的巨集方法即可。如:

REGISTER_STRINGL_CONSTANT("__SITE__", "www.bo56.com", 12, CONST_PERSISTENT);

巨集方法的最後一個引數是一些識別符號。
CONST_PERSISTENT 表示為持久的。常駐記憶體。
CONST_CS 表示為區分大小寫。
注意我們上面定義常量時使用的是SITE,但是呼叫的時候使用的是site

還有一套可以指定名稱空間的巨集方法。巨集方法中帶NS。如:

REGISTER_NS_STRINGL_CONSTANT("say", "__SITE__", "bo56.com", 8, CONST_CS|CONST_PERSISTENT);

第一個引數就是名稱空間。

為了展示常量定義的一些細節。我們定義了一個__ARR__常量。
ZVAL_NEW_PERSISTENT_ARR(&c.value);我們想讓__ARR__為持久的。所以使用ZVAL_NEW_PERSISTENT_ARR建立一個數組。
陣列建立完後,我們需要初始化。初始化的程式碼就是

zend_hash_init(Z_ARRVAL(c.value), 0, NULL,
                        (dtor_func_t)say_entry_dtor_persistent, 1);

引數中的say_entry_dtor_persistent是一個解構函式,用於釋放陣列的元素。

到這裡,如果編譯執行。當程式執行結束的時候,你會發現一個致命錯誤。錯誤資訊如下:

Fatal error: Internal zval's can't be arrays, objects or resources in Unknown on line 0

因為在程式執行完畢,內部zval釋放的時候,會進行型別檢測。如果發現是array object或者resources,則會報錯。可以檢視Zend/zend_variables.c檔案中_zval_internal_dtor方法。
為了解決這個問題,我們需要手動釋放我們建立的__ARR__相關的陣列。
模組解除安裝時執行的方法,是優先Zend內部zval釋放方法之前呼叫的。因此,我們只要在PHP_MSHUTDOWN_FUNCTION(say)方法中手動釋放。不再讓Zend去釋放就可以解決了。

原始碼下載