Transientをもりもり使用したテーマを構築した際、問題になるのは「変わらないんだけどキャッシュなの?バグなの?」というお客様からの質問。
お客様もわかりづらいし、私たちも調査などなどで時間を使ってしまいます。
管理者としてログインしているときにはフロントのTransientを効かせないようにすればいいんじゃないか?ということで、そんなことができるのかとコアファイルを覗いてみました。
[追記:2018/06/19]プラグインが公式プラグインディレクトリに掲載
ダッシュボードからボタン一つで全てのTransientがクリアできるプラグイン「Clear Transient From Dashboard」が公式ディレクトリに掲載されました。
get_transient()では返り値を一括制御できない
Transients APIについてはこちらを参照。
get_transient()で既にキャッシュされている値を取得、有効期限を確認して、期限が切れていたら値を作り直してset_transient()、切れていなかったら取得したキャッシュの内容を使用する…というのが基本の使い方。
Transientを効かせないというのは、「常にget_transient()がfalseを返せばいい」ということ。
それならと、うまいフィルターがないかどうか確認。
※wp-includes/option.php (WordPress 4.9.6)
/**
* Get the value of a transient.
*
* If the transient does not exist, does not have a value, or has expired,
* then the return value will be false.
*
* @since 2.8.0
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
* @return mixed Value of transient.
*/
function get_transient( $transient ) {
/**
* Filters the value of an existing transient.
*
* The dynamic portion of the hook name, `$transient`, refers to the transient name.
*
* Passing a truthy value to the filter will effectively short-circuit retrieval
* of the transient, returning the passed value instead.
*
* @since 2.8.0
* @since 4.4.0 The `$transient` parameter was added
*
* @param mixed $pre_transient The default value to return if the transient does not exist.
* Any value other than false will short-circuit the retrieval
* of the transient, and return the returned value.
* @param string $transient Transient name.
*/
$pre = apply_filters( "pre_transient_{$transient}", false, $transient );
if ( false !== $pre )
return $pre;
if ( wp_using_ext_object_cache() ) {
$value = wp_cache_get( $transient, 'transient' );
} else {
$transient_option = '_transient_' . $transient;
if ( ! wp_installing() ) {
// If option is not in alloptions, it is not autoloaded and thus has a timeout
$alloptions = wp_load_alloptions();
if ( !isset( $alloptions[$transient_option] ) ) {
$transient_timeout = '_transient_timeout_' . $transient;
$timeout = get_option( $transient_timeout );
if ( false !== $timeout && $timeout < time() ) {
delete_option( $transient_option );
delete_option( $transient_timeout );
$value = false;
}
}
}
if ( ! isset( $value ) )
$value = get_option( $transient_option );
}
/**
* Filters an existing transient's value.
*
* The dynamic portion of the hook name, `$transient`, refers to the transient name.
*
* @since 2.8.0
* @since 4.4.0 The `$transient` parameter was added
*
* @param mixed $value Value of transient.
* @param string $transient Transient name.
*/
return apply_filters( "transient_{$transient}", $value, $transient );
}
フィルターはいくつかあるけど、全てフィルター名に$transientを使っている…。
$transientにはTransientの名前が格納されているので、Transientの名前ごとにフックをかけていかなければならない…フィルターを使用して一括で返り値を変更するのは難しそう。
オブジェクトキャッシュも一枚噛んでる
また、wp_using_ext_object_cache()の分岐も注目。オブジェクトキャッシュも絡んできているよう。
wp_cache_get()の中を少し遡ってみたけど、フィルターはなさそうだったので、ここでfalseが返せないとオブジェクトキャッシュ使用時はアウトですね。
※wp-includes/load.php (WordPress 4.9.6)
/**
* Toggle `$_wp_using_ext_object_cache` on and off without directly
* touching global.
*
* @since 3.7.0
*
* @global bool $_wp_using_ext_object_cache
*
* @param bool $using Whether external object cache is being used.
* @return bool The current 'using' setting.
*/
function wp_using_ext_object_cache( $using = null ) {
global $_wp_using_ext_object_cache;
$current_using = $_wp_using_ext_object_cache;
if ( null !== $using )
$_wp_using_ext_object_cache = $using;
return $current_using;
}
うーん…これはどっかのタイミングで$_wp_using_ext_object_cacheをfalseに差し替えれば大丈夫そうかな?
get_option()でもどうにもできない
get_transient()内では返り値をfalseにはできなさそうだった…ということで、最後の砦、get_option()のフックを探してみる。
get_transient()内で、$transient_option = ‘_transient_’ . $transient; からの $value = get_option( $transient_option ); なので、get_option()に渡るときには、Transient名は全て ‘_transient_’ から始まるようになっている(DBを覗いたことがある人は、このコードを見なくても既に知っているかも)。
私の大嫌いな正規表現で「オプション名が’_transient_’ から始まったら false を返すよ!」ってしてあげればいいのでしょう。
…ということで、get_option()を覗いてみたのですが、あったフィルターはこちら!
※wp-includes/option.php (WordPress 4.9.6)
function get_option( $option, $default = false ) {
・・・省略・・・
$pre = apply_filters( "pre_option_{$option}", false, $option, $default );
・・・省略・・・
return apply_filters( "default_option_{$option}", $default, $option, $passed_default );
・・・省略・・・
return apply_filters( "option_{$option}", maybe_unserialize( $value ), $option );
}
もれなく$option!
SQL文で値を取得して、それをreturnしているので、これ以上遡ることは不可。なんということでしょう。
残念な結論
結論:一括なんてするな!Transient名ごとにフックしろ!
add_filter()で正規表現使えたり、フック増えたりすると嬉しいのだけど…ここまで拘っているということは、一括フィルターは用意する気なんてないよってことなんでしょうね。
おとなしくTransient名ごとにフックを書いてあげるしかないみたいです。


