Skip to content

Commit ea31d23

Browse files
More validation on ranges + tweak error messages (#5)
* Initial plan * Add validation for backwards ranges in cron expressions Co-authored-by: jahidulpabelislam <15434150+jahidulpabelislam@users.noreply.github.com> * Fix month/day name conversion for backwards range validation Co-authored-by: jahidulpabelislam <15434150+jahidulpabelislam@users.noreply.github.com> * Add docblock and improve code formatting Co-authored-by: jahidulpabelislam <15434150+jahidulpabelislam@users.noreply.github.com> * Remove unused parameter from convertToNumericValue method Co-authored-by: jahidulpabelislam <15434150+jahidulpabelislam@users.noreply.github.com> * Change error message from 'range is backwards' to 'values must be ordered' Co-authored-by: jahidulpabelislam <15434150+jahidulpabelislam@users.noreply.github.com> * Only check range ordering for numeric values, not named values Co-authored-by: jahidulpabelislam <15434150+jahidulpabelislam@users.noreply.github.com> * Tweak * Add back lost tests * Tweak tweaks * Tweak error * Reduce line length * Tweak errors * Tweak error messages * Show full range in error * Test mix of numeric and test --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jahidulpabelislam <15434150+jahidulpabelislam@users.noreply.github.com> Co-authored-by: Jahidul Pabel Islam <jahidul.pabel.islam@hotmail.com> Co-authored-by: Jahidul Pabel Islam <jahidul@d3r.com>
1 parent bd089de commit ea31d23

File tree

2 files changed

+105
-73
lines changed

2 files changed

+105
-73
lines changed

src/CronLinter.php

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function ($v) {
104104
];
105105

106106
$defaultRegex = "/^(\d{1,2}|\*)$/";
107-
$errorPrefix = "Line $lineNo has invalid value for";
107+
$errorPrefix = "Line $lineNo contains an invalid value for";
108108

109109
foreach ($checks as $name => $data) {
110110
$offset = 0;
@@ -113,12 +113,14 @@ function ($v) {
113113
$values = explode(",", $data["values"]);
114114
$hasMultipleValues = count($values) > 1;
115115
foreach ($values as $value) {
116-
$valueErrorPrefix = $hasMultipleValues ? "$errorPrefix {$name}[$offset]:" : "$errorPrefix $name:";
116+
$errorIndex = $hasMultipleValues ? "{$name}[$offset]" : $name;
117+
$valueErrorPrefix = "$errorPrefix $errorIndex:";
118+
$rangeErrorPrefix = "Line $lineNo contains an invalid range for $errorIndex:";
117119

118120
$steppedValues = explode("/", $value);
119121
if (count($steppedValues) > 2) {
120122
$stepsErrorName = $hasMultipleValues ? "{$name}[$offset]" : $name;
121-
$this->errors[] = "Line $lineNo has too many step values for $stepsErrorName: $value";
123+
$this->errors[] = "Line $lineNo contains too many step values for $stepsErrorName: $value";
122124
continue;
123125
}
124126

@@ -130,7 +132,7 @@ function ($v) {
130132
$rangeValues[0] = "-" . $rangeValues[0];
131133
}
132134
if (count($rangeValues) < 2) {
133-
$this->errors[] = "$valueErrorPrefix $firstValue (only wildcard * or range supported)";
135+
$this->errors[] = "$valueErrorPrefix $firstValue (must be wildcard `*` or a range)";
134136
$steppedValues = [$steppedValues[1]];
135137
}
136138
}
@@ -141,14 +143,28 @@ function ($v) {
141143
$rangeValues[0] = "-" . $rangeValues[0];
142144
}
143145
if (count($rangeValues) > 2) {
144-
$rangeErrorName = $hasMultipleValues ? "{$name}[$offset]" : $name;
145-
$this->errors[] = "Line $lineNo has too many values for the range for $rangeErrorName: $steppedValue";
146+
$this->errors[] = "$rangeErrorPrefix $steppedValue (too many values)";
146147
continue;
147148
}
148149

150+
$hasInvalidValue = false;
149151
foreach ($rangeValues as $rangeValue) {
150152
if (!preg_match($regEx, $rangeValue) || ($rangeValue !== "*" && !in_array(strtolower($rangeValue), $validValues))) {
151153
$this->errors[] = "$valueErrorPrefix $rangeValue";
154+
$hasInvalidValue = true;
155+
}
156+
}
157+
158+
if (!$hasInvalidValue && count($rangeValues) === 2) {
159+
$numericalValues = array_filter($rangeValues, "is_numeric");
160+
if (count($numericalValues) === 2) {
161+
$sorted = $rangeValues;
162+
sort($sorted);
163+
if ($sorted !== $rangeValues) {
164+
$this->errors[] = "$rangeErrorPrefix $steppedValue (must be in ascending order)";
165+
}
166+
} else {
167+
$this->errors[] = "$rangeErrorPrefix $steppedValue (must be numeric)";
152168
}
153169
}
154170
}

tests/LinterTest.php

Lines changed: 83 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -65,73 +65,89 @@ public function testValid(string $expression): void {
6565

6666
public static function invalidProvider(): array {
6767
return [
68-
["-0 * * * * php test.php", ["Line 1 has invalid value for Minute: -0"]],
69-
["-0-10 * * * * php test.php", ["Line 1 has invalid value for Minute: -0"]],
70-
["60 * * * * php test.php", ["Line 1 has invalid value for Minute: 60"]],
71-
["59,69 * * * * php test.php", ["Line 1 has invalid value for Minute[1]: 69"]],
72-
["60,65 * * * * php test.php", ["Line 1 has invalid value for Minute[0]: 60", "Line 1 has invalid value for Minute[1]: 65"]],
73-
["15-30-45 * * * * php test.php", ["Line 1 has too many values for the range for Minute: 15-30-45"]],
74-
["*/60 * * * * php test.php", ["Line 1 has invalid value for Minute: 60"]],
75-
["55-61/2 * * * * php test.php", ["Line 1 has invalid value for Minute: 61"]],
76-
["55-61-63/2 * * * * php test.php", ["Line 1 has too many values for the range for Minute: 55-61-63"]],
77-
["61-62/2 * * * * php test.php", ["Line 1 has invalid value for Minute: 61", "Line 1 has invalid value for Minute: 62"]],
78-
["1/2 * * * * php test.php", ["Line 1 has invalid value for Minute: 1 (only wildcard * or range supported)"]],
79-
["*/1/2 * * * * php test.php", ["Line 1 has too many step values for Minute: */1/2"]],
80-
["2-4-6/2/1 * * * * php test.php", ["Line 1 has too many step values for Minute: 2-4-6/2/1"]],
81-
82-
["* -1 * * * php test.php", ["Line 1 has invalid value for Hour: -1"]],
83-
["* -1-23 * * * php test.php", ["Line 1 has invalid value for Hour: -1"]],
84-
["* 24 * * * php test.php", ["Line 1 has invalid value for Hour: 24"]],
85-
["* 12,24 * * * php test.php", ["Line 1 has invalid value for Hour[1]: 24"]],
86-
["* 24,32 * * * php test.php", ["Line 1 has invalid value for Hour[0]: 24", "Line 1 has invalid value for Hour[1]: 32"]],
87-
["* 6-12-18 * * * php test.php", ["Line 1 has too many values for the range for Hour: 6-12-18"]],
88-
["* */40 * * * php test.php", ["Line 1 has invalid value for Hour: 40"]],
89-
["* 23-24/2 * * * php test.php", ["Line 1 has invalid value for Hour: 24"]],
90-
["* 24-26/2 * * * php test.php", ["Line 1 has invalid value for Hour: 24","Line 1 has invalid value for Hour: 26"]],
91-
["* 1/2 * * * php test.php", ["Line 1 has invalid value for Hour: 1 (only wildcard * or range supported)"]],
92-
["* 1/2/3 * * * php test.php", ["Line 1 has too many step values for Hour: 1/2/3"]],
93-
94-
["* * -2 * * php test.php", ["Line 1 has invalid value for Day of month: -2"]],
95-
["* * -2-14 * * php test.php", ["Line 1 has invalid value for Day of month: -2"]],
96-
["* * 0 * * php test.php", ["Line 1 has invalid value for Day of month: 0"]],
97-
["* * 32 * * php test.php", ["Line 1 has invalid value for Day of month: 32"]],
98-
["* * 28,35 * * php test.php", ["Line 1 has invalid value for Day of month[1]: 35"]],
99-
["* * 32,35 * * php test.php", ["Line 1 has invalid value for Day of month[0]: 32", "Line 1 has invalid value for Day of month[1]: 35"]],
100-
["* * 7-14-21 * * php test.php", ["Line 1 has too many values for the range for Day of month: 7-14-21"]],
101-
["* * */32 * * php test.php", ["Line 1 has invalid value for Day of month: 32"]],
102-
["* * 21-35/2 * * php test.php", ["Line 1 has invalid value for Day of month: 35"]],
103-
["* * 32-35/2 * * php test.php", ["Line 1 has invalid value for Day of month: 32", "Line 1 has invalid value for Day of month: 35"]],
104-
["* * 7/14 * * php test.php", ["Line 1 has invalid value for Day of month: 7 (only wildcard * or range supported)"]],
105-
["* * 7/14/28 * * php test.php", ["Line 1 has too many step values for Day of month: 7/14/28"]],
106-
107-
["* * * -3 * php test.php", ["Line 1 has invalid value for Month: -3"]],
108-
["* * * -3-12 * php test.php", ["Line 1 has invalid value for Month: -3"]],
109-
["* * * 0 * php test.php", ["Line 1 has invalid value for Month: 0"]],
110-
["* * * 13 * php test.php", ["Line 1 has invalid value for Month: 13"]],
111-
["* * * 3,13 * php test.php", ["Line 1 has invalid value for Month[1]: 13"]],
112-
["* * * 13,14 * php test.php", ["Line 1 has invalid value for Month[0]: 13", "Line 1 has invalid value for Month[1]: 14"]],
113-
["* * * 3-6-9 * php test.php", ["Line 1 has too many values for the range for Month: 3-6-9"]],
114-
["* * * */24 * php test.php", ["Line 1 has invalid value for Month: 24"]],
115-
["* * * 8-16/2 * php test.php", ["Line 1 has invalid value for Month: 16"]],
116-
["* * * 14-16/2 * php test.php", ["Line 1 has invalid value for Month: 14", "Line 1 has invalid value for Month: 16"]],
117-
["* * * 8/12 * php test.php", ["Line 1 has invalid value for Month: 8 (only wildcard * or range supported)"]],
118-
["* * * 8/12/16 * php test.php", ["Line 1 has too many step values for Month: 8/12/16"]],
119-
["* * * june * php test.php", ["Line 1 has invalid value for Month: june"]],
120-
121-
["* * * * -4 php test.php", ["Line 1 has invalid value for Day of week: -4"]],
122-
["* * * * -3-5 php test.php", ["Line 1 has invalid value for Day of week: -3"]],
123-
["* * * * 7 php test.php", ["Line 1 has invalid value for Day of week: 7"]],
124-
["* * * * 4,8 php test.php", ["Line 1 has invalid value for Day of week[1]: 8"]],
125-
["* * * * 7,8 php test.php", ["Line 1 has invalid value for Day of week[0]: 7", "Line 1 has invalid value for Day of week[1]: 8"]],
126-
["* * * * 2-4-6 php test.php", ["Line 1 has too many values for the range for Day of week: 2-4-6"]],
127-
["* * * * */7 php test.php", ["Line 1 has invalid value for Day of week: 7"]],
128-
["* * * * 4-8/2 php test.php", ["Line 1 has invalid value for Day of week: 8"]],
129-
["* * * * 8-10/2 php test.php", ["Line 1 has invalid value for Day of week: 8", "Line 1 has invalid value for Day of week: 10"]],
130-
["* * * * 8/2 php test.php", ["Line 1 has invalid value for Day of week: 8 (only wildcard * or range supported)"]],
131-
["* * * * 3/6/9 php test.php", ["Line 1 has too many step values for Day of week: 3/6/9"]],
132-
["* * * * thurs php test.php", ["Line 1 has invalid value for Day of week: thurs"]],
133-
134-
["* * * 18 45 php test.php", ["Line 1 has invalid value for Month: 18", "Line 1 has invalid value for Day of week: 45"]],
68+
["-0 * * * * php test.php", ["Line 1 contains an invalid value for Minute: -0"]],
69+
["-0-10 * * * * php test.php", ["Line 1 contains an invalid value for Minute: -0"]],
70+
["60 * * * * php test.php", ["Line 1 contains an invalid value for Minute: 60"]],
71+
["59,69 * * * * php test.php", ["Line 1 contains an invalid value for Minute[1]: 69"]],
72+
["60,65 * * * * php test.php", ["Line 1 contains an invalid value for Minute[0]: 60", "Line 1 contains an invalid value for Minute[1]: 65"]],
73+
["15-30-45 * * * * php test.php", ["Line 1 contains an invalid range for Minute: 15-30-45 (too many values)"]],
74+
["*/60 * * * * php test.php", ["Line 1 contains an invalid value for Minute: 60"]],
75+
["55-61/2 * * * * php test.php", ["Line 1 contains an invalid value for Minute: 61"]],
76+
["55-61-63/2 * * * * php test.php", ["Line 1 contains an invalid range for Minute: 55-61-63 (too many values)"]],
77+
["61-62/2 * * * * php test.php", ["Line 1 contains an invalid value for Minute: 61", "Line 1 contains an invalid value for Minute: 62"]],
78+
["1/2 * * * * php test.php", ["Line 1 contains an invalid value for Minute: 1 (must be wildcard `*` or a range)"]],
79+
["*/1/2 * * * * php test.php", ["Line 1 contains too many step values for Minute: */1/2"]],
80+
["2-4-6/2/1 * * * * php test.php", ["Line 1 contains too many step values for Minute: 2-4-6/2/1"]],
81+
["59-0 * * * * php test.php", ["Line 1 contains an invalid range for Minute: 59-0 (must be in ascending order)"]],
82+
["10-9 * * * * php test.php", ["Line 1 contains an invalid range for Minute: 10-9 (must be in ascending order)"]],
83+
["10-9/2 * * * * php test.php", ["Line 1 contains an invalid range for Minute: 10-9 (must be in ascending order)"]],
84+
["60-50 * * * * php test.php", ["Line 1 contains an invalid value for Minute: 60"]],
85+
86+
["* -1 * * * php test.php", ["Line 1 contains an invalid value for Hour: -1"]],
87+
["* -1-23 * * * php test.php", ["Line 1 contains an invalid value for Hour: -1"]],
88+
["* 24 * * * php test.php", ["Line 1 contains an invalid value for Hour: 24"]],
89+
["* 12,24 * * * php test.php", ["Line 1 contains an invalid value for Hour[1]: 24"]],
90+
["* 24,32 * * * php test.php", ["Line 1 contains an invalid value for Hour[0]: 24", "Line 1 contains an invalid value for Hour[1]: 32"]],
91+
["* 6-12-18 * * * php test.php", ["Line 1 contains an invalid range for Hour: 6-12-18 (too many values)"]],
92+
["* */40 * * * php test.php", ["Line 1 contains an invalid value for Hour: 40"]],
93+
["* 23-24/2 * * * php test.php", ["Line 1 contains an invalid value for Hour: 24"]],
94+
["* 24-26/2 * * * php test.php", ["Line 1 contains an invalid value for Hour: 24","Line 1 contains an invalid value for Hour: 26"]],
95+
["* 1/2 * * * php test.php", ["Line 1 contains an invalid value for Hour: 1 (must be wildcard `*` or a range)"]],
96+
["* 1/2/3 * * * php test.php", ["Line 1 contains too many step values for Hour: 1/2/3"]],
97+
["* 23-0 * * * php test.php", ["Line 1 contains an invalid range for Hour: 23-0 (must be in ascending order)"]],
98+
["* 12-5 * * * php test.php", ["Line 1 contains an invalid range for Hour: 12-5 (must be in ascending order)"]],
99+
["* 23-25 * * * php test.php", ["Line 1 contains an invalid value for Hour: 25"]],
100+
101+
["* * -2 * * php test.php", ["Line 1 contains an invalid value for Day of month: -2"]],
102+
["* * -2-14 * * php test.php", ["Line 1 contains an invalid value for Day of month: -2"]],
103+
["* * 0 * * php test.php", ["Line 1 contains an invalid value for Day of month: 0"]],
104+
["* * 32 * * php test.php", ["Line 1 contains an invalid value for Day of month: 32"]],
105+
["* * 28,35 * * php test.php", ["Line 1 contains an invalid value for Day of month[1]: 35"]],
106+
["* * 32,35 * * php test.php", ["Line 1 contains an invalid value for Day of month[0]: 32", "Line 1 contains an invalid value for Day of month[1]: 35"]],
107+
["* * 7-14-21 * * php test.php", ["Line 1 contains an invalid range for Day of month: 7-14-21 (too many values)"]],
108+
["* * */32 * * php test.php", ["Line 1 contains an invalid value for Day of month: 32"]],
109+
["* * 21-35/2 * * php test.php", ["Line 1 contains an invalid value for Day of month: 35"]],
110+
["* * 32-35/2 * * php test.php", ["Line 1 contains an invalid value for Day of month: 32", "Line 1 contains an invalid value for Day of month: 35"]],
111+
["* * 7/14 * * php test.php", ["Line 1 contains an invalid value for Day of month: 7 (must be wildcard `*` or a range)"]],
112+
["* * 7/14/28 * * php test.php", ["Line 1 contains too many step values for Day of month: 7/14/28"]],
113+
["* * 31-1 * * php test.php", ["Line 1 contains an invalid range for Day of month: 31-1 (must be in ascending order)"]],
114+
["* * 31-0 * * php test.php", ["Line 1 contains an invalid value for Day of month: 0"]],
115+
116+
["* * * -3 * php test.php", ["Line 1 contains an invalid value for Month: -3"]],
117+
["* * * -3-12 * php test.php", ["Line 1 contains an invalid value for Month: -3"]],
118+
["* * * 0 * php test.php", ["Line 1 contains an invalid value for Month: 0"]],
119+
["* * * 13 * php test.php", ["Line 1 contains an invalid value for Month: 13"]],
120+
["* * * 3,13 * php test.php", ["Line 1 contains an invalid value for Month[1]: 13"]],
121+
["* * * 13,14 * php test.php", ["Line 1 contains an invalid value for Month[0]: 13", "Line 1 contains an invalid value for Month[1]: 14"]],
122+
["* * * 3-6-9 * php test.php", ["Line 1 contains an invalid range for Month: 3-6-9 (too many values)"]],
123+
["* * * */24 * php test.php", ["Line 1 contains an invalid value for Month: 24"]],
124+
["* * * 8-16/2 * php test.php", ["Line 1 contains an invalid value for Month: 16"]],
125+
["* * * 14-16/2 * php test.php", ["Line 1 contains an invalid value for Month: 14", "Line 1 contains an invalid value for Month: 16"]],
126+
["* * * 8/12 * php test.php", ["Line 1 contains an invalid value for Month: 8 (must be wildcard `*` or a range)"]],
127+
["* * * 8/12/16 * php test.php", ["Line 1 contains too many step values for Month: 8/12/16"]],
128+
["* * * 12-0 * php test.php", ["Line 1 contains an invalid value for Month: 0"]],
129+
["* * * 12-1 * php test.php", ["Line 1 contains an invalid range for Month: 12-1 (must be in ascending order)"]],
130+
["* * * june * php test.php", ["Line 1 contains an invalid value for Month: june"]],
131+
["* * * mar-feb * php test.php", ["Line 1 contains an invalid range for Month: mar-feb (must be numeric)"]],
132+
133+
["* * * * -4 php test.php", ["Line 1 contains an invalid value for Day of week: -4"]],
134+
["* * * * -3-5 php test.php", ["Line 1 contains an invalid value for Day of week: -3"]],
135+
["* * * * 7 php test.php", ["Line 1 contains an invalid value for Day of week: 7"]],
136+
["* * * * 4,8 php test.php", ["Line 1 contains an invalid value for Day of week[1]: 8"]],
137+
["* * * * 7,8 php test.php", ["Line 1 contains an invalid value for Day of week[0]: 7", "Line 1 contains an invalid value for Day of week[1]: 8"]],
138+
["* * * * 2-4-6 php test.php", ["Line 1 contains an invalid range for Day of week: 2-4-6 (too many values)"]],
139+
["* * * * */7 php test.php", ["Line 1 contains an invalid value for Day of week: 7"]],
140+
["* * * * 4-8/2 php test.php", ["Line 1 contains an invalid value for Day of week: 8"]],
141+
["* * * * 8-10/2 php test.php", ["Line 1 contains an invalid value for Day of week: 8", "Line 1 contains an invalid value for Day of week: 10"]],
142+
["* * * * 8/2 php test.php", ["Line 1 contains an invalid value for Day of week: 8 (must be wildcard `*` or a range)"]],
143+
["* * * * 3/6/9 php test.php", ["Line 1 contains too many step values for Day of week: 3/6/9"]],
144+
["* * * * thurs php test.php", ["Line 1 contains an invalid value for Day of week: thurs"]],
145+
["* * * * 6-0 php test.php", ["Line 1 contains an invalid range for Day of week: 6-0 (must be in ascending order)"]],
146+
["* * * * 5-2 php test.php", ["Line 1 contains an invalid range for Day of week: 5-2 (must be in ascending order)"]],
147+
["* * * * 0-mon php test.php", ["Line 1 contains an invalid range for Day of week: 0-mon (must be numeric)"]],
148+
["* * * * fri-mon php test.php", ["Line 1 contains an invalid range for Day of week: fri-mon (must be numeric)"]],
149+
150+
["* * * 18 45 php test.php", ["Line 1 contains an invalid value for Month: 18", "Line 1 contains an invalid value for Day of week: 45"]],
135151
];
136152
}
137153

0 commit comments

Comments
 (0)