sfValidatorDate.class.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * sfValidatorDate validates a date. It also converts the input value to a valid date.
  11. *
  12. * @package symfony
  13. * @subpackage validator
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @version SVN: $Id: sfValidatorDate.class.php 13278 2008-11-23 15:04:24Z FabianLange $
  16. */
  17. class sfValidatorDate extends sfValidatorBase
  18. {
  19. /**
  20. * Configures the current validator.
  21. *
  22. * Available options:
  23. *
  24. * * date_format: A regular expression that dates must match
  25. * * with_time: true if the validator must return a time, false otherwise
  26. * * date_output: The format to use when returning a date (default to Y-m-d)
  27. * * datetime_output: The format to use when returning a date with time (default to Y-m-d H:i:s)
  28. * * date_format_error: The date format to use when displaying an error for a bad_format error (use date_format if not provided)
  29. * * max: The maximum date allowed (as a timestamp)
  30. * * min: The minimum date allowed (as a timestamp)
  31. * * date_format_range_error: The date format to use when displaying an error for min/max (default to d/m/Y H:i:s)
  32. *
  33. * Available error codes:
  34. *
  35. * * bad_format
  36. * * min
  37. * * max
  38. *
  39. * @param array $options An array of options
  40. * @param array $messages An array of error messages
  41. *
  42. * @see sfValidatorBase
  43. */
  44. protected function configure($options = array(), $messages = array())
  45. {
  46. $this->addMessage('bad_format', '"%value%" does not match the date format (%date_format%).');
  47. $this->addMessage('max', 'The date must be before %max%.');
  48. $this->addMessage('min', 'The date must be after %min%.');
  49. $this->addOption('date_format', null);
  50. $this->addOption('with_time', false);
  51. $this->addOption('date_output', 'Y-m-d');
  52. $this->addOption('datetime_output', 'Y-m-d H:i:s');
  53. $this->addOption('date_format_error');
  54. $this->addOption('min', null);
  55. $this->addOption('max', null);
  56. $this->addOption('date_format_range_error', 'd/m/Y H:i:s');
  57. }
  58. /**
  59. * @see sfValidatorBase
  60. */
  61. protected function doClean($value)
  62. {
  63. if (is_array($value))
  64. {
  65. $clean = $this->convertDateArrayToTimestamp($value);
  66. }
  67. else if ($regex = $this->getOption('date_format'))
  68. {
  69. if (!preg_match($regex, $value, $match))
  70. {
  71. throw new sfValidatorError($this, 'bad_format', array('value' => $value, 'date_format' => $this->getOption('date_format_error') ? $this->getOption('date_format_error') : $this->getOption('date_format')));
  72. }
  73. $clean = $this->convertDateArrayToTimestamp($match);
  74. }
  75. else if (!ctype_digit($value))
  76. {
  77. $clean = strtotime($value);
  78. if (false === $clean)
  79. {
  80. throw new sfValidatorError($this, 'invalid', array('value' => $value));
  81. }
  82. }
  83. else
  84. {
  85. $clean = (integer) $value;
  86. }
  87. if ($this->hasOption('max') && $clean > $this->getOption('max'))
  88. {
  89. throw new sfValidatorError($this, 'max', array('value' => $value, 'max' => date($this->getOption('date_format_range_error'), $this->getOption('max'))));
  90. }
  91. if ($this->hasOption('min') && $clean < $this->getOption('min'))
  92. {
  93. throw new sfValidatorError($this, 'min', array('value' => $value, 'min' => date($this->getOption('date_format_range_error'), $this->getOption('min'))));
  94. }
  95. return $clean === $this->getEmptyValue() ? $clean : date($this->getOption('with_time') ? $this->getOption('datetime_output') : $this->getOption('date_output'), $clean);
  96. }
  97. /**
  98. * Converts an array representing a date to a timestamp.
  99. *
  100. * The array can contains the following keys: year, month, day, hour, minute, second
  101. *
  102. * @param array $value An array of date elements
  103. *
  104. * @return int A timestamp
  105. */
  106. protected function convertDateArrayToTimestamp($value)
  107. {
  108. // all elements must be empty or a number
  109. foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $key)
  110. {
  111. if (isset($value[$key]) && !preg_match('#^\d+$#', $value[$key]) && !empty($value[$key]))
  112. {
  113. throw new sfValidatorError($this, 'invalid', array('value' => $value));
  114. }
  115. }
  116. // if one date value is empty, all others must be empty too
  117. $empties =
  118. (!isset($value['year']) || !$value['year'] ? 1 : 0) +
  119. (!isset($value['month']) || !$value['month'] ? 1 : 0) +
  120. (!isset($value['day']) || !$value['day'] ? 1 : 0)
  121. ;
  122. if ($empties > 0 && $empties < 3)
  123. {
  124. throw new sfValidatorError($this, 'invalid', array('value' => $value));
  125. }
  126. else if (3 == $empties)
  127. {
  128. return $this->getEmptyValue();
  129. }
  130. if (!checkdate(intval($value['month']), intval($value['day']), intval($value['year'])))
  131. {
  132. throw new sfValidatorError($this, 'invalid', array('value' => $value));
  133. }
  134. if ($this->getOption('with_time'))
  135. {
  136. // if second is set, minute and hour must be set
  137. // if minute is set, hour must be set
  138. if (
  139. $this->isValueSet($value, 'second') && (!$this->isValueSet($value, 'minute') || !$this->isValueSet($value, 'hour'))
  140. ||
  141. $this->isValueSet($value, 'minute') && !$this->isValueSet($value, 'hour')
  142. )
  143. {
  144. throw new sfValidatorError($this, 'invalid', array('value' => $value));
  145. }
  146. $clean = mktime(
  147. isset($value['hour']) ? intval($value['hour']) : 0,
  148. isset($value['minute']) ? intval($value['minute']) : 0,
  149. isset($value['second']) ? intval($value['second']) : 0,
  150. intval($value['month']),
  151. intval($value['day']),
  152. intval($value['year'])
  153. );
  154. }
  155. else
  156. {
  157. $clean = mktime(0, 0, 0, intval($value['month']), intval($value['day']), intval($value['year']));
  158. }
  159. if (false === $clean)
  160. {
  161. throw new sfValidatorError($this, 'invalid', array('value' => var_export($value, true)));
  162. }
  163. return $clean;
  164. }
  165. protected function isValueSet($values, $key)
  166. {
  167. return isset($values[$key]) && !in_array($values[$key], array(null, ''), true);
  168. }
  169. /**
  170. * @see sfValidatorBase
  171. */
  172. protected function isEmpty($value)
  173. {
  174. if (is_array($value))
  175. {
  176. $filtered = array_filter($value);
  177. return empty($filtered);
  178. }
  179. return parent::isEmpty($value);
  180. }
  181. }