2016年5月17日火曜日

FuelPHP 1.7 月の検証でドハマリ valid_dateのバグ

今回は、検証後の日付の値がひと月戻る現象についてお伝えしようと思います。

まず、フォームはこんな感じ。月を選択するセレクトボックスです。


    <select name="month">
        <option value="2016-05">2016年 5月</option>
        <option value="2016-06">2016年 6月</option>
        <option value="2016-07">2016年 7月</option>
    </select>

いたって普通ですね。

Validationルールはこんな感じ


    $val = Validation::forge();

    $val->add('month', '月')
        ->add_rule('required')
        ->add_rule('valid_date', 'Y-m');

ここまでは特に問題ないと思います。

monthの値は'2016-05'を送信し、これを実行してみます。


    $result = $val->run();        //これは当然true
    $data = $val->validated();    //何が入るでしょう????

なんとびっくりなのですが、$data['month']の値は'2016-04'になります。

一か月戻っちゃってるよ!!!

coreのほうのコードをのぞいてみると元凶を見つけました。


    //中略
    $parsed = date_parse_from_format($format, $val);  //$valは'2016-05', $formatは'Y-m'
    //中略
    return date($format, mktime($parsed['hour'], $parsed['minute'], $parsed['second'], $parsed['month'], $parsed['day'], $parsed['year']));
    //↑お前が元凶だ!!

まず、$parsedの中身を見てみましょう


array(12) {
  ["year"]=>
  int(2016)
  ["month"]=>
  int(5)
  ["day"]=>
  bool(false)
  ["hour"]=>
  bool(false)
  ["minute"]=>
  bool(false)
  ["second"]=>
  bool(false)
  ["fraction"]=>
  bool(false)
  ["warning_count"]=>
  int(0)
  ["warnings"]=>
  array(0) {
  }
  ["error_count"]=>
  int(0)
  ["errors"]=>
  array(0) {
  }
  ["is_localtime"]=>
  bool(false)
}

$parsedのyear, monthはそれぞれ 2016, 5 なんですが、dayがbooleanのfalseです。
'2016-05'を送信したので年と月以外の値がfalseになっています。
falseは0と扱われますので、dayは0と同等です。
day(と時刻関連)が0なのにmktimeに代入してしまっているのが問題。
mktimeは2016年5月0日を、2016年5月1日の前日である2016年4月30日であると解釈しますので、これを'Y-m'でフォーマットすると2016-04になるんですねー

Y-mのフォーマットはあまり使われることはないと思いますが、使われる際はvalid_dateをオーバーライドするか独自ルールを作っての検証が必要です。
(FuelPHP 1.7でのお話なので、それ以降は治っていると思いたい)

0 件のコメント:

コメントを投稿