php-timecopを使って日時に関する動作テストを行う

テストの時に時間を止めるか、特定日時に変更できたら色々捗るよなーと思いました。
Google先生に頼んで探してみたら、やっぱりありました。
php-timecop というPHP拡張です。

http://www.slideshare.net/hnw/php-13502189
http://www.slideshare.net/hnw/phptimecop

makeとかphp.iniを触ったりとかがあるので自前のサーバが必要ですが、面白そうですね。


https://github.com/hnw/php-timecop

…うっ、公式の情報が圧倒的に少ない(白目)
という事で、簡単にまとめてみました。
(この記事の情報は、「version 1.0.4, 2013/03/11」を基にしています。)


GitHubのREADME.mdを読んでインストールとphp.iniの設定を行うと、
「timecop_freeze()」「timecop_travel()」「timecop_return()」の3関数が新たに使用できるようになると共に、元からある時刻系の関数とDateTimeクラスがtimecopに対応したものに書き換わります。


時刻系の関数とDateTimeクラスの使い方は変わりませんので、追加された関数のみ、それぞれ説明します。

boolean timecop_freeze ([ int $timestamp = time() ]);

timecop_freeze()関数は、PHPの現在時刻を仮想的に不変とします。
指定した$timestampのUNIX時刻で時間を固定できます。
$timestampを指定しなければ、現在時刻のまま仮想的に時間を停止できます。

timecop_travel()関数またはtimecop_return()関数を呼び出すまで、時間は固定されたままとなります。

返り値:
$timestampが無効なものであればFALSEを、それ以外の場合にTRUEを返します。

boolean timecop_travel ([ int $timestamp = time() ]);

timecop_travel()関数は、PHPの現在時刻を仮想的に変更します。timecop_freeze()関数とは違い、仮想時刻は変化していきます。
指定した$timestampのUNIX時刻に時間をズラす事ができます。
$timestampを指定しなければ、現在時刻のままとなります。(実質的にtimecop_return()関数を呼び出したのと同じになります。)

返り値:
$timestampが無効なものであればFALSEを、それ以外の場合にTRUEを返します。

boolean timecop_return();

timecop_return()関数は、PHPの現在時刻を通常通りに戻します。

返り値:
TRUEを返します。

(※フレームワーク等で時間計測を行なっている場合、正しくない時間経過となるため、
 処理の最後にはtimecop_return()関数を呼び出すようにした方が良いと思います。)


という事で、使ってみましょう

vim ~/php-timecop.php

<?php
date_default_timezone_set('Asia/Tokyo');


echo date('Y-m-d H:i:s'). "\n";

echo "\ntimecop_freeze\n";
timecop_freeze(946749896);
echo date('Y-m-d H:i:s'). "\n";
sleep(1);
echo date('Y-m-d H:i:s'). "\n";

echo "\ntimecop_return\n";
timecop_return();
echo date('Y-m-d H:i:s'). "\n";
sleep(1);
echo date('Y-m-d H:i:s'). "\n";

echo "\ntimecop_travel\n";
timecop_travel(946749896);
echo date('Y-m-d H:i:s'). "\n";
sleep(1);
echo date('Y-m-d H:i:s'). "\n";

echo "\ntimecop_return\n";
timecop_return();
echo date('Y-m-d H:i:s'). "\n";

php ~/php-timecop.php

2013-03-16 18:15:25

timecop_freeze
2000-01-02 03:04:56
2000-01-02 03:04:56

timecop_return
2013-03-16 18:15:26
2013-03-16 18:15:27

timecop_travel
2000-01-02 03:04:56
2000-01-02 03:04:57

timecop_return
2013-03-16 18:15:28

◆ハマったこと
timecop拡張とは全然関係ない所でドハマり。
FuelPHPのテストで使いたかったため、oilの呼び出し時に独自のphp.iniを呼びだそうと「php -c ~/php_timecop.ini oil test」とかやってたんですが、
どうやらPHPUnitの側で読み出し対象が「/etc/php.ini」に変更されてしまう模様。
(「php -c ~/php_timecop.ini oil console」では独自のphp.iniが読み出し対象になっていました)

うーん、不思議ー。