ウェブアプリケーションでは、一定期間状態を保持したいことが多くあります。それを実現するためにさまざまな方法が提案され、利用されてきました。古くはブラウザに保持させる Cookie、サーバーに保存させるセッション。最近では HTML5 の Web Storage があります。
WordPress には、この一定期間保持するという目的にぴったりのAPI(Application Programming Interface)があります。Transients API と呼ばれるもので、指定した秒数が経過するまでデーターベース上に保持してくれるというものです。保存・取り出し・削除の3つの関数で構成されているシンプルなものです。WordPress 2.8.0 から使えるようになったもので、現在ではコアファイルのさまざまなところで使われています。
使い方については他のサイトを見ていただくとして、この記事ではhackネタを書いてみたいと思います。
まずは、ちょっと長いですが関数の定義部分から。引用元は WordPress 3.2.1 の wp-includes/function.php
です。
/**
* Delete a transient
*
* @since 2.8.0
* @package WordPress
* @subpackage Transient
*
* @uses do_action() Calls 'delete_transient_$transient' hook before transient is deleted.
* @uses do_action() Calls 'deleted_transient' hook on success.
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
* @return bool true if successful, false otherwise
*/
function delete_transient( $transient ) {
global $_wp_using_ext_object_cache;
do_action( 'delete_transient_' . $transient, $transient );
if ( $_wp_using_ext_object_cache ) {
$result = wp_cache_delete( $transient, 'transient' );
} else {
$option_timeout = '_transient_timeout_' . $transient;
$option = '_transient_' . $transient;
$result = delete_option( $option );
if ( $result )
delete_option( $option_timeout );
}
if ( $result )
do_action( 'deleted_transient', $transient );
return $result;
}
/**
* Get the value of a transient
*
* If the transient does not exist or does not have a value, then the return value
* will be false.
*
* @uses apply_filters() Calls 'pre_transient_$transient' hook before checking the transient.
* Any value other than false will "short-circuit" the retrieval of the transient
* and return the returned value.
* @uses apply_filters() Calls 'transient_$option' hook, after checking the transient, with
* the transient value.
*
* @since 2.8.0
* @package WordPress
* @subpackage Transient
*
* @param string $transient Transient name. Expected to not be SQL-escaped
* @return mixed Value of transient
*/
function get_transient( $transient ) {
global $_wp_using_ext_object_cache;
$pre = apply_filters( 'pre_transient_' . $transient, false );
if ( false !== $pre )
return $pre;
if ( $_wp_using_ext_object_cache ) {
$value = wp_cache_get( $transient, 'transient' );
} else {
$transient_option = '_transient_' . $transient;
if ( ! defined( '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;
if ( get_option( $transient_timeout ) < time() ) {
delete_option( $transient_option );
delete_option( $transient_timeout );
return false;
}
}
}
$value = get_option( $transient_option );
}
return apply_filters( 'transient_' . $transient, $value );
}
/**
* Set/update the value of a transient
*
* You do not need to serialize values. If the value needs to be serialized, then
* it will be serialized before it is set.
*
* @since 2.8.0
* @package WordPress
* @subpackage Transient
*
* @uses apply_filters() Calls 'pre_set_transient_$transient' hook to allow overwriting the
* transient value to be stored.
* @uses do_action() Calls 'set_transient_$transient' and 'setted_transient' hooks on success.
*
* @param string $transient Transient name. Expected to not be SQL-escaped.
* @param mixed $value Transient value. Expected to not be SQL-escaped.
* @param int $expiration Time until expiration in seconds, default 0
* @return bool False if value was not set and true if value was set.
*/
function set_transient( $transient, $value, $expiration = 0 ) {
global $_wp_using_ext_object_cache;
$value = apply_filters( 'pre_set_transient_' . $transient, $value );
if ( $_wp_using_ext_object_cache ) {
$result = wp_cache_set( $transient, $value, 'transient', $expiration );
} else {
$transient_timeout = '_transient_timeout_' . $transient;
$transient = '_transient_' . $transient;
if ( false === get_option( $transient ) ) {
$autoload = 'yes';
if ( $expiration ) {
$autoload = 'no';
add_option( $transient_timeout, time() + $expiration, '', 'no' );
}
$result = add_option( $transient, $value, '', $autoload );
} else {
if ( $expiration )
update_option( $transient_timeout, time() + $expiration );
$result = update_option( $transient, $value );
}
}
if ( $result ) {
do_action( 'set_transient_' . $transient );
do_action( 'setted_transient', $transient );
}
return $result;
}
基本的には、2つの項目をセットにしてオプションとして保存しています。項目はデータ自体となる _transient_識別子文字列
と、保持期限となる _transient_timeout_識別子文字列
。
識別子文字列と値に対してのサニタイズやシリアライズ処理は関数が面倒を見てくれますが、識別子文字列が長い場合は注意が必要です。テーブルのカラムから来る制限で45文字を超えることが出来ないのですが、サニタイズによってこの長さを超えてしまう可能性があります。半角英数を使うのが無難でしょう。
取り出すときには有効期限ないかどうかを確認し(756行目)、期限を過ぎている場合には削除もしてくれます(757・758行目)。Codex を見る限りでは何もしなくても期限が過ぎれば削除してくれるようですが、相当する処理は見つけられませんでした。
オブジェクトキャッシュが有効になっている場合には、データーベースの変わりにそちらを使うので更なる高速化が期待できます。
拙作プラグインの AmazonLink でも試験的に導入してみました。自前で行っていた処理がほとんどいらなくなったおかげで、プログラムがかなり簡略化できています。今のところ問題も起きていないので、このまま次のリリースに入れる予定です。2.8.0以上という条件はありますが、これはお勧めです。