HtmlSelectDate   F
last analyzed

Complexity

Total Complexity 127

Size/Duplication

Total Lines 339
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 255
dl 0
loc 339
rs 2
c 1
b 0
f 0
wmc 127

1 Method

Rating   Name   Duplication   Size   Complexity  
F handle() 0 337 127

How to fix   Complexity   

Complex Class

Complex classes like HtmlSelectDate often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HtmlSelectDate, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Smarty\FunctionHandler;
3
4
use Smarty\Template;
5
6
/**
7
 * Smarty {html_select_date} plugin
8
 * Type:     function
9
 * Name:     html_select_date
10
 * Purpose:  Prints the dropdowns for date selection.
11
 * ChangeLog:
12
 *
13
 *            - 1.0 initial release
14
 *            - 1.1 added support for +/- N syntax for begin
15
 *              and end year values. (Monte)
16
 *            - 1.2 added support for yyyy-mm-dd syntax for
17
 *              time value. (Jan Rosier)
18
 *            - 1.3 added support for choosing format for
19
 *              month values (Gary Loescher)
20
 *            - 1.3.1 added support for choosing format for
21
 *              day values (Marcus Bointon)
22
 *            - 1.3.2 support negative timestamps, force year
23
 *              dropdown to include given date unless explicitly set (Monte)
24
 *            - 1.3.4 fix behaviour of 0000-00-00 00:00:00 dates to match that
25
 *              of 0000-00-00 dates (cybot, boots)
26
 *            - 2.0 complete rewrite for performance,
27
 *              added attributes month_names, *_id
28
 *
29
 * @version 2.0
30
 * @author  Andrei Zmievski
31
 * @author  Monte Ohrt <monte at ohrt dot com>
32
 * @author  Rodney Rehm
33
 *
34
 * @param array                     $params parameters
35
 *
36
 * @param \Smarty\Template $template
37
 *
38
 * @return string
39
 * @throws \Smarty\Exception
40
 */
