結論
- 外部APIなど時間のかかる処理は、
get_transient()で確認し、falseなら取得し直してset_transient()で保存する、という形が基本です。 - transient の有効期限は「最大の寿命」で、最低保証はありません。公式にも、期限前に消えることがあると書かれています。消えても動く(再取得できる)コードにします。
- 取得結果が
falseや空文字になりうる場合は、そのまま保存すると「キャッシュなし」と区別できません。配列に包んで保存します。 - transient の名前は172文字以内にします(公式の記載)。
原因・仕組み
transient は、有効期限付きの一時データです。外部オブジェクトキャッシュ(Memcached など)が無い環境では、wp_options テーブルに _transient_名前 と _transient_timeout_名前 の2行で保存されます。外部オブジェクトキャッシュがあると、DBではなくメモリに保存され、wp_options には現れません。
| 項目 | 内容 |
|---|---|
| 保存 | set_transient( $name, $value, $expiration_seconds ) |
| 取得 | get_transient( $name )(期限切れ・未保存は false) |
| 削除 | delete_transient( $name ) |
| 保存先 | オブジェクトキャッシュがなければ wp_options |
| 期限 | 最大寿命。最低保証なし |
期限切れの行は、取得されたタイミングなどで削除されます。大量に溜まるとオプションテーブルを圧迫するため、不要になったら delete_transient() を呼びます。
手順(サンプルコード)
function myplug_get_rates() {
$key = 'myplug_rates_v1';
$data = get_transient( $key );
if ( false !== $data ) {
return $data['rates']; // キャッシュあり
}
$res = wp_remote_get( 'https://api.example.com/rates', array( 'timeout' => 5 ) );
if ( is_wp_error( $res ) || 200 !== wp_remote_retrieve_response_code( $res ) ) {
return array(); // 失敗は短時間だけ保存してもよい
}
$rates = json_decode( wp_remote_retrieve_body( $res ), true );
if ( ! is_array( $rates ) ) {
return array();
}
// 配列に包んで保存(false・空でもキャッシュと区別できる)
set_transient( $key, array( 'rates' => $rates ), 15 * MINUTE_IN_SECONDS );
return $rates;
}
出力する値は、キャッシュから取り出した後でも、必ずエスケープします(esc_html( $rates['USD'] ) など)。外部の値は信頼できないデータです。
動作確認(検証環境と結果)
WordPress 7.1.2(ja)、PHP 8.2.12、MariaDB 10.4.32。php run.php transient-vs-option.php を実行しました。外部API呼び出し(wp_remote_get)自体はネットワークを使うため実行していません(未検証)。
ext object cache: NULL
array(1) { ["a"]=> int(1) }
_transient_timeout_t_demo autoload=off
_transient_t_demo autoload=off
missing: bool(false)
stored false: string(0) ""
after expiry: bool(false)
len 172: set=true get='v'
len 180: set=true get='v'
len 200: set=true get='v'
- 外部オブジェクトキャッシュがない環境では、
_transient_t_demoと_transient_timeout_t_demoの2行がwp_optionsに作られ、autoloadはoffでした。 - 未保存のキーは
falseでした。 falseを保存して取得すると、空文字''が返りました。false !== $dataで判定すると「キャッシュあり」と見なされ、空文字を返してしまいます。配列で包む理由はここです。- 1秒で期限切れにして2秒待つと、
falseに戻りました。 - 180文字、200文字の名前でも、この環境では保存・取得できました。ただし公式は172文字までとしているため、長い名前に依存しないでください。レビュー時の再実行では、172文字超の名前の行が
option_name(191文字)で切り詰められたままdelete_transient()で消えずに残り、その状態で同じスクリプトを再実行すると、172文字の名前の取得がfalseになることがありました(切り詰められた行と名前が衝突したためと考えられます)。
transient で外部APIの結果をキャッシュする方法の注意点
- 名前に
$_GETの値などを使うと、transient が無限に増えます。キー数に上限を設けるか、使わない設計にします。 - ユーザーごとに内容が違うデータは、
get_current_user_id()をキーに含めるか、キャッシュしません。 - 期限を
0にすると、期限なし(オプションと同様に自動読み込み)になります。大きなデータでは避けます。 - 外部APIが落ちたときに、毎回リクエストを投げ続けないよう、失敗も短い期限で保存する方法もあります。ただし、その場合は成功時のキャッシュと区別できるキー・値にします。
- プラグインを削除するとき、
delete_transient()で後始末をします。
transient で外部APIの結果をキャッシュする方法でよくあるミス
get_transient()の結果をif ( $data )で判定し、空配列や0をキャッシュなしとして扱う。- 取得失敗の結果(エラー)を、長時間キャッシュしてしまう。
- 「期限までは必ず残る」と考え、再取得のコードを書かない。
- 名前に日付や変数を入れ続け、古いtransientが溜まる。
transient で外部APIの結果をキャッシュする方法のチェックリスト
- キャッシュが無い場合の再取得処理が書かれているか。
false・空値のケースで、正しく判定できるか。- 名前は固定の接頭辞付きで、172文字以内か。
- 取得した外部データを、出力時にエスケープしているか。
transient で外部APIの結果をキャッシュする方法のFAQ(よくある質問)
Q. transient とオプションはどちらを使うべき?
A. 消えても再生成できるデータは transient、消えると困る設定値はオプションです。
Q. 期限が来る前に更新したいときは?
A. delete_transient() で削除するか、set_transient() で上書きします。
Q. マルチサイトで共有するには?
A. set_site_transient() を使います。
筆者の見解(transient で外部APIの結果をキャッシュする方法)
transient は「速くするための一時的なメモ」であって、「データの置き場」ではないと考えます。消える前提で、再生成の処理を先に書き、そのあとでキャッシュを足すと、バグの少ない作りになります。false を保存しない、という一点だけでも、守る価値があると思います。
transient で外部APIの結果をキャッシュする方法の関連項目
- set_transient() の使い方|説明・引数・注意点
- get_transient() の使い方|説明・引数・注意点
- delete_transient() の使い方|説明・引数・注意点
- get_option() の使い方|説明・引数・注意点
- update_option() の使い方|説明・引数・注意点

