黃聰:WordPress動作鈎子函數add_action()、do_action()源碼解析


WordPress常用兩種鈎子,過濾鈎子和動作鈎子。過濾鈎子相關函數及源碼分析在上篇文章中完成,本篇主要分析動作鈎子源碼。

 

然而,在了解了動作鈎子的源碼后你會發現,動作鈎子核心代碼竟然跟過濾鈎子差不多!是的,至此,我不得不告訴你,動作鈎子只是WP開發者為了區分概念而把過濾鈎子另外命名的一種東西!當然,它們還是有一些細微的差別,下面我們將從源碼來深入解讀。

 

動作鈎子概念:動作鈎子是WP代碼執行到某處或某個事件發生時觸發的一系列函數,插件可以利用動作鈎子API在WP代碼執行的特定點之前插入一系列函數以控制執行。它跟過濾鈎子極像,唯一不同的是過濾鈎子返回一個處理后的值,而動作鈎子僅完成函數執行並不返回值,如果鈎子不存在則返回NULL並新增該鈎子。

 

動作鈎子原理:由於動作鈎子和過濾鈎子幾乎一樣,所以它們的實現原理也是一樣的。它主要利用一個全局變量$wp_filter,增加動作函數時使用add_action()函數給全局變量$wp_filter增加了一個數組元素,這個元素鍵名中含有鈎子名,值中含有對應函數及執行優先級等信息,在調用do_action()函數使用動作鈎子時,它通過循環查找出所有跟鈎子關聯的函數並將其依次調用,最后返回處理后的數據。

 

動作鈎子使用步驟

由於PHP代碼會經過Zend等引擎翻譯,代碼中步驟的先后順序並不重要,所以以下步驟僅為便於理解鈎子原理的偽步驟,不具有實際參考意義!

1、創建鈎子(可省略):使用do_action()函數可以創建一個沒有掛載函數的鈎子,掛載函數可以通過add_action()添加,最后再使用do_action()調用執行;

2、創建動作函數:它可以有傳入參數也可以無傳入參數,其他與創建普通函數沒有任何區別,函數的作用為完成某項動作;

3、掛載函數:即使用add_action()將函數掛載到指定鈎子上;

4、執行動作鈎子:使用do_action()可以依次執行掛載在指定鈎子上的所有函數以完成指定任務;

 

動作鈎子函數詳解:

在看動作鈎子函數作用、參數說明等時,你會發現幾乎是跟過濾鈎子重復的。至於為什么會這樣,那就要看源碼了,我保證,看完源碼后你會感慨自己被WP開發者涮了!

1、add_action($tag,$function_to_add,$priority = 10,$accepted_args = 1)

add_action()作用:該函數用於給指定的動作鈎子$tag添加指定的掛載函數$function_to_add,同時它可以確定掛載函數執行優先級及其可接收參數個數;

add_action()參數說明

$tag為鈎子名;

$function_to_add為掛載函數名;

可選參數$priority為該掛載函數執行的優先級,默認為10,該數字越小則越早執行,數字相同則按其添加到鈎子上的順序執行,越早添加越早執行;

可選參數$accepted_args確定掛載函數接收的參數個數,默認為1;

add_action()源碼分析:

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
    return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

 

怎么樣,看到了吧!被坑了有木有!add_action()函數的代碼竟然是調用一次add_filter()!這尼瑪完全是一個人的大名和小名的問題有木有!

 

2、do_action($tag, $arg = '')

do_action()作用:該函數調用掛載在過濾鈎子$tag上的所有函數以完全特定的任務;

do_action()參數說明:

$tag為鈎子名;

$arg為動作鈎子上掛載函數的傳入參數,默認為空;

do_action()源碼分析:

function do_action($tag, $arg = '') {
    global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
    if ( ! isset($wp_actions) )
        $wp_actions = array();
# 如果$wp_actions變量未設置過,則將其定義為數組;
    if ( ! isset($wp_actions[$tag]) )
        $wp_actions[$tag] = 1;
    else
        ++$wp_actions[$tag];
    # 如果$wp_actions[$tag]未設置則將其賦值為1,否則將其值加1;
    if ( isset($wp_filter['all']) ) {
        $wp_current_filter[] = $tag;
        $all_args = func_get_args();
        _wp_call_all_hook($all_args);
    }
    # 跟apply_filters()中的all鈎子處理方式完全一樣!_wp_call_all_hook()源碼分析見上篇文章過濾鈎子源碼解析;
    if ( !isset($wp_filter[$tag]) ) {
        if ( isset($wp_filter['all']) )
            array_pop($wp_current_filter);
        return;
    }
    # 當前鈎子不存在,則直接返回,不再執行以后代碼;
    if ( !isset($wp_filter['all']) )
        $wp_current_filter[] = $tag;
    # 將當前鈎子設置為$tag;
    $args = array();
    if ( is_array($arg) && 1 == count($arg) && isset($arg[0]) && is_object($arg[0]) )
        $args[] =& $arg[0];
    else
        $args[] = $arg;
    # do_action()若有傳入參數,且為一個數組,該數組僅此一個元素,該元素有值則將$args值設置為引用$arg[0],否則直接賦值;
    for ( $a = 2; $a < func_num_args(); $a++ )
        $args[] = func_get_arg($a);
    # 通過for循環,若do_action()有不只一個傳入參數,將這些值賦給數組$args;
    if ( !isset( $merged_filters[ $tag ] ) ) {
        ksort($wp_filter[$tag]);
        $merged_filters[ $tag ] = true;
    }
    # 跟apply_filter()函數排序代碼完全一樣!詳解見上文;
    reset( $wp_filter[ $tag ] );
    do {
        foreach ( (array) current($wp_filter[$tag]) as $the_ )
            if ( !is_null($the_['function']) )
                call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    } while ( next($wp_filter[$tag]) !== false );
    array_pop($wp_current_filter);
}
    # 除了少了一行return $value其他跟apply_filters()完全一樣!

 

看過動作鈎子的源碼,是不是驚呼,原來這丫就是過濾鈎子換了個名兒而已!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM