Menu

Hiểu về hook trong WordPress

Mục lục

  1. Hook là cái gì ?
  2. Cơ chế của hook
  3. Các biến lưu hook
  4. Add một hook thì chuyện gì xảy ra ?
  5. Chuyện gì xảy ra khi gọi hook ?
  6. Dùng nhiều hook có nặng trang không ?
  7. Lời kết

wordpress hook

Khi phát triển các plugin, theme, chúng ta không thể nào không biết đến các hook, action hook, filter hook. Ta sử dụng hàm add_action/add_filter để thêm các hàm vào những hook này, sau đó dùng do_action và do_filter để gọi tất cả những hàm đã add vào hook.

Nhưng liệu chúng ta có biết rõ cơ chế quản lý hook của WordPress hay không ? Đại loại như : Add hook thì chuyện gì thực sự xảy ra bên trong WordPress ?, các hàm này được map như thế nào với các hook và lưu ở đâu ? Trong database hay trong biến php ? Thứ tự ưu tiên của hook là như thế nào ? Từ tất cả những điều trên, chúng ta sẽ đến với câu hỏi : Sử dụng nhiều hook có làm nặng website không ?

Trong bài viết ngày hôm nay, chúng ta sẽ cùng tìm hiểu về cách mà WordPress quản lý các hook này trong core của nó. Đi từ ý tưởng cho đến cách mà nó hiện thực. Hiểu rõ nó, chúng ta có thể thoải mái và tự tin sử dụng các hook.

Hook là cái gì ?

Hook có nghĩa là móc, WordPress định nghĩa hook là những cái móc, nằm ở vị trí nào đó trong code của theme, plugin, core. Lập trình viên có thể xác định vị trí đặt móc, sau đó ở một đoạn code khác, họ móc các hàm vào tên của hook. Nhờ như vậy, mỗi lần vị trí đặt hook được thực thi, các hàm được móc vào hook sẽ được thực thi theo một thứ tự nhất định.

Có thể liên tưởng cách hoạt động của hook như trong mô tả của phương pháp lập trình hướng sự kiện. Khi một hook ( tương ứng với sự kiện ) được gọi ( sự kiện xảy ra ) thì các hàm gắng với hook đó sẽ được thực thi.

Nhờ có Hook, chúng ta giải quyết được khá nhiều vấn đề khi code. Cho phép mở rộng khả năng hợp-tác giữa các dòng code với nhau, xây dựng được một interface tốt cho phép nhiều plugin, class phối hợp làm việc với nhau. Bạn không cần phải biết plugin kia, theme kia code hàm gì, bạn chỉ cần gọi đến 1 tên hook cố định, họ muốn xử lý thế nào cũng được, đó là tùy họ.

Cơ chế của hook

Như vậy, hook là một ma trận giữa tên hook và các hàm hook. Để hiểu cách làm việc của nó, ta phải đi theo hình xoán ốc. Đầu tiên ta sẽ hỏi nó lưu như thế nào ? Nhưng để hiểu được lưu như thế nào, ta phải tính xem nó sẽ được gọi như thế nào ?

Bởi vì WordPress viết bằng PHP, nên ta hãy nghĩ xem, PHP hỗ trợ kỹ thuật gì trong việc lưu hàm vào biến và gọi nó thông qua biến này ? Trong PHP, có một hàm giúp ta làm chuyện này, đó là call_user_func_array .  Bạn có thể đọc ở trang tài liệu của PHP bằng cách ấn vào tên hàm ở trên, mình đã add link.

Và WordPress sử dụng hàm này để gọi các hook của họ. Như vậy, WordPress sẽ phải có phương pháp lưu phù hợp với hàm này.

Hàm call_user_func_array chấp nhận hai tham số là :

  • callback : Có kiểu dữ liệu là Callable, nó chưa đủ thông tin để xác định một hàm.
  • param_arr : Mảng các tham số

Tham số callback chấp nhận một vài kiểu như sau :

  • Tên hàm : Tên của mộ hàm đơn, hàm này phải được load lên trước khi có lệnh gọi tới hook này. ví dụ call_user_func('my_callback_function');
  • Static method trong class : Là một mảng bao gồm tên của class và tên phương thức trong class. Ví dụ : call_user_func(array('MyClass', 'myCallbackMethod'));
  • Phương thức trong đối tượng : Thay vì phương thức static trong class, bạn cũng có thể truyền vào tên phương thức kèm theo đối tượng có phương thức đó. Ví dụ : call_user_func(array($obj, 'myCallbackMethod'));
  • Tên phương thức static trong php 5.2.3call_user_func('MyClass::myCallbackMethod');

Và WordPress cũng chấp nhận các tham số như vậy trong hàm add_action và add_filter.

Khi bạn gọi các hàm do_action và apply_filters thì WordPress sẽ sử dụng call_user_func_array để gọi các hàm hook.

Các biến lưu hook

Các hàm hook được lưu thành một ma trận, map giữa tên hook, độ ưu tiên, id và hàm hook. Nó tạo thành các mảng nhiều chiều và lưu trong những biến toàn cục bằng từ khóa blobal , đoạn code này nằm ở file plugin.php dòng 23 :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
// Initialize the filter globals.
global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
if ( ! isset( $wp_filter ) )
    $wp_filter = array();
if ( ! isset( $wp_actions ) )
    $wp_actions = array();
if ( ! isset( $merged_filters ) )
    $merged_filters = array();
if ( ! isset( $wp_current_filter ) )
    $wp_current_filter = array();

Trong đó, ta có thể hiểu ý nghĩa của các biến như sau :

  • $wp_filter : Chứa các hook
  • $wp_actions : Chưa số lần thực thi của action
  • $merged_filters : Danh sách những hook đã được apply
  • $wp_current_filter : Hook đang được gọi.