41
class HtmlSelectDate extends Base {
42
43
	public function handle($params, Template $template) {
44
		// generate timestamps used for month names only
45
		static $_month_timestamps = null;
46
		static $_current_year = null;
47
		if ($_month_timestamps === null) {
48
			$_current_year = date('Y');
49
			$_month_timestamps = [];
50
			for ($i = 1; $i <= 12; $i++) {
51
				$_month_timestamps[$i] = mktime(0, 0, 0, $i, 1, 2000);
52
			}
53
		}
54
		/* Default values. */
55
		$prefix = 'Date_';
56
		$start_year = null;
57
		$end_year = null;
58
		$display_days = true;
59
		$display_months = true;
60
		$display_years = true;
61
		$month_format = '%B';
62
		/* Write months as numbers by default  GL */
63
		$month_value_format = '%m';
64
		$day_format = '%02d';
65
		/* Write day values using this format MB */
66
		$day_value_format = '%d';
67
		$year_as_text = false;
68
		/* Display years in reverse order? Ie. 2000,1999,.... */
69
		$reverse_years = false;
70
		/* Should the select boxes be part of an array when returned from PHP?
71
		   e.g. setting it to "birthday", would create "birthday[Day]",
72
		   "birthday[Month]" & "birthday[Year]". Can be combined with prefix */
73
		$field_array = null;
74
		/* <select size>'s of the different <select> tags.
75
		   If not set, uses default dropdown. */
76
		$day_size = null;
77
		$month_size = null;
78
		$year_size = null;
79
		/* Unparsed attributes common to *ALL* the <select>/<input> tags.
80
		   An example might be in the template: all_extra ='class ="foo"'. */
81
		$all_extra = null;
82
		/* Separate attributes for the tags. */
83
		$day_extra = null;
84
		$month_extra = null;
85
		$year_extra = null;
86
		/* Order in which to display the fields.
87
		   "D" -> day, "M" -> month, "Y" -> year. */
88
		$field_order = 'MDY';
89
		/* String printed between the different fields. */
90
		$field_separator = "\n";
91
		$option_separator = "\n";
92
		$time = null;
93
94
		// $all_empty = null;
95
		// $day_empty = null;
96
		// $month_empty = null;
97
		// $year_empty = null;
98
		$extra_attrs = '';
99
		$all_id = null;
100
		$day_id = null;
101
		$month_id = null;
102
		$year_id = null;
103
		foreach ($params as $_key => $_value) {
104
			switch ($_key) {
105
				case 'time':
106
					$$_key = $_value; // we'll handle conversion below
107
					break;
108
				case 'month_names':
109
					if (is_array($_value) && count($_value) === 12) {
110
						$$_key = $_value;
111
					} else {
112
						trigger_error('html_select_date: month_names must be an array of 12 strings', E_USER_NOTICE);
113
					}
114
					break;
115
				case 'prefix':
116
				case 'field_array':
117
				case 'start_year':
118
				case 'end_year':
119
				case 'day_format':
120
				case 'day_value_format':
121
				case 'month_format':
122
				case 'month_value_format':
123
				case 'day_size':
124
				case 'month_size':
125
				case 'year_size':
126
				case 'all_extra':
127
				case 'day_extra':
128
				case 'month_extra':
129
				case 'year_extra':
130
				case 'field_order':
131
				case 'field_separator':
132
				case 'option_separator':
133
				case 'all_empty':
134
				case 'month_empty':
135
				case 'day_empty':
136
				case 'year_empty':
137
				case 'all_id':
138
				case 'month_id':
139
				case 'day_id':
140
				case 'year_id':
141
					$$_key = (string)$_value;
142
					break;
143
				case 'display_days':
144
				case 'display_months':
145
				case 'display_years':
146
				case 'year_as_text':
147
				case 'reverse_years':
148
					$$_key = (bool)$_value;
149
					break;
150
				default:
151
					if (!is_array($_value)) {
152
						$extra_attrs .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_value) . '"';
153
					} else {
154
						trigger_error("html_select_date: extra attribute '{$_key}' cannot be an array", E_USER_NOTICE);
155
					}
156
					break;
157
			}
158
		}
159
		// Note: date() is faster than strftime()
160
		// Note: explode(date()) is faster than date() date() date()
161
162
		if (isset($time) && is_array($time)) {
163
			if (isset($time[$prefix . 'Year'])) {
164
				// $_REQUEST[$field_array] given
165
				foreach ([
166
					         'Y' => 'Year',
167
					         'm' => 'Month',
168
					         'd' => 'Day'
169
				         ] as $_elementKey => $_elementName) {
170
					$_variableName = '_' . strtolower($_elementName);
171
					$$_variableName =
172
						$time[$prefix . $_elementName] ?? date($_elementKey);
173
				}
174
			} elseif (isset($time[$field_array][$prefix . 'Year'])) {
175
				// $_REQUEST given
176
				foreach ([
177
					         'Y' => 'Year',
178
					         'm' => 'Month',
179
					         'd' => 'Day'
180
				         ] as $_elementKey => $_elementName) {
181
					$_variableName = '_' . strtolower($_elementName);
182
					$$_variableName = $time[$field_array][$prefix . $_elementName] ?? date($_elementKey);
183
				}
184
			} else {
185
				// no date found, use NOW
186
				[$_year, $_month, $_day] = explode('-', date('Y-m-d'));
187
			}
188
		} elseif (isset($time) && preg_match("/(\d*)-(\d*)-(\d*)/", $time, $matches)) {
189
			$_year = $_month = $_day = null;
190
			if ($matches[1] > '') {
191
				$_year = (int)$matches[1];
192
			}
193
			if ($matches[2] > '') {
194
				$_month = (int)$matches[2];
195
			}
196
			if ($matches[3] > '') {
197
				$_day = (int)$matches[3];
198
			}
199
		} elseif ($time === null) {
200
			if (array_key_exists('time', $params)) {
201
				$_year = $_month = $_day = null;
202
			} else {
203
				[$_year, $_month, $_day] = explode('-', date('Y-m-d'));
204
			}
205
		} else {
206
			$time = smarty_make_timestamp($time);
207
			[$_year, $_month, $_day] = explode('-', date('Y-m-d', $time));
208
		}
209
210
		// make syntax "+N" or "-N" work with $start_year and $end_year
211
		// Note preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match) is slower than trim+substr
212
		foreach ([
213
			         'start',
214
			         'end'
215
		         ] as $key) {
216
			$key .= '_year';
217
			$t = $$key;
218
			if ($t === null) {
219
				$$key = (int)$_current_year;
220
			} elseif ($t[0] === '+') {
221
				$$key = (int)($_current_year + (int)trim(substr($t, 1)));
222
			} elseif ($t[0] === '-') {
223
				$$key = (int)($_current_year - (int)trim(substr($t, 1)));
224
			} else {
225
				$$key = (int)$$key;
226
			}
227
		}
228
		// flip for ascending or descending
229
		if (($start_year > $end_year && !$reverse_years) || ($start_year < $end_year && $reverse_years)) {
230
			$t = $end_year;
231
			$end_year = $start_year;
232
			$start_year = $t;
233
		}
234
		// generate year <select> or <input>
235
		if ($display_years) {
0 ignored issues
show
introduced by
The condition $display_years is always true.
Loading history...
236
			$_extra = '';
237
			$_name = $field_array ? ($field_array . '[' . $prefix . 'Year]') : ($prefix . 'Year');
0 ignored issues
show
introduced by
$field_array is of type null, thus it always evaluated to false.
Loading history...
238
			if ($all_extra) {
0 ignored issues
show
introduced by
$all_extra is of type null, thus it always evaluated to false.
Loading history...
239
				$_extra .= ' ' . $all_extra;
240
			}
241
			if ($year_extra) {
0 ignored issues
show
introduced by
$year_extra is of type null, thus it always evaluated to false.
Loading history...
242
				$_extra .= ' ' . $year_extra;
243
			}
244
			if ($year_as_text) {
0 ignored issues
show
introduced by
The condition $year_as_text is always false.
Loading history...
245
				$_html_years =
246
					'<input type="text" name="' . $_name . '" value="' . $_year . '" size="4" maxlength="4"' . $_extra .
247
					$extra_attrs . ' />';
248
			} else {
249
				$_html_years = '<select name="' . $_name . '"';
250
				if ($year_id !== null || $all_id !== null) {
0 ignored issues
show
introduced by
The condition $all_id !== null is always false.
Loading history...
251
					$_html_years .= ' id="' . smarty_function_escape_special_chars(
252
							$year_id !== null ?
253
								($year_id ? $year_id : $_name) :
254
								($all_id ? ($all_id . $_name) :
255
									$_name)
256
						) . '"';
257
				}
258
				if ($year_size) {
0 ignored issues
show
introduced by
$year_size is of type null, thus it always evaluated to false.
Loading history...
259
					$_html_years .= ' size="' . $year_size . '"';
260
				}
261
				$_html_years .= $_extra . $extra_attrs . '>' . $option_separator;
262
				if (isset($year_empty) || isset($all_empty)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $all_empty seems to never exist and therefore isset should always be false.
Loading history...
Comprehensibility Best Practice introduced by
The variable $year_empty does not exist. Did you maybe mean $year_extra?
Loading history...
263
					$_html_years .= '<option value="">' . (isset($year_empty) ? $year_empty : $all_empty) . '</option>' .
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $all_empty does not seem to be defined for all execution paths leading up to this point.
Loading history...
264
						$option_separator;
265
				}
266
				$op = $start_year > $end_year ? -1 : 1;
267
				for ($i = $start_year; $op > 0 ? $i <= $end_year : $i >= $end_year; $i += $op) {
268
					$_html_years .= '<option value="' . $i . '"' . ($_year == $i ? ' selected="selected"' : '') . '>' . $i .
269
						'</option>' . $option_separator;
270
				}
271
				$_html_years .= '</select>';
272
			}
273
		}
274
		// generate month <select> or <input>
275
		if ($display_months) {
0 ignored issues
show
introduced by
The condition $display_months is always true.
Loading history...
276
			$_extra = '';
277
			$_name = $field_array ? ($field_array . '[' . $prefix . 'Month]') : ($prefix . 'Month');
0 ignored issues
show
introduced by
$field_array is of type null, thus it always evaluated to false.
Loading history...
278
			if ($all_extra) {
0 ignored issues
show
introduced by
$all_extra is of type null, thus it always evaluated to false.
Loading history...
279
				$_extra .= ' ' . $all_extra;
280
			}
281
			if ($month_extra) {
0 ignored issues
show
introduced by
$month_extra is of type null, thus it always evaluated to false.
Loading history...
282
				$_extra .= ' ' . $month_extra;
283
			}
284
			$_html_months = '<select name="' . $_name . '"';
285
			if ($month_id !== null || $all_id !== null) {
0 ignored issues
show
introduced by
The condition $all_id !== null is always false.
Loading history...
286
				$_html_months .= ' id="' . smarty_function_escape_special_chars(
287
						$month_id !== null ?
288
							($month_id ? $month_id : $_name) :
289
							($all_id ? ($all_id . $_name) :
290
								$_name)
291
					) . '"';
292
			}
293
			if ($month_size) {
0 ignored issues
show
introduced by
$month_size is of type null, thus it always evaluated to false.
Loading history...
294
				$_html_months .= ' size="' . $month_size . '"';
295
			}
296
			$_html_months .= $_extra . $extra_attrs . '>' . $option_separator;
297
			if (isset($month_empty) || isset($all_empty)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $month_empty does not exist. Did you maybe mean $month_extra?
Loading history...
298
				$_html_months .= '<option value="">' . (isset($month_empty) ? $month_empty : $all_empty) . '</option>' .
299
					$option_separator;
300
			}
301
			for ($i = 1; $i <= 12; $i++) {
302
				$_val = sprintf('%02d', $i);
303
				$_text = isset($month_names) ? smarty_function_escape_special_chars($month_names[$i]) :
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $month_names does not exist. Did you maybe mean $month_size?
Loading history...
304
					($month_format === '%m' ? $_val : @strftime($month_format, $_month_timestamps[$i]));
305
				$_value = $month_value_format === '%m' ? $_val : @strftime($month_value_format, $_month_timestamps[$i]);
306
				$_html_months .= '<option value="' . $_value . '"' . ($_val == $_month ? ' selected="selected"' : '') .
0 ignored issues
show
Bug introduced by
Are you sure $_value of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

306
				$_html_months .= '<option value="' . /** @scrutinizer ignore-type */ $_value . '"' . ($_val == $_month ? ' selected="selected"' : '') .
Loading history...
307
					'>' . $_text . '</option>' . $option_separator;
0 ignored issues
show
Bug introduced by
Are you sure $_text of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

307
					'>' . /** @scrutinizer ignore-type */ $_text . '</option>' . $option_separator;
Loading history...
308
			}
309
			$_html_months .= '</select>';
310
		}
311
		// generate day <select> or <input>
312
		if ($display_days) {
0 ignored issues
show
introduced by
The condition $display_days is always true.
Loading history...
313
			$_extra = '';
314
			$_name = $field_array ? ($field_array . '[' . $prefix . 'Day]') : ($prefix . 'Day');
0 ignored issues
show
introduced by
$field_array is of type null, thus it always evaluated to false.
Loading history...
315
			if ($all_extra) {
0 ignored issues
show
introduced by
$all_extra is of type null, thus it always evaluated to false.
Loading history...
316
				$_extra .= ' ' . $all_extra;
317
			}
318
			if ($day_extra) {
0 ignored issues
show
introduced by
$day_extra is of type null, thus it always evaluated to false.
Loading history...
319
				$_extra .= ' ' . $day_extra;
320
			}
321
			$_html_days = '<select name="' . $_name . '"';
322
			if ($day_id !== null || $all_id !== null) {
0 ignored issues
show
introduced by
The condition $all_id !== null is always false.
Loading history...
323
				$_html_days .= ' id="' .
324
					smarty_function_escape_special_chars(
325
						$day_id !== null ? ($day_id ? $day_id : $_name) :
326
							($all_id ? ($all_id . $_name) : $_name)
327
					) . '"';
328
			}
329
			if ($day_size) {
0 ignored issues
show
introduced by
$day_size is of type null, thus it always evaluated to false.
Loading history...
330
				$_html_days .= ' size="' . $day_size . '"';
331
			}
332
			$_html_days .= $_extra . $extra_attrs . '>' . $option_separator;
333
			if (isset($day_empty) || isset($all_empty)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $day_empty seems to never exist and therefore isset should always be false.
Loading history...
334
				$_html_days .= '<option value="">' . (isset($day_empty) ? $day_empty : $all_empty) . '</option>' .
335
					$option_separator;
336
			}
337
			for ($i = 1; $i <= 31; $i++) {
338
				$_val = sprintf('%02d', $i);
339
				$_text = $day_format === '%02d' ? $_val : sprintf($day_format, $i);
340
				$_value = $day_value_format === '%02d' ? $_val : sprintf($day_value_format, $i);
341
				$_html_days .= '<option value="' . $_value . '"' . ($_val == $_day ? ' selected="selected"' : '') . '>' .
342
					$_text . '</option>' . $option_separator;
343
			}
344
			$_html_days .= '</select>';
345
		}
346
		// order the fields for output
347
		$_html = '';
348
		for ($i = 0; $i <= 2; $i++) {
349
			switch ($field_order[$i]) {
350
				case 'Y':
351
				case 'y':
352
					if (isset($_html_years)) {
353
						if ($_html) {
354
							$_html .= $field_separator;
355
						}
356
						$_html .= $_html_years;
357
					}
358
					break;
359
				case 'm':
360
				case 'M':
361
					if (isset($_html_months)) {
362
						if ($_html) {
363
							$_html .= $field_separator;
364
						}
365
						$_html .= $_html_months;
366
					}
367
					break;
368
				case 'd':
369
				case 'D':
370
					if (isset($_html_days)) {
371
						if ($_html) {
372
							$_html .= $field_separator;
373
						}
374
						$_html .= $_html_days;
375
					}
376
					break;
377
			}
378
		}
379
		return $_html;
380
	}
381
}