出于性能原因,使用DO_ACTION嵌套钩子-安全/必要?

时间:2019-08-06 作者:Nic

我正在编写一个插件,它使用许多不同的操作/过滤器挂钩来操纵用户的不同部分。php页面(例如。manage_users_columns, admin_head, user_row_actions, editable_roles)

在每个钩子的回调函数中,我使用wp\\u get\\u current\\u user()并执行wp查询,以统计当前用户的用户id存储为元值的帖子数。我使用此结果来决定执行的过滤器输出/操作(复杂任务,无法更改)。

因此,在加载用户时。php页面中,WP查询似乎在每个钩子回调函数中执行(这很有意义)。

出于性能原因,我想“嵌套”挂钩。在“init”操作挂钩中,应执行WP查询,并将结果传递给以下操作挂钩和过滤器。还有两个问题:

1) 这里有必要关心性能吗?或者Wordpress是否有一些内部“缓存”机制,可以识别相同的查询,并在加载页面时只执行一次?

2a)以下示例目前有效,但我不确定一般情况下使用该示例是否安全。问题是:由于我的自定义钩子“myfuncs”是在钩子(init)中定义的,所以它是否总是已知的,并且可以钩住,例如在函数中。php还是插件代码?

2b)关于嵌套的WP挂钩(例如admin\\u head-users.php):它们是否根据优先级正常执行,或者它们是否会延迟执行/有时根本不会执行,因为它们是嵌套的?

3) 是否有不同的解决方案/最佳做法?

add_action(\'init\', \'initfunc\', 1 );
add_action(\'myfuncs\', \'myfuncs_callback\', 1 );

public function initfunc() {
    global $pagenow;
    $args = (array) $pagenow; // only to simplify this example.

    do_action( \'myfuncs\', $args );
}

public function myfuncs_callback ($args) {
    if($args[0] === "somevalue") {
        add_action( \'admin_head-users.php\', \'add_user_css\' ,1 );
    }

}

public function add_user_css () {
    echo "<style>/*some styles*/</style>";
}

1 个回复
SO网友:Jacob Peattie

这是一个很好的用例Transients 或者Object Cache. 使用哪种方法取决于您是需要对每个请求执行此查询/检查,还是只需要每小时/天/周执行一次,等等。

对于这两个选项,将首先创建一个单独的函数来执行检查并返回结果数。然后在每个钩子回调中使用此函数来确定执行的输出/操作(如您所说)。

function wpse_344520_count_user_posts() {
    $user_id = get_current_user_id();
    $query   = new WP_Query(
        [
                // etc.
        ]
    );

    $count = $query->found_posts;

    return $count;
}

add_action(
    \'manage_users_columns\',
    function() {
        $count = wpse_344520_count_user_posts();

        if ( $count > 0 ) {
            // Do something.
        }
    }
);

add_action(
    \'admin_head\',
    function() {
        $count = wpse_344520_count_user_posts();

        if ( $count > 0 ) {
            // Do something.
        }
    }
);

// etc.
诀窍在于,在这个函数中,您应该检查查询是否已经执行,如果存在,则返回现有结果。您可以使用瞬态或对象缓存作为存储机制,具体取决于您的需求。

对象缓存verson如下所示:

function wpse_344520_count_user_posts() {
    // Check if we\'ve cached a result.
    $count = wp_cache_get( \'wpse_344520_count\', $user_id );

    // If we have...
    if ( false !== $count ) { // Must be strict comparison so we can store 0 if necessary.
        // ...return it.
        return $count;
    } 

    // Otherwise perform the query...
    $user_id = get_current_user_id();

    $query = new WP_Query(
        [
            // etc.
        ]
    );

    $count = $query->found_posts;

    // ..cache the result... 
    wp_cache_set( \'wpse_344520_count\', $count, $user_id );

    // ...and return it.
    return $count;
}
现在查询只运行一次,后续调用wpse_344520_count_user_posts() 将使用缓存的结果。

transient方法类似,但将其结果保存在数据库中,这样可以在多个单独的请求上缓存结果。如果不需要精确到第二秒的结果,请使用此方法:

function wpse_344520_count_user_posts() {
    $user_id = get_current_user_id();

    // Check if we\'ve cached a result.
    $count = get_transient( \'wpse_344520_count_\' . $user_id );

    // If we have...
    if ( $count !== false ) { // Must be strict comparison so we can store 0 if necessary.
        // ...return it.
        return $count;
    } 

    // Otherwise perform the query...
    $query = new WP_Query(
        [
            // etc.
        ]
    );

    $count = $query->found_posts;

    // ..and cache the result... 
    set_transient( \'wpse_344520_count_\' . $user_id, $count, HOUR_IN_SECONDS );

    // ...then return it.
    return $count;
}
正如你所看到的,这非常相似。唯一的区别是:

我们使用get_transient()set_transient() 而不是wp_cache_get()wp_cache_set().set_transient(). 在我的示例中,我使用了built in constants 将时间设置为一小时