2
2
3
3
namespace SilverStripe \Core \Validation \FieldValidation ;
4
4
5
- use SilverStripe \ Core \ Validation \ FieldValidation \ FieldValidator ;
5
+ use InvalidArgumentException ;
6
6
use SilverStripe \Core \Validation \ValidationResult ;
7
7
8
8
/**
9
9
* Validates that a value is a valid date, which means that it follows the equivalent formats:
10
10
* - PHP date format Y-m-d
11
- * - SO format y-MM-dd i.e. DBDate::ISO_DATE
11
+ * - ISO format y-MM-dd i.e. DBDate::ISO_DATE
12
12
*
13
13
* Blank string values are allowed
14
14
*/
15
- class DateFieldValidator extends FieldValidator
15
+ class DateFieldValidator extends StringFieldValidator
16
16
{
17
+ /**
18
+ * Minimum value as a date string
19
+ */
20
+ private ?string $ minValue = null ;
21
+
22
+ /**
23
+ * Minimum value as a unix timestamp
24
+ */
25
+ private ?int $ minTimestamp = null ;
26
+
27
+ /**
28
+ * Maximum value as a date string
29
+ */
30
+ private ?string $ maxValue = null ;
31
+
32
+ /**
33
+ * Maximum value as a unix timestamp
34
+ */
35
+ private ?int $ maxTimestamp = null ;
36
+
37
+ /**
38
+ * Converter to convert date strings to another format for display in error messages
39
+ *
40
+ * @var callable
41
+ */
42
+ private $ converter ;
43
+
44
+ public function __construct (
45
+ string $ name ,
46
+ mixed $ value ,
47
+ ?string $ minValue = null ,
48
+ ?string $ maxValue = null ,
49
+ ?callable $ converter = null ,
50
+ ) {
51
+ // Convert Y-m-d args to timestamps
52
+ if (!is_null ($ minValue )) {
53
+ $ this ->minTimestamp = $ this ->dateToTimestamp ($ minValue );
54
+ }
55
+ if (!is_null ($ maxValue )) {
56
+ $ this ->maxTimestamp = $ this ->dateToTimestamp ($ maxValue );
57
+ }
58
+ if (!is_null ($ this ->minTimestamp ) && !is_null ($ this ->maxTimestamp )
59
+ && $ this ->maxTimestamp < $ this ->minTimestamp
60
+ ) {
61
+ throw new InvalidArgumentException ('maxValue cannot be less than minValue ' );
62
+ }
63
+ $ this ->minValue = $ minValue ;
64
+ $ this ->maxValue = $ maxValue ;
65
+ $ this ->converter = $ converter ;
66
+ parent ::__construct ($ name , $ value );
67
+ }
68
+
17
69
protected function validateValue (): ValidationResult
18
70
{
19
- $ result = ValidationResult:: create ();
20
- // Allow empty strings
21
- if ($ this -> value === '' ) {
71
+ $ result = parent :: validateValue ();
72
+ // If the value is not a string, we can't validate it as a date
73
+ if (! $ result -> isValid () ) {
22
74
return $ result ;
23
75
}
24
- // Not using symfony/validator because it was allowing d-m-Y format strings
25
- $ date = date_parse_from_format ( $ this ->getFormat (), $ this ->value ?? '' );
26
- if ($ date === false || $ date [ ' error_count ' ] > 0 || $ date [ ' warning_count ' ] > 0 ) {
76
+ // Validate value is a valid date
77
+ $ timestamp = $ this ->dateToTimestamp ( $ this ->value ?? '' );
78
+ if ($ timestamp === false ) {
27
79
$ result ->addFieldError ($ this ->name , $ this ->getMessage ());
80
+ return $ result ;
81
+ }
82
+ // Validate value is within range
83
+ if (!is_null ($ this ->minTimestamp ) && $ timestamp < $ this ->minTimestamp ) {
84
+ $ minValue = $ this ->minValue ;
85
+ if (!is_null ($ this ->converter )) {
86
+ $ minValue = call_user_func ($ this ->converter , $ this ->minValue ) ?: $ this ->minValue ;
87
+ }
88
+ $ message = _t (
89
+ __CLASS__ . '.TOOSMALL ' ,
90
+ 'Value cannot be older than {minValue} ' ,
91
+ ['minValue ' => $ minValue ]
92
+ );
93
+ $ result ->addFieldError ($ this ->name , $ message );
94
+ } elseif (!is_null ($ this ->maxTimestamp ) && $ timestamp > $ this ->maxTimestamp ) {
95
+ $ maxValue = $ this ->maxValue ;
96
+ if (!is_null ($ this ->converter )) {
97
+ $ maxValue = call_user_func ($ this ->converter , $ this ->maxValue ) ?: $ this ->maxValue ;
98
+ }
99
+ $ message = _t (
100
+ __CLASS__ . '.TOOLARGE ' ,
101
+ 'Value cannot be newer than {maxValue} ' ,
102
+ ['maxValue ' => $ maxValue ]
103
+ );
104
+ $ result ->addFieldError ($ this ->name , $ message );
28
105
}
29
106
return $ result ;
30
107
}
@@ -38,4 +115,17 @@ protected function getMessage(): string
38
115
{
39
116
return _t (__CLASS__ . '.INVALID ' , 'Invalid date ' );
40
117
}
118
+
119
+ /**
120
+ * Parse a date string into a unix timestamp using the format specified by getFormat()
121
+ */
122
+ private function dateToTimestamp (string $ date ): int |false
123
+ {
124
+ // Not using symfony/validator because it was allowing d-m-Y format strings
125
+ $ date = date_parse_from_format ($ this ->getFormat (), $ date );
126
+ if ($ date === false || $ date ['error_count ' ] > 0 || $ date ['warning_count ' ] > 0 ) {
127
+ return false ;
128
+ }
129
+ return mktime ($ date ['hour ' ], $ date ['minute ' ], $ date ['second ' ], $ date ['month ' ], $ date ['day ' ], $ date ['year ' ]);
130
+ }
41
131
}
0 commit comments