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