PageRenderTime 62ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/src/vendor/symfony/src/Symfony/Component/Form/DateField.php

https://github.com/jackbravo/symfony-sandbox
PHP | 278 lines | 161 code | 36 blank | 81 comment | 14 complexity | 6f59b6c5fb7dc32f38cc4cd65cb7c2a6 MD5 | raw file
  1. <?php
  2. namespace Symfony\Component\Form;
  3. /*
  4. * This file is part of the Symfony framework.
  5. *
  6. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  7. *
  8. * This source file is subject to the MIT license that is bundled
  9. * with this source code in the file LICENSE.
  10. */
  11. use Symfony\Component\Form\ValueTransformer\ReversedTransformer;
  12. use Symfony\Component\Form\ValueTransformer\DateTimeToStringTransformer;
  13. use Symfony\Component\Form\ValueTransformer\DateTimeToTimestampTransformer;
  14. use Symfony\Component\Form\ValueTransformer\ValueTransformerChain;
  15. use Symfony\Component\Form\ValueTransformer\DateTimeToLocalizedStringTransformer;
  16. use Symfony\Component\Form\ValueTransformer\DateTimeToArrayTransformer;
  17. class DateField extends HybridField
  18. {
  19. const FULL = 'full';
  20. const LONG = 'long';
  21. const MEDIUM = 'medium';
  22. const SHORT = 'short';
  23. const DATETIME = 'datetime';
  24. const STRING = 'string';
  25. const TIMESTAMP = 'timestamp';
  26. const RAW = 'raw';
  27. const INPUT = 'input';
  28. const CHOICE = 'choice';
  29. protected static $formats = array(
  30. self::FULL,
  31. self::LONG,
  32. self::MEDIUM,
  33. self::SHORT,
  34. );
  35. protected static $intlFormats = array(
  36. self::FULL => \IntlDateFormatter::FULL,
  37. self::LONG => \IntlDateFormatter::LONG,
  38. self::MEDIUM => \IntlDateFormatter::MEDIUM,
  39. self::SHORT => \IntlDateFormatter::SHORT,
  40. );
  41. protected static $widgets = array(
  42. self::INPUT,
  43. self::CHOICE,
  44. );
  45. protected static $types = array(
  46. self::DATETIME,
  47. self::STRING,
  48. self::TIMESTAMP,
  49. self::RAW,
  50. );
  51. /**
  52. * The ICU formatter instance
  53. * @var \IntlDateFormatter
  54. */
  55. protected $formatter;
  56. /**
  57. * Configures the text field.
  58. *
  59. * Available options:
  60. *
  61. * * widget: How to render the field ("input" or "select"). Default: "input"
  62. * * years: An array of years for the year select tag (optional)
  63. * * months: An array of months for the month select tag (optional)
  64. * * days: An array of days for the day select tag (optional)
  65. * * format: See DateValueTransformer. Default: medium
  66. * * type: The type of the date ("date", "datetime" or "timestamp"). Default: "date"
  67. * * data_timezone: The timezone of the data
  68. * * user_timezone: The timezone of the user entering a new value
  69. * * pattern: The pattern for the select boxes when "widget" is "select".
  70. * You can use the placeholders "{{ year }}", "{{ month }}" and "{{ day }}".
  71. * Default: locale dependent
  72. *
  73. * @param array $options Options for this field
  74. * @throws \InvalidArgumentException Thrown if you want to show a timestamp with the select widget.
  75. */
  76. protected function configure()
  77. {
  78. $this->addOption('years', range(date('Y') - 5, date('Y') + 5));
  79. $this->addOption('months', range(1, 12));
  80. $this->addOption('days', range(1, 31));
  81. $this->addOption('format', self::MEDIUM, self::$formats);
  82. $this->addOption('type', self::DATETIME, self::$types);
  83. $this->addOption('data_timezone', 'UTC');
  84. $this->addOption('user_timezone', 'UTC');
  85. $this->addOption('widget', self::CHOICE, self::$widgets);
  86. $this->addOption('pattern');
  87. $this->formatter = new \IntlDateFormatter(
  88. $this->locale,
  89. self::$intlFormats[$this->getOption('format')],
  90. \IntlDateFormatter::NONE
  91. );
  92. if ($this->getOption('type') === self::STRING) {
  93. $this->setNormalizationTransformer(new ReversedTransformer(
  94. new DateTimeToStringTransformer(array(
  95. 'input_timezone' => $this->getOption('data_timezone'),
  96. 'output_timezone' => $this->getOption('data_timezone'),
  97. 'format' => 'Y-m-d',
  98. ))
  99. ));
  100. } else if ($this->getOption('type') === self::TIMESTAMP) {
  101. $this->setNormalizationTransformer(new ReversedTransformer(
  102. new DateTimeToTimestampTransformer(array(
  103. 'output_timezone' => $this->getOption('data_timezone'),
  104. 'input_timezone' => $this->getOption('data_timezone'),
  105. ))
  106. ));
  107. } else if ($this->getOption('type') === self::RAW) {
  108. $this->setNormalizationTransformer(new ReversedTransformer(
  109. new DateTimeToArrayTransformer(array(
  110. 'input_timezone' => $this->getOption('data_timezone'),
  111. 'output_timezone' => $this->getOption('data_timezone'),
  112. 'fields' => array('year', 'month', 'day'),
  113. ))
  114. ));
  115. }
  116. if ($this->getOption('widget') === self::INPUT) {
  117. $this->setValueTransformer(new DateTimeToLocalizedStringTransformer(array(
  118. 'date_format' => $this->getOption('format'),
  119. 'time_format' => DateTimeToLocalizedStringTransformer::NONE,
  120. 'input_timezone' => $this->getOption('data_timezone'),
  121. 'output_timezone' => $this->getOption('user_timezone'),
  122. )));
  123. $this->setFieldMode(self::FIELD);
  124. } else {
  125. $this->setValueTransformer(new DateTimeToArrayTransformer(array(
  126. 'input_timezone' => $this->getOption('data_timezone'),
  127. 'output_timezone' => $this->getOption('user_timezone'),
  128. )));
  129. $this->setFieldMode(self::GROUP);
  130. $this->addChoiceFields();
  131. }
  132. }
  133. /**
  134. * Generates an array of choices for the given values
  135. *
  136. * If the values are shorter than $padLength characters, they are padded with
  137. * zeros on the left side.
  138. *
  139. * @param array $values The available choices
  140. * @param integer $padLength The length to pad the choices
  141. * @return array An array with the input values as keys and the
  142. * padded values as values
  143. */
  144. protected function generatePaddedChoices(array $values, $padLength)
  145. {
  146. $choices = array();
  147. foreach ($values as $value) {
  148. $choices[$value] = str_pad($value, $padLength, '0', STR_PAD_LEFT);
  149. }
  150. return $choices;
  151. }
  152. /**
  153. * Generates an array of localized month choices
  154. *
  155. * @param array $months The month numbers to generate
  156. * @return array The localized months respecting the configured
  157. * locale and date format
  158. */
  159. protected function generateMonthChoices(array $months)
  160. {
  161. $pattern = $this->formatter->getPattern();
  162. if (preg_match('/M+/', $pattern, $matches)) {
  163. $this->formatter->setPattern($matches[0]);
  164. $choices = array();
  165. foreach ($months as $month) {
  166. $choices[$month] = $this->formatter->format(gmmktime(0, 0, 0, $month));
  167. }
  168. $this->formatter->setPattern($pattern);
  169. } else {
  170. $choices = $this->generatePaddedChoices($months, 2);
  171. }
  172. return $choices;
  173. }
  174. public function getPattern()
  175. {
  176. // set order as specified in the pattern
  177. if ($this->getOption('pattern')) {
  178. return $this->getOption('pattern');
  179. }
  180. // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy)
  181. // lookup various formats at http://userguide.icu-project.org/formatparse/datetime
  182. if (preg_match('/^([yMd]+).+([yMd]+).+([yMd]+)$/', $this->formatter->getPattern())) {
  183. return preg_replace(array('/y+/', '/M+/', '/d+/'), array('{{ year }}', '{{ month }}', '{{ day }}'), $this->formatter->getPattern());
  184. }
  185. // default fallback
  186. return '{{ year }}-{{ month }}-{{ day }}';
  187. }
  188. /**
  189. * Adds (or replaces if already added) the fields used when widget=CHOICE
  190. */
  191. protected function addChoiceFields()
  192. {
  193. $this->add(new ChoiceField('year', array(
  194. 'choices' => $this->generatePaddedChoices($this->getOption('years'), 4),
  195. )));
  196. $this->add(new ChoiceField('month', array(
  197. 'choices' => $this->generateMonthChoices($this->getOption('months')),
  198. )));
  199. $this->add(new ChoiceField('day', array(
  200. 'choices' => $this->generatePaddedChoices($this->getOption('days'), 2),
  201. )));
  202. }
  203. /**
  204. * Returns whether the year of the field's data is valid
  205. *
  206. * The year is valid if it is contained in the list passed to the field's
  207. * option "years".
  208. *
  209. * @return boolean
  210. */
  211. public function isYearWithinRange()
  212. {
  213. $date = $this->getNormalizedData();
  214. return null === $date || in_array($date->format('Y'), $this->getOption('years'));
  215. }
  216. /**
  217. * Returns whether the month of the field's data is valid
  218. *
  219. * The month is valid if it is contained in the list passed to the field's
  220. * option "months".
  221. *
  222. * @return boolean
  223. */
  224. public function isMonthWithinRange()
  225. {
  226. $date = $this->getNormalizedData();
  227. return null === $date || in_array($date->format('m'), $this->getOption('months'));
  228. }
  229. /**
  230. * Returns whether the day of the field's data is valid
  231. *
  232. * The day is valid if it is contained in the list passed to the field's
  233. * option "days".
  234. *
  235. * @return boolean
  236. */
  237. public function isDayWithinRange()
  238. {
  239. $date = $this->getNormalizedData();
  240. return null === $date || in_array($date->format('d'), $this->getOption('days'));
  241. }
  242. }