diff options
Diffstat (limited to 'vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php')
-rw-r--r-- | vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php new file mode 100644 index 00000000..93d3502e --- /dev/null +++ b/vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateType.php | |||
@@ -0,0 +1,309 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of the Symfony package. | ||
5 | * | ||
6 | * (c) Fabien Potencier <fabien@symfony.com> | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | namespace Symfony\Component\Form\Extension\Core\Type; | ||
13 | |||
14 | use Symfony\Component\Form\AbstractType; | ||
15 | use Symfony\Component\Form\FormInterface; | ||
16 | use Symfony\Component\Form\FormBuilderInterface; | ||
17 | use Symfony\Component\Form\FormView; | ||
18 | use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; | ||
19 | use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; | ||
20 | use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; | ||
21 | use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; | ||
22 | use Symfony\Component\Form\ReversedTransformer; | ||
23 | use Symfony\Component\OptionsResolver\Options; | ||
24 | use Symfony\Component\OptionsResolver\OptionsResolverInterface; | ||
25 | use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; | ||
26 | |||
27 | class DateType extends AbstractType | ||
28 | { | ||
29 | const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM; | ||
30 | |||
31 | const HTML5_FORMAT = 'yyyy-MM-dd'; | ||
32 | |||
33 | private static $acceptedFormats = array( | ||
34 | \IntlDateFormatter::FULL, | ||
35 | \IntlDateFormatter::LONG, | ||
36 | \IntlDateFormatter::MEDIUM, | ||
37 | \IntlDateFormatter::SHORT, | ||
38 | ); | ||
39 | |||
40 | /** | ||
41 | * {@inheritdoc} | ||
42 | */ | ||
43 | public function buildForm(FormBuilderInterface $builder, array $options) | ||
44 | { | ||
45 | $dateFormat = is_int($options['format']) ? $options['format'] : self::DEFAULT_FORMAT; | ||
46 | $timeFormat = \IntlDateFormatter::NONE; | ||
47 | $calendar = \IntlDateFormatter::GREGORIAN; | ||
48 | $pattern = is_string($options['format']) ? $options['format'] : null; | ||
49 | |||
50 | if (!in_array($dateFormat, self::$acceptedFormats, true)) { | ||
51 | throw new InvalidOptionsException('The "format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.'); | ||
52 | } | ||
53 | |||
54 | if (null !== $pattern && (false === strpos($pattern, 'y') || false === strpos($pattern, 'M') || false === strpos($pattern, 'd'))) { | ||
55 | throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" and "d". Its current value is "%s".', $pattern)); | ||
56 | } | ||
57 | |||
58 | if ('single_text' === $options['widget']) { | ||
59 | $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer( | ||
60 | $options['model_timezone'], | ||
61 | $options['view_timezone'], | ||
62 | $dateFormat, | ||
63 | $timeFormat, | ||
64 | $calendar, | ||
65 | $pattern | ||
66 | )); | ||
67 | } else { | ||
68 | $yearOptions = $monthOptions = $dayOptions = array( | ||
69 | 'error_bubbling' => true, | ||
70 | ); | ||
71 | |||
72 | $formatter = new \IntlDateFormatter( | ||
73 | \Locale::getDefault(), | ||
74 | $dateFormat, | ||
75 | $timeFormat, | ||
76 | 'UTC', | ||
77 | $calendar, | ||
78 | $pattern | ||
79 | ); | ||
80 | $formatter->setLenient(false); | ||
81 | |||
82 | if ('choice' === $options['widget']) { | ||
83 | // Only pass a subset of the options to children | ||
84 | $yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years'])); | ||
85 | $yearOptions['empty_value'] = $options['empty_value']['year']; | ||
86 | $monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months'])); | ||
87 | $monthOptions['empty_value'] = $options['empty_value']['month']; | ||
88 | $dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days'])); | ||
89 | $dayOptions['empty_value'] = $options['empty_value']['day']; | ||
90 | } | ||
91 | |||
92 | // Append generic carry-along options | ||
93 | foreach (array('required', 'translation_domain') as $passOpt) { | ||
94 | $yearOptions[$passOpt] = $monthOptions[$passOpt] = $dayOptions[$passOpt] = $options[$passOpt]; | ||
95 | } | ||
96 | |||
97 | $builder | ||
98 | ->add('year', $options['widget'], $yearOptions) | ||
99 | ->add('month', $options['widget'], $monthOptions) | ||
100 | ->add('day', $options['widget'], $dayOptions) | ||
101 | ->addViewTransformer(new DateTimeToArrayTransformer( | ||
102 | $options['model_timezone'], $options['view_timezone'], array('year', 'month', 'day') | ||
103 | )) | ||
104 | ->setAttribute('formatter', $formatter) | ||
105 | ; | ||
106 | } | ||
107 | |||
108 | if ('string' === $options['input']) { | ||
109 | $builder->addModelTransformer(new ReversedTransformer( | ||
110 | new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d') | ||
111 | )); | ||
112 | } elseif ('timestamp' === $options['input']) { | ||
113 | $builder->addModelTransformer(new ReversedTransformer( | ||
114 | new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone']) | ||
115 | )); | ||
116 | } elseif ('array' === $options['input']) { | ||
117 | $builder->addModelTransformer(new ReversedTransformer( | ||
118 | new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], array('year', 'month', 'day')) | ||
119 | )); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * {@inheritdoc} | ||
125 | */ | ||
126 | public function finishView(FormView $view, FormInterface $form, array $options) | ||
127 | { | ||
128 | $view->vars['widget'] = $options['widget']; | ||
129 | |||
130 | // Change the input to a HTML5 date input if | ||
131 | // * the widget is set to "single_text" | ||
132 | // * the format matches the one expected by HTML5 | ||
133 | if ('single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) { | ||
134 | $view->vars['type'] = 'date'; | ||
135 | } | ||
136 | |||
137 | if ($form->getConfig()->hasAttribute('formatter')) { | ||
138 | $pattern = $form->getConfig()->getAttribute('formatter')->getPattern(); | ||
139 | |||
140 | // remove special characters unless the format was explicitly specified | ||
141 | if (!is_string($options['format'])) { | ||
142 | $pattern = preg_replace('/[^yMd]+/', '', $pattern); | ||
143 | } | ||
144 | |||
145 | // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy) | ||
146 | // lookup various formats at http://userguide.icu-project.org/formatparse/datetime | ||
147 | if (preg_match('/^([yMd]+)[^yMd]*([yMd]+)[^yMd]*([yMd]+)$/', $pattern)) { | ||
148 | $pattern = preg_replace(array('/y+/', '/M+/', '/d+/'), array('{{ year }}', '{{ month }}', '{{ day }}'), $pattern); | ||
149 | } else { | ||
150 | // default fallback | ||
151 | $pattern = '{{ year }}{{ month }}{{ day }}'; | ||
152 | } | ||
153 | |||
154 | $view->vars['date_pattern'] = $pattern; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * {@inheritdoc} | ||
160 | */ | ||
161 | public function setDefaultOptions(OptionsResolverInterface $resolver) | ||
162 | { | ||
163 | $compound = function (Options $options) { | ||
164 | return $options['widget'] !== 'single_text'; | ||
165 | }; | ||
166 | |||
167 | $emptyValue = $emptyValueDefault = function (Options $options) { | ||
168 | return $options['required'] ? null : ''; | ||
169 | }; | ||
170 | |||
171 | $emptyValueNormalizer = function (Options $options, $emptyValue) use ($emptyValueDefault) { | ||
172 | if (is_array($emptyValue)) { | ||
173 | $default = $emptyValueDefault($options); | ||
174 | |||
175 | return array_merge( | ||
176 | array('year' => $default, 'month' => $default, 'day' => $default), | ||
177 | $emptyValue | ||
178 | ); | ||
179 | } | ||
180 | |||
181 | return array( | ||
182 | 'year' => $emptyValue, | ||
183 | 'month' => $emptyValue, | ||
184 | 'day' => $emptyValue | ||
185 | ); | ||
186 | }; | ||
187 | |||
188 | $format = function (Options $options) { | ||
189 | return $options['widget'] === 'single_text' ? DateType::HTML5_FORMAT : DateType::DEFAULT_FORMAT; | ||
190 | }; | ||
191 | |||
192 | $resolver->setDefaults(array( | ||
193 | 'years' => range(date('Y') - 5, date('Y') + 5), | ||
194 | 'months' => range(1, 12), | ||
195 | 'days' => range(1, 31), | ||
196 | 'widget' => 'choice', | ||
197 | 'input' => 'datetime', | ||
198 | 'format' => $format, | ||
199 | 'model_timezone' => null, | ||
200 | 'view_timezone' => null, | ||
201 | 'empty_value' => $emptyValue, | ||
202 | // Don't modify \DateTime classes by reference, we treat | ||
203 | // them like immutable value objects | ||
204 | 'by_reference' => false, | ||
205 | 'error_bubbling' => false, | ||
206 | // If initialized with a \DateTime object, FormType initializes | ||
207 | // this option to "\DateTime". Since the internal, normalized | ||
208 | // representation is not \DateTime, but an array, we need to unset | ||
209 | // this option. | ||
210 | 'data_class' => null, | ||
211 | 'compound' => $compound, | ||
212 | )); | ||
213 | |||
214 | $resolver->setNormalizers(array( | ||
215 | 'empty_value' => $emptyValueNormalizer, | ||
216 | )); | ||
217 | |||
218 | $resolver->setAllowedValues(array( | ||
219 | 'input' => array( | ||
220 | 'datetime', | ||
221 | 'string', | ||
222 | 'timestamp', | ||
223 | 'array', | ||
224 | ), | ||
225 | 'widget' => array( | ||
226 | 'single_text', | ||
227 | 'text', | ||
228 | 'choice', | ||
229 | ), | ||
230 | )); | ||
231 | |||
232 | $resolver->setAllowedTypes(array( | ||
233 | 'format' => array('int', 'string'), | ||
234 | )); | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * {@inheritdoc} | ||
239 | */ | ||
240 | public function getName() | ||
241 | { | ||
242 | return 'date'; | ||
243 | } | ||
244 | |||
245 | private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $timestamps) | ||
246 | { | ||
247 | $pattern = $formatter->getPattern(); | ||
248 | $timezone = $formatter->getTimezoneId(); | ||
249 | |||
250 | if (version_compare(\PHP_VERSION, '5.5.0-dev', '>=')) { | ||
251 | $formatter->setTimeZone(\DateTimeZone::UTC); | ||
252 | } else { | ||
253 | $formatter->setTimeZoneId(\DateTimeZone::UTC); | ||
254 | } | ||
255 | |||
256 | if (preg_match($regex, $pattern, $matches)) { | ||
257 | $formatter->setPattern($matches[0]); | ||
258 | |||
259 | foreach ($timestamps as $key => $timestamp) { | ||
260 | $timestamps[$key] = $formatter->format($timestamp); | ||
261 | } | ||
262 | |||
263 | // I'd like to clone the formatter above, but then we get a | ||
264 | // segmentation fault, so let's restore the old state instead | ||
265 | $formatter->setPattern($pattern); | ||
266 | } | ||
267 | |||
268 | if (version_compare(\PHP_VERSION, '5.5.0-dev', '>=')) { | ||
269 | $formatter->setTimeZone($timezone); | ||
270 | } else { | ||
271 | $formatter->setTimeZoneId($timezone); | ||
272 | } | ||
273 | |||
274 | return $timestamps; | ||
275 | } | ||
276 | |||
277 | private function listYears(array $years) | ||
278 | { | ||
279 | $result = array(); | ||
280 | |||
281 | foreach ($years as $year) { | ||
282 | $result[$year] = gmmktime(0, 0, 0, 6, 15, $year); | ||
283 | } | ||
284 | |||
285 | return $result; | ||
286 | } | ||
287 | |||
288 | private function listMonths(array $months) | ||
289 | { | ||
290 | $result = array(); | ||
291 | |||
292 | foreach ($months as $month) { | ||
293 | $result[$month] = gmmktime(0, 0, 0, $month, 15); | ||
294 | } | ||
295 | |||
296 | return $result; | ||
297 | } | ||
298 | |||
299 | private function listDays(array $days) | ||
300 | { | ||
301 | $result = array(); | ||
302 | |||
303 | foreach ($days as $day) { | ||
304 | $result[$day] = gmmktime(0, 0, 0, 5, $day); | ||
305 | } | ||
306 | |||
307 | return $result; | ||
308 | } | ||
309 | } | ||