PHP7擴展開發(五):回調php函數與開發一個並行擴展

起步

不少時候,須要把控制權限交給用戶,或者在擴展裏完成某件過後去回調用戶的方法。php

在PHP擴展裏是經過 call_user_function_ex 函數來調用用戶空間的函數的。swoole

定義

它的定義在 Zend/zend_API.h :多線程

#define call_user_function_ex(function_table, object, function_name, retval_ptr, param_count, params, no_separation, symbol_table)

    _call_user_function_ex(object, function_name, retval_ptr, param_count, params, no_separation)

經過宏定義替換爲_call_user_function_ex,其中參數 function_table 被移除了,它之因此在API才存在大概是爲了兼容之前的寫法。函數的真正定義是:併發

ZEND_API int _call_user_function_ex(
    zval *object, 
    zval *function_name, 
    zval *retval_ptr, 
    uint32_t param_count, 
    zval params[], 
    int no_separation);

參數分析:異步

  • zval *object:這個是用來咱們調用類裏的某個方法的對象。函數

  • zval *function_name:要調用的函數的名字。測試

  • zval *retval_ptr:收集回調函數的返回值。優化

  • uint32_t param_count:回調函數須要傳遞參數的個數。ui

  • zval params[]: 參數列表。spa

  • int no_separation:是否對zval進行分離,若是設爲1則直接會出錯,分離的做用是爲了優化空間。

回調功能的實現

PHP_FUNCTION(hello_callback)
{
    zval *function_name;
    zval retval;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &function_name) == FAILURE) {
        return;
    }
    if (Z_TYPE_P(function_name) != IS_STRING) {
        php_printf("Function require string argumnets!");
        return;
    }
    //TSRMLS_FETCH();
    if (call_user_function_ex(EG(function_table), NULL, function_name, &retval, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) {
        php_printf("Function call failed!");
        return;
    }

    *return_value = retval;
    zval_copy_ctor(return_value);
    zval_ptr_dtor(&retval);

}

zval_copy_ctor()原始(zval)的內容拷貝給它。zval_ptr_dtor()釋放空間。return_value不是一個函數外的變量,它的由函數聲明裏的變量。PHP_FUNCTION(hello_callback)這個聲明是簡寫,最終會被預處理宏替換爲:

void zif_hello_callback(zend_execute_data *execute_data, zval *return_value)

return_value變量其實也就是最終返回給調用腳本的,RETURN_STR(s) 等返回函數最終也都是宏替換爲對該變量的操做。

測試腳本:

<?php
function fun1() {
    for ($i = 0; $i < 5; $i++) {
        echo 'fun1:'.$i."\n";
    }
    return 'call end';
}

echo hello_callback('fun1');

一個並行擴展

早期的php不支持多進程多線程的,如今隨着發展有不少擴展不斷完善它,諸如pthread,swoole等,不只能多線程,並且能實現異步。

利用c語言多線程pthread庫來實現一個簡單的並行擴展。

先聲明咱們一會用到的結構:

struct myarg
{
    zval *fun;
    zval ret;
};

線程函數:

static void my_thread(struct myarg *arg) {
    zval *fun = arg->fun;
    zval ret = arg->ret;
    if (call_user_function_ex(EG(function_table), NULL, fun, &ret, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) {
        return;
    }
}

函數的實現:

PHP_FUNCTION(hello_thread)
{
    pthread_t tid;
    zval *fun1, *fun2;
    zval ret1, ret2;
    struct myarg arg;
    int ret;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &fun1, &fun2) == FAILURE) {
        return;
    }
    arg.fun = fun1;
    arg.ret = ret1;
    ret = pthread_create(&tid, NULL, (void*)my_thread, (void*)&arg);
    if(ret != 0) {
        php_printf("Thread Create Error\n");
        exit(0);
    }
    if (call_user_function_ex(EG(function_table), NULL, fun2, &ret2, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) {
        return;
    }
    pthread_join(tid, NULL);
    RETURN_NULL();

}

測試腳本:

<?php
function fun1() {
    for ($i = 0; $i < 5; $i++) {
        echo 'fun1:'.$i.'\n';
    }
}

function fun2() {
    for ($i = 0; $i < 5; $i++) {
        echo 'fun2:'.$i.'\n';
    }
}

hello_thread('fun1', 'fun2');
echo 'after 多併發';

輸出:

20161130143035.png

兩次的輸出結果不同,而且echo 'after 多併發';是在兩個函數都運行完後才執行的。

相關文章
相關標籤/搜索