Skip to content

Commit 9cfe03d

Browse files
committed
fix(bool): skip string coercion for non-scalar values in BoolValidator
- Guard coerceValue with is_scalar before string normalization - Add tests for array and object inputs with coerce() - Document scalar-only coercion in API reference - Record fix in CHANGELOG
1 parent 6be5d44 commit 9cfe03d

4 files changed

Lines changed: 19 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Fixed
8+
9+
- `BoolValidator::coerce()` no longer string-casts non-scalar values (arrays triggered `E_WARNING`, objects without `__toString` threw `Error`); non-scalars are passed through to type validation, which raises `ValidationException` as for other invalid types
10+
711
### Added
812

913
- `passthrough()` on `AssociativeValidator` and `ObjectValidator`: copy undeclared keys or public properties from the input to the validated output without validating them; declared schema fields are still validated; values already written from the schema (including `outputKey` targets) are not overwritten by passthrough

docs/api-reference/validator-factory.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ $result = $coercingValidator->validate('1'); // Returns: true (bool)
189189
$result = $coercingValidator->validate('false'); // Returns: false (bool)
190190
```
191191

192+
With `coerce()`, only **scalar** values are normalized to booleans from common string forms (`true` / `on` / `1`, etc.). Arrays, objects, and other non-scalars are not cast to string; they fail boolean type validation with `ValidationException`.
193+
192194
**Returns:** `BoolValidator` instance with boolean-specific validation methods.
193195

194196
## Usage Patterns

src/Lemmon/Validator/BoolValidator.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ protected function coerceValue(mixed $value): mixed
1616
if ($value === '') {
1717
return null; // Empty string to null for form safety
1818
}
19-
if (in_array(strtolower((string) $value), ['true', 'on', '1'], true)) {
19+
if (!is_scalar($value)) {
20+
return $value;
21+
}
22+
$normalized = strtolower((string) $value);
23+
if (in_array($normalized, ['true', 'on', '1'], true)) {
2024
return true;
2125
}
22-
if (in_array(strtolower((string) $value), ['false', 'off', '0'], true)) {
26+
if (in_array($normalized, ['false', 'off', '0'], true)) {
2327
return false;
2428
}
2529
return $value;

tests/BoolValidatorTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,10 @@
5252
expect(fn() => $validator->validate('invalid'))->toThrow(ValidationException::class);
5353
expect(fn() => $validator->validate(123))->toThrow(ValidationException::class);
5454
});
55+
56+
it('should reject non-scalar values with coercion without PHP warnings or errors', function () {
57+
$validator = Validator::isBool()->coerce();
58+
59+
expect(fn() => $validator->validate([]))->toThrow(ValidationException::class);
60+
expect(fn() => $validator->validate(new stdClass()))->toThrow(ValidationException::class);
61+
});

0 commit comments

Comments
 (0)