Đồng thời các biến này cũng được lưu trong $GLOBALS[];

Add một hook thì chuyện gì xảy ra ?

Đây là hàm xử lý khi bạn add một hook :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
 * Hook a function or method to a specific filter action.
 *
 * WordPress offers filter hooks to allow plugins to modify
 * various types of internal data at runtime.
 *
 * A plugin can modify data by binding a callback to a filter hook. When the filter
 * is later applied, each bound callback is run in order of priority, and given
 * the opportunity to modify a value by returning a new value.
 *
 * The following example shows how a callback function is bound to a filter hook.
 *
 * Note that `$example` is passed to the callback, (maybe) modified, then returned:
 *
 *     function example_callback( $example ) {
 *         // Maybe modify $example in some way.
 *         return $example;
 *     }
 *     add_filter( 'example_filter', 'example_callback' );
 *
 * Since WordPress 1.5.1, bound callbacks can take as many arguments as are
 * passed as parameters in the corresponding apply_filters() call. The `$accepted_args`
 * parameter allows for calling functions only when the number of args match.
 *
 * *Note:* the function will return true whether or not the callback is valid.
 * It is up to you to take care. This is done for optimization purposes,
 * so everything is as quick as possible.
 *
 * @since 0.71
 *
 * @global array $wp_filter      A multidimensional array of all hooks and the callbacks hooked to them.
 * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added,
 *                               it doesn't need to run through that process.
 *
 * @param string   $tag             The name of the filter to hook the $function_to_add callback to.
 * @param callback $function_to_add The callback to be run when the filter is applied.
 * @param int      $priority        Optional. Used to specify the order in which the functions
 *                                  associated with a particular action are executed. Default 10.
 *                                  Lower numbers correspond with earlier execution,
 *                                  and functions with the same priority are executed
 *                                  in the order in which they were added to the action.
 * @param int      $accepted_args   Optional. The number of arguments the function accepts. Default 1.
 * @return boolean true
 */
function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
    global $wp_filter, $merged_filters;
    $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
    $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
    unset( $merged_filters[ $tag ] );
    return true;
}

Như bạn thấy, khi add một hook, thì hàm hook này sẽ được lưu vào biến $wp_filter kèm theo số lượng tham số mà nó muốn nhận. Đồng thời cũng loại nó ra khỏi danh sách những hook vừa được thực thi.

Chuyện gì xảy ra khi gọi hook ?

Tất nhiên rồi, khi hook được gọi, thì nó cần phải được thực thi và đưa ra khỏi danh sách cần thực thi bằng cách đánh dấu. Dưới đây là hàm apply_filters mà chúng ta thường sử dụng :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function apply_filters( $tag, $value ) {
    global $wp_filter, $merged_filters, $wp_current_filter;
    $args = array();
    // Do 'all' actions first.
    if ( isset($wp_filter['all']) ) {
        $wp_current_filter[] = $tag;
        $args = func_get_args();
        _wp_call_all_hook($args);
    }
    if ( !isset($wp_filter[$tag]) ) {
        if ( isset($wp_filter['all']) )
            array_pop($wp_current_filter);
        return $value;
    }
    if ( !isset($wp_filter['all']) )
        $wp_current_filter[] = $tag;
    // Sort.
    if ( !isset( $merged_filters[ $tag ] ) ) {
        ksort($wp_filter[$tag]);
        $merged_filters[ $tag ] = true;
    }
    reset( $wp_filter[ $tag ] );
    if ( empty($args) )
        $args = func_get_args();
    do {
        foreach( (array) current($wp_filter[$tag]) as $the_ )
            if ( !is_null($the_['function']) ){
                $args[1] = $value;
                $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
            }
    } while ( next($wp_filter[$tag]) !== false );
    array_pop( $wp_current_filter );
    return $value;
}

Rất đơn giản như các bạn thấy trong hàm trên, đầu tiên WP sẽ gọi đến hook all . Sau đó sắp xếp lại mảng hook, đồng thời reset index của nó, tiếp theo là chạy lần lượt hết các hàm hook và gọi nó lên bằng hàm call_user_func_array mà ta đã nói lúc đầu. Bạn có thể xem thêm về các hàm khác như do_action, do_action_ref_array, … trong file plugin.php.

Dùng nhiều hook có nặng trang không ?

Nếu bỏ qua yếu tố tốc độ xử lý của từng hàm hook, thì có thể nói, nhiều hook hơn sẽ làm tăng thêm thời gian chạy hook, đó là tất nhiên rồi. Nhưng thời gian tăng thêm này thực sự không đáng kể. Mà vấn đề mấu chốt ta phải hiểu đó là ở các hàm hook. Nếu chỉ có một hàm hook lặp vô hạn được móc vào hook, thì nó vẫn là lặp vô hạn.

Lời kết

Như vậy, câu hỏi lớn nhất khi chúng ta làm việc với hook đã được giải đáp, Dúng nhiều hook cũng không làm giảm tốc độ xử lý của code, mà vấn đề nằm ở cách chúng ta code các hàm hook.

Tuy nhiên không có sự lạm dụng nào là tốt. Lạm dụng sử dụng hook, đôi khi sẽ khiến bạn không quản lý được những hàm nào sẽ chạy khi gọi hook này, ở một trường hợp nào đó, rất có thể bạn sẽ khiến nhiều hàm riêng lẻ bị phụ thuộc vào nhau thông qua một hook nào đó, điều đó có thể gây rắc rối cho bạn khi xét về tính độc lập của các hàm.

 

Facebook Comments

No comments

Trả lời