php的擴充與嵌入--php擴充中的數組和雜湊表2_PHP教程

來源:互聯網
上載者:User
接著上面一節,繼續說php擴充中的數組與雜湊表的api,這節主要是說回調遍曆函數正常遍曆函數解構函式排序、對比、極值函數

Iteration by hash Apply:對數組進行遍曆,最簡單的是使用一種與php語言中foreach語句功能類似的函數,zend_hash_apply,它接收一個回呼函數,並將hashtable的每一個元素傳遞給它。
typedef int (*apply_func_t)(void *pDest TSRMLS_DC);void zend_hash_apply(HashTable *ht,        apply_func_t apply_func TSRMLS_DC);
typedef int (*apply_func_arg_t)(void *pDest,                            void *argument TSRMLS_DC);void zend_hash_apply_with_argument(HashTable *ht,        apply_func_arg_t apply_func, void *data TSRMLS_DC);
一個是可以傳遞參數的,還有一個是不傳遞參數,只傳遞雜湊表中的值的。對於可傳遞參數的函數而言,在擴充中應用到可能性更大。回呼函數可能有不同的返回值:
表 回呼函數的返回值
Constant Meaning
ZEND_HASH_APPLY_KEEP 結束當前請求,進入下一個迴圈。與PHP語言forech語句中的一次迴圈執行完畢或者遇到continue關鍵字的作用一樣。
ZEND_HASH_APPLY_STOP 跳出,與PHP語言forech語句中的break關鍵字的作用一樣。
ZEND_HASH_APPLY_REMOVE 刪除當前的元素,然後繼續處理下一個。相當於在PHP語言中:unset($foo[$key]);continue;

對於一段簡單的php遍曆代碼
擴充形式如下:首先定義回呼函數
int php_sample_print_zval(zval **val TSRMLS_DC){    //重新copy一個zval,防止破壞原資料    zval tmpcopy = **val;    zval_copy_ctor(&tmpcopy);         //轉換為字串    INIT_PZVAL(&tmpcopy);    convert_to_string(&tmpcopy);        //開始輸出    php_printf("The value is: ");    PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));    php_printf("\n");         //毀屍滅跡    zval_dtor(&tmpcopy);         //返回,繼續遍曆下一個~    return ZEND_HASH_APPLY_KEEP;}
然後定義迴圈函數:zend_hash_apply(arrht, php_sample_print_zval TSRMLS_CC);
遍曆了一個名為arrht,元素類型是zval*的雜湊表。
注意儲存在雜湊表中的並不是元素,而是指標,也就是一個zval**.在複製的時候也是複製指標的,雜湊表本身不會動內容的。

為了能夠在迴圈的時候既接受到值,也接收到key,第三種形式zend_hash_apply():zend_hash_apply_with_arguments()
 $val) {     echo "The value of $key is: $val\n"; }?>
針對這段php代碼的c代碼:
int php_sample_print_zval_and_key(zval **val,         int num_args, va_list args, zend_hash_key *hash_key) {    /* 複製zval從而使得原來的內容被儲存下來 */         zval tmpcopy = **val;         /* tsrm_ls is needed by output functions */         TSRMLS_FETCH();         zval_copy_ctor(&tmpcopy);         /* Reset refcount & Convert */    INIT_PZVAL(&tmpcopy);    convert_to_string(&tmpcopy);         /* 輸出 */    php_printf("The value of ");    if (hash_key->nKeyLength) {          /* 如果是字串類型的key */            PHPWRITE(hash_key->arKey, hash_key->nKeyLength);         } else {        /* 如果是數字類型的key */         php_printf("%ld", hash_key->h);         }    php_printf(" is: ");    PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));     php_printf("\n");    /* Toss out old copy */    zval_dtor(&tmpcopy);    /* continue; */    return ZEND_HASH_APPLY_KEEP;}

執行遍曆:zend_hash_apply_with_arguments(arrht, php_sample_print_zval_and_key, 0);當我們檢查這個hash_key是字串類型還是數字類型時,是通過nKeyLength屬性來檢測的,而不是arKey屬性。這是因為核心有時候會留在arKey屬性裡些髒資料,但nKeyLength屬性是安全的,可以安全的使用。甚至對於Null 字元串索引,它也照樣能處理。比如:$foo[''] ="Bar";索引的值是NULL字元,但它的長度卻是包括最後這個NULL字元的,所以為1。

Iteration by move forward不用callback也可以實現雜湊表遍曆。這時候用的是雜湊表的內部指標。
在使用者空間裡有很多可用的函數:
1, 'b'=>2, 'c'=>3);    reset($arr);    while (list($key, $val) = each($arr)) {        /* Do something with $key and $val */    }    reset($arr);    $firstkey = key($arr);    $firstval = current($arr);    $bval = next($arr);    $cval = next($arr);?>
每一個相應的函數都會有一個zend版本:
* /* reset() */void zend_hash_internal_pointer_reset(HashTable *ht);        /* key() */int zend_hash_get_current_key(HashTable *ht,        char **strIdx, unit *strIdxLen,        ulong *numIdx, zend_bool duplicate);* /* current() */int zend_hash_get_current_data(HashTable *ht, void **pData);* /* next()/each() */int zend_hash_move_forward(HashTable *ht);* /* prev() */int zend_hash_move_backwards(HashTable *ht);* /* end() */void zend_hash_internal_pointer_end(HashTable *ht);* /* Other... */int zend_hash_get_current_key_type(HashTable *ht);int zend_hash_has_more_elements(HashTable *ht);
next() prev() end()其實都是找到相應的索引值,再用zend_hash_get_current_data()返回元素值
each()跟next步驟一樣,但是又調用並返回了zend_hash_get_current_key()

