Symfony1.4でフォーマットを指定して日時のバリデーションをする方法


前置き

日時のバリデーションをする際に思い込みでやってしまい、かなりの時間を浪費したので備忘の意味も含めて記載します。

環境について

  • PHP 5.5
  • Symfony 1.4
  • Apache 2.2
  • CentOS 6

Symfony1.4で日時のフォーマットを指定してバリデーションをするには

フォーマットを指定して日時のバリデーションをするにはsfValidatorDateクラスを継承したsfValidatorDateTimeクラスを使って下記のように設定をすればバリデーションが出来ます。

validation
new sfValidatorDateTime([
    'date_format' => '/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}) (?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})/',
]));

ここで大事なのは(?P\d{4})という記述方法です。
この記述方法は名前付きサブパターンというらしいのですが、この記述方法で無いとSymfony1.4では正常にフォーマットのバリデーションを行うことが出来ません。
ドキュメントにもサブパターンを使いましょうとあります。

バリでショーンが正常に行えない場合ですが、具体的に例を上げると次のような設定だとバリデーションに引っかからずに通ってしまいます。

バリデーションが通ってしまう例

validation2
new sfValidatorDateTime([
    'date_format' => '/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/',
    'min' => strtotime('2017-03-21 13:00:00'),
]));

上記の設定だとフォーマットが違う場合には弾かれるが、最小値より過去の日時を入力した場合、バリデーションを実行した日時が最小値を過ぎてしまっている場合にはバリデーションが通ってしまいまいます。
なぜこのバリデーションが通ってしまうかと言うと、フォーマットのチェックの処理が下記のような実装になっているため、サブパターン付きの正規表現以外では「year」、「month」、「date」のインデックスが存在せず、検査後の値が現在日時となってしまい、最小値の指定をしたとしても最小値が現在よりも過去であれば通ってしまう結果となります。

symfony-1.4/lib/validator/sfValidatorDate.class.php
  protected function convertDateArrayToString($value)
  {
    // all elements must be empty or a number
    foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $key)
    {
      if (isset($value[$key]) && !preg_match('#^\d+$#', $value[$key]) && !empty($value[$key]))
      {
        throw new sfValidatorError($this, 'invalid', array('value' => $value));
      }
    }
    // if one date value is empty, all others must be empty too
    $empties =
      (!isset($value['year']) || !$value['year'] ? 1 : 0) +
      (!isset($value['month']) || !$value['month'] ? 1 : 0) +
      (!isset($value['day']) || !$value['day'] ? 1 : 0)
    ;
    if ($empties > 0 && $empties < 3)
    {
      throw new sfValidatorError($this, 'invalid', array('value' => $value));
    }
    else if (3 == $empties)
    {
      return $this->getEmptyValue();
    }
    ...

まとめ

この通りSymfony1.4で日時のチェックをする際には名前付きのサブパターンが必須となります。
何かをする際にはドキュメントはちゃんと読みましょう。