時間によって変化するページ描画を確認したい
時限で公開開始/終了するページやサービスを実装する場合、「未来に行って確認したいなぁ」と思うことが間々あるかと思います。
DBに保存してあったり、ソース上にべた書きしたりしている時刻を書き換えるのは、何処か不格好ですし、複数の時限処理が絡み合っている場合はそれだけ多くのところを書き換えて、さらに確認後は元に戻さなくてはいけません。
なので timecop を利用するのが便利だと思いますが、 timecop_freeze() または timecop_travel() は、それだけではそのページを描画した際の時間認識を書き換えるだけとなり、別ページに遷移すると効果は失われます(よね……?)。
そこで、セッションを利用して、設定を維持したままページ移動が行える仕組みを考えてみました。
(きっと過去数多のエンジニアが同じことを考え実践していらっしゃったと思うのですが、軽く検索した限りまとまった記述に行き当たらなかったので、ひとつの方法として書き留めます)
構想
だいたい下記の仕組みです。
- 時刻を任意に設定し、「freeze」か「travel」かを選ぶ
- 渡された時刻とどちらを選んだかによって、異なる値をセッションに保存
- Web描画時に必ず通るbaseコントローラでセッションの値を読んで、 timecop_freeze() または timecop_travel() を行う。セッションが消えていた場合は何もしない=元通りの時刻で扱われる
実装
- timecop_freeze() は時刻(任意に設定した値)が不変
- timecop_travel() は時間(設定時の時刻との差)が不変
なので、その変わらない値を持ち回って、ページ遷移の度に設定を行います。
$smarty.now も裏はphpなので、ちゃんとtimecopの影響を受けて時刻が変わります。
以下、バリデーション等を省略した抜粋になります。
timecopが入っていない環境にソースを反映する可能性がある場合は、 function_exists('timecop_travel') など噛ませればその環境での利用可否を判断できるかと。
時刻を登録するところ
timeset
<form action="timecop/travel" method="post">
<input type="text" name="date" value="{$smarty.now|date_format:'%Y/%m/%d %H:%M:%S'}">
<button type="submit" name="type" value="freeze">時刻を固定する</button>
<button type="submit" name="type" value="travel">時刻を移動する</button>
</form>
Controller_Timecop
public function action_travel()
{
$date = Input::post('date');
// 「freeze」を選んだ場合
// 設定した時刻のタイムスタンプ(int型)をセッションに保存します
if (Input::post('type') == 'freeze') {
\Session::set( 'timecop', strtotime($date) );
// 「travel」を選んだ場合
// まず時刻を元に戻し、現在時刻と設定時刻の差(string型)をセッションに保存します
// 差は、strtotime()に渡せばきちんと計算できる形に整えています
} else {
timecop_return();
$timeDiff = date_diff( date_create(), date_create( $date ) );
\Session::set( 'timecop', $timeDiff->format( '%R%y years %R%m months %R%d days %R%h hours %R%i minutes %R%s seconds' ) );
}
\Response::redirect();
}
登録された時刻を設定するところ
Controller_Base
$timecop = \Session::get( 'timecop' );
if ( !is_null( $timecop ) ) {
if ( is_int( $timecop ) ) {
timecop_freeze( $timecop );
} elseif ( is_string( $timecop ) ) {
timecop_travel( strtotime( $timecop ) );
}
}