所以下面給出了不用回呼函數的雜湊表遍曆方法:
void php_sample_print_var_hash(HashTable *arrht){    for(zend_hash_internal_pointer_reset(arrht);    zend_hash_has_more_elements(arrht) == SUCCESS;    zend_hash_move_forward(arrht)) {        char *key;        uint keylen;        ulong idx;        int type;        zval **ppzval, tmpcopy;        type = zend_hash_get_current_key_ex(arrht, &key, &keylen,                                                  &idx, 0, NULL);//獲得返回的key的類型。這個類型可能有三種        if (zend_hash_get_current_data(arrht, (void**)&ppzval) == FAILURE) {//獲得當前索引所指的資料值            /* Should never actually fail             * since the key is known to exist. */            continue;        }        /* 複製zval的值,從而原來的值不會被破壞掉 */        tmpcopy = **ppzval;        zval_copy_ctor(&tmpcopy);        /* 重新設定refcount 並且轉換 */        INIT_PZVAL(&tmpcopy);        convert_to_string(&tmpcopy);        /* 輸出 */        php_printf("The value of ");        if (type == HASH_KEY_IS_STRING) {            /* String Key / Associative */            PHPWRITE(key, keylen);        } else {            /* Numeric Key */            php_printf("%ld", idx);        }        php_printf(" is: ");        PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));        php_printf("\n");        /* 銷毀原來的副本 */        zval_dtor(&tmpcopy);    }}
來看一下zend_hash_get_current_key_ex返回值的可能性:Constant Meaning

HASH_KEY_IS_STRING 當前元素的索引是字串類型的。therefore, a pointer to the element's key name will be populated into strIdx, and its length will be populated into stdIdxLen. If the duplicate flag is set to a nonzero value, the key will be estrndup()'d before being populated into strIdx. The calling application is expected to free this duplicated string.

HASH_KEY_IS_LONG 當前元素的索引是數字型的。
HASH_KEY_NON_EXISTANT HashTable中的內部指標已經移動到尾部,不指向任何元素。


Destruction注意只有四種解構函式:前兩個是用來從雜湊表中刪掉單個元素的:
int zend_hash_del(HashTable *ht, char *arKey, uint nKeyLen);int zend_hash_index_del(HashTable *ht, ulong h);
返回SUCCESS OR FAILURE
分別對應字串和數字索引的版本。
當一個元素從雜湊表中移除的時候,雜湊表的解構函式帶著指向這個元素的指標被調用。

完全刪除雜湊表的時候:void zend_hash_clean(HashTable *ht);相當於是迴圈調用一下zend_hash_del。調用下面這個函數除了執行clean之外,還會把zend_hash_init申請的空間都給搞掉:void zend_hash_destroy(HashTable *ht);
來看一個雜湊表的生命週期就可以對整個過程有更清楚的認識:
int sample_strvec_handler(int argc, char **argv TSRMLS_DC){    HashTable *ht;    /* 為雜湊表分配空間 */    ALLOC_HASHTABLE(ht);    /* 初始化雜湊表的內部狀態 */    if (zend_hash_init(ht, argc, NULL,                        ZVAL_PTR_DTOR, 0) == FAILURE) {        FREE_HASHTABLE(ht);        return FAILURE;    }    /* 把每個字串變成zval* */    while (argc) {        zval *value;        MAKE_STD_ZVAL(value);        ZVAL_STRING(value, argv[argc], 1);        argv++;        if (zend_hash_next_index_insert(ht, (void**)&value,                            sizeof(zval*)) == FAILURE) {            /* 對於分配失敗的情況應該跳掉 */            zval_ptr_dtor(&value);        }    }    /* Do some work */    process_hashtable(ht);    /* 毀壞雜湊表     * 釋放所有的分配的空曠 */    zend_hash_destroy(ht);    /* Free the HashTable itself */    FREE_HASHTABLE(ht);    return SUCCESS;}


Sorting, Comparing, and Going to the Extreme(s)對於兩個雜湊表進行大小比較:typedef int (*compare_func_t)(void *a, void *b TSRMLS_DC);這個函數就跟qsort一樣,期待你自己的函數去比較a和b,返回-1 0 1

下面就是一個用大小比較的例子:
int zend_hash_minmax(HashTable *ht, compare_func_t compar,                        int flag, void **pData TSRMLS_DC);
flag為0就返回最小值,否則就是最大值。

下面則給出一個更為具體的例子,通過不同的flag就可以控制到底是返回最大值還是最小值:
int fname_compare(zend_function *a, zend_function *b TSRMLS_DC){    return strcasecmp(a->common.function_name, b->common.function_name);}void php_sample_funcname_sort(TSRMLS_D){    zend_function *fe;    if (zend_hash_minmax(EG(function_table), fname_compare,                0, (void **)&fe) == SUCCESS) {        php_printf("Min function: %s\n", fe->common.function_name);    }    if (zend_hash_minmax(EG(function_table), fname_compare,                1, (void **)&fe) == SUCCESS) {        php_printf("Max function: %s\n", fe->common.function_name);    }}

還有一個進行雜湊比較的函數:int zend_hash_compare(HashTable *hta, HashTable *htb,
compare_func_t compar, zend_bool ordered TSRMLS_DC);先比較雜湊表的個數,哪個多哪個大。如果一樣多的,就每個元素去比較。

另外還有一個專門的排序函數:
typedef void (*sort_func_t)(void **Buckets, size_t numBuckets,            size_t sizBucket, compare_func_t comp TSRMLS_DC);int zend_hash_sort(HashTable *ht, sort_func_t sort_func,        compare_func_t compare_func, int renumber TSRMLS_DC);
一般就用zend_qsort作為sort_func就夠了。renumber這個參數如果設為1的話,那麼就會拋棄原有的索引鍵值關係,賦予新的數字鍵值。zend_hash_sort(target_hash, zend_qsort,array_data_compare, 1 TSRMLS_CC);array_data_compare是一個返回compare_func_t類型資料的函數,它將按照HashTable中zval*值的大小進行排序。


http://www.bkjia.com/PHPjc/635044.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/635044.htmlTechArticle接著上面一節,繼續說php擴充中的數組與雜湊表的api,這節主要是說回調遍曆函數正常遍曆函數解構函式排序、對比、極函數 Iteration by ha...

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.