fix: Improve time_log validation to detect and reject invalid formats

Problem:
Users were sending time_log data as associative arrays with keys
like 'start_time', 'end_time', 'date', 'billable' instead of the
expected flat array format [int, int, string, bool].

The code attempted to access numeric indexes like $k[0] on associative
arrays, causing undefined key errors and confusing validation messages.

Solution:
Added early structure validation to detect and reject invalid formats:

1. Check if entry is an array
2. Detect associative arrays (has string keys)
3. Ensure numeric indexes [0] and [1] exist before type checking
4. Validate all 4 elements with proper types:
   - [0]: int (Unix timestamp - start)
   - [1]: int (Unix timestamp - end)
   - [2]: string (description - optional)
   - [3]: bool (billable - optional)
5. Improved error messages that clearly explain expected format

Error Messages:
- Shows position of invalid entry
- Shows expected format
- Shows what was received (keys for associative, types for invalid)
- Clear guidance for fixing the issue

Example Error:
"Time log entry at position 0 uses invalid format. Expected:
[unix_start, unix_end, description, billable]. Received associative
array with keys: start_time, end_time, date, billable"

Files modified:
- app/Http/Requests/Task/StoreTaskRequest.php
- app/Http/Requests/Task/UpdateTaskRequest.php
This commit is contained in:
David Bomba 2025-11-26 05:26:55 +00:00
parent 32b1ca8cb8
commit 8a169c4774
2 changed files with 60 additions and 14 deletions

View File

@ -68,20 +68,42 @@ class StoreTaskRequest extends Request
return;
}
foreach ($values as $k) {
if (!is_int($k[0]) || !is_int($k[1])) {
return $fail('The '.$attribute.' - '.print_r($k, true).' is invalid. Unix timestamps only.');
}
if(count($k) > 4) {
return $fail('The timelog can only have up to 4 elements.');
foreach ($values as $key => $k) {
// Check if this is an array
if (!is_array($k)) {
return $fail('Time log entry at position '.$key.' must be an array.');
}
if (isset($k[3]) && !is_bool($k[3])) {
return $fail('The '.$attribute.' - '.print_r($k, true).' is invalid. The 4th element must be a boolean.');
// Check for associative array (has string keys)
if (array_keys($k) !== range(0, count($k) - 1)) {
return $fail('Time log entry at position '.$key.' uses invalid format. Expected: [unix_start, unix_end, description, billable]. Received associative array with keys: '.implode(', ', array_keys($k)));
}
// Ensure minimum required elements exist
if (!isset($k[0]) || !isset($k[1])) {
return $fail('Time log entry at position '.$key.' must have at least 2 elements: [start_timestamp, end_timestamp].');
}
// Validate types for required elements
if (!is_int($k[0]) || !is_int($k[1])) {
return $fail('Time log entry at position '.$key.' is invalid. Elements [0] and [1] must be Unix timestamps (integers). Received: '.print_r($k, true));
}
// Validate max elements
if(count($k) > 4) {
return $fail('Time log entry at position '.$key.' can only have up to 4 elements. Received '.count($k).' elements.');
}
// Validate optional element [2] (description)
if (isset($k[2]) && !is_string($k[2])) {
return $fail('Time log entry at position '.$key.': element [2] (description) must be a string. Received: '.gettype($k[2]));
}
// Validate optional element [3] (billable)
if (isset($k[3]) && !is_bool($k[3])) {
return $fail('Time log entry at position '.$key.': element [3] (billable) must be a boolean. Received: '.gettype($k[3]));
}
}
if (!$this->checkTimeLog($values)) {

View File

@ -74,17 +74,41 @@ class UpdateTaskRequest extends Request
return;
}
foreach ($values as $k) {
foreach ($values as $key => $k) {
// Check if this is an array
if (!is_array($k)) {
return $fail('Time log entry at position '.$key.' must be an array.');
}
// Check for associative array (has string keys)
if (array_keys($k) !== range(0, count($k) - 1)) {
return $fail('Time log entry at position '.$key.' uses invalid format. Expected: [unix_start, unix_end, description, billable]. Received associative array with keys: '.implode(', ', array_keys($k)));
}
// Ensure minimum required elements exist
if (!isset($k[0]) || !isset($k[1])) {
return $fail('Time log entry at position '.$key.' must have at least 2 elements: [start_timestamp, end_timestamp].');
}
// Validate types for required elements
if (!is_int($k[0]) || !is_int($k[1])) {
return $fail('The '.$attribute.' - '.print_r($k, true).' is invalid. Unix timestamps only.');
return $fail('Time log entry at position '.$key.' is invalid. Elements [0] and [1] must be Unix timestamps (integers). Received: '.print_r($k, true));
}
// Validate max elements
if(count($k) > 4) {
return $fail('The timelog can only have up to 4 elements.');
return $fail('Time log entry at position '.$key.' can only have up to 4 elements. Received '.count($k).' elements.');
}
// Validate optional element [2] (description)
if (isset($k[2]) && !is_string($k[2])) {
return $fail('Time log entry at position '.$key.': element [2] (description) must be a string. Received: '.gettype($k[2]));
}
// Validate optional element [3] (billable)
if(isset($k[3]) && !is_bool($k[3])) {
return $fail('The '.$attribute.' - '.print_r($k, true).' is invalid. The 4th element must be a boolean.');
return $fail('Time log entry at position '.$key.': element [3] (billable) must be a boolean. Received: '.gettype($k[3]));
}
}