Download the PHP package gladehq/php-coerce without Composer
On this page you can find all versions of the php package gladehq/php-coerce. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package php-coerce
php-coerce
Safe, predictable type coercion for PHP. If it cannot convert meaningfully, it returns
null. Never guess.
PHP's native type juggling is unpredictable. (int)"abc" returns 0. empty(0) returns true. BackedEnum::from() throws on invalid values. Data arrives in many formats: HTML forms, JSON APIs, CSV files, .env files, and database rows. Each source has its own quirks.
php-coerce provides a single, consistent interface to handle all of it. The rule is simple: if a conversion is ambiguous, lossy, or meaningless, it returns null. Never guess.
Table of Contents
- Requirements
- Installation
- Quick Start
- Static API
- Fluent API
- API Reference
- Boolean Coercion
- Integer Coercion
- Positive & Unsigned Integer
- Float Coercion
- Rounded Float
- Decimal String (BC Math safe)
- Percent
- String Coercion
- Array Coercion
- Array Transform
- DateTime Coercion
- Enum Coercion
- Comparison
- Blank Detection
- Format Validation
- Configuration
- Number Format
- Date Format
- Boolean Values
- Float Epsilon
- Bulk Configuration
- Reset
- Real-World Examples
- Architecture
- Design Principles
- Edge Cases & Gotchas
- Testing
- Contributing
- Security
- Changelog
- License
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.1 |
| Runtime dependencies | None |
Framework-agnostic. Works with Laravel, Symfony, Slim, or plain PHP.
Installation
No service providers, no configuration files, no bootstrapping required. Import the class and use it.
Quick Start
Static API
The simplest way to use the package. All methods are static, zero setup required.
Fluent API
Chain from a single value with Coerce::from(). Use *Or($default) methods for guaranteed non-null returns.
API Reference
Boolean Coercion
| Input | Output |
|---|---|
true / false |
true / false |
1 / 0 |
true / false |
'true', '1', 'yes', 'on' |
true |
'false', '0', 'no', 'off' |
false |
2, 'abc', null, [] |
null |
- String matching is case-insensitive:
"TRUE","True", and"true"all resolve totrue. - Only integers
0and1map to booleans.2,-1, and other integers returnnull. - Truthy and falsy string sets are configurable.
Fluent:
Integer Coercion
| Input | Output |
|---|---|
42 |
42 |
'42' |
42 |
' 42 ' |
42 (trimmed) |
'-5' |
-5 |
'+42' |
42 |
'1,234,567' |
1234567 (auto-detected US format) |
'1.234.567' |
1234567 (auto-detected EU format) |
42.9 |
null. No silent truncation. |
'42.9' |
null |
true / false |
null. Booleans are not numbers. |
'abc' |
null |
'1e5' |
null. Scientific notation is not integer domain. |
'007' |
null. Leading zeros rejected. |
'9999999999999999999' |
null. Overflow protection. |
Fluent:
Positive & Unsigned Integer
Thin guards built on top of toInteger().
| Input | toPositiveInt |
toUnsignedInt |
|---|---|---|
1 |
1 |
1 |
0 |
null |
0 |
-1 |
null |
null |
'42' |
42 |
42 |
'abc' |
null |
null |
Fluent:
Float Coercion
| Input | Output |
|---|---|
3.14 |
3.14 |
42 |
42.0 (int promoted to float) |
'3.14' |
3.14 |
'1,234.56' |
1234.56 (auto-detected US) |
'1.234,56' |
1234.56 (auto-detected EU) |
'1e5' |
100000.0 (scientific notation) |
'1.5E-3' |
0.0015 |
INF |
INF (native float passthrough) |
NAN |
null. Not a meaningful number. |
'INF' / 'NaN' |
null. String representations rejected. |
true / false |
null |
'abc' |
null |
Fluent:
Rounded Float
Coerces to float then rounds to the requested number of decimal places. Returns null if the value cannot be coerced.
Fluent:
Decimal String (BC Math safe)
Returns a fixed-point decimal string suitable for bcmath functions or financial display. The result has exactly $scale digits after the decimal point. Returns null for values that cannot be coerced, INF, or NAN.
Throws \InvalidArgumentException if $scale < 0.
Fluent:
Percent
Normalises a percentage value to a decimal ratio (0–1 range). Two input conventions are supported:
- Percent string (
'50%'): strips the%suffix and divides by 100 - Numeric value: if the value is within
[-1, 1]it is returned as-is (already a ratio); otherwise it is divided by 100
Note: An integer
1is treated as within[-1, 1]and returned as1.0(100%). If you mean 1%, pass'1%'.
Fluent:
String Coercion
| Input | Output |
|---|---|
'hello' |
'hello' |
42 |
'42' |
3.14 |
'3.14' |
true / false |
'true' / 'false' |
| Stringable object | Result of __toString() |
null, [], non-Stringable object |
null |
toStringOrEmpty() returns '' instead of null for unconvertible values.
Fluent:
Array Coercion
Conversion is attempted in this priority order:
- Already an array: returned as-is
- Traversable (Iterator, Generator): converted via
iterator_to_array() - JSON string (starts with
[or{): decoded withjson_decode() - String with separator: split by separator, each part trimmed
- Scalar string (no JSON, no separator): wrapped in a single-element array
- int, float, bool: wrapped in a single-element array
- null, non-traversable objects:
null
JSON takes priority over separator splitting:
Fluent:
Array Transform
Coerces the value to an array (using the same logic as toArray()) then applies $fn to every element. Returns null if the value cannot be converted to an array. Keys are preserved.
Fluent:
DateTime Coercion
Always returns DateTimeImmutable or null. Never DateTime.
| Input | Output |
|---|---|
DateTimeImmutable instance |
Returned as-is |
DateTime instance |
Converted to DateTimeImmutable |
1705276800 (int) |
Unix timestamp, returns DateTimeImmutable |
0 (int) |
Unix epoch (1970-01-01) |
-1 (int) |
1969-12-31 23:59:59 |
'2024-01-15' |
Parsed by PHP |
'2024-01-15T10:30:00+00:00' |
ISO 8601 parsed |
'0' (string) |
null. Numeric strings rejected. |
'1705276800' (string) |
null. Pass as int for timestamps. |
'not a date' |
null |
'', ' ' |
null |
3.14, true, [] |
null |
Important: Numeric strings are intentionally rejected in auto mode. To handle a timestamp stored as a string, cast it first:
Custom date format enforces strict matching. Trailing garbage is rejected:
Auto mode accepts PHP's relative date strings.
In auto mode, toDateTime() delegates to PHP's native \DateTimeImmutable constructor, which accepts the full range of formats that strtotime() understands. This includes relative expressions:
This is not a bug. It is PHP's documented behavior, and it is intentionally preserved so that toDateTime() in auto mode is as permissive as PHP itself.
However, if your input comes from untrusted sources (user form fields, API payloads, CSV rows) and you expect a concrete date, relative strings passing through silently may be surprising. For example, an API field that should contain a birth date would silently accept 'next monday' and produce a future date with no error.
The fix is to set an explicit format. When a format is configured, the native fallback is bypassed entirely and only strings that match the format exactly are accepted:
Use setDateFormat() whenever you are processing external input that must represent a real, absolute date.
Fluent:
Enum Coercion
Works with PHP 8.1+ backed enums (string-backed and int-backed). Returns null instead of throwing.
| Input | Enum Class | Output |
|---|---|---|
'active' |
Status::class |
Status::Active |
Status::Active |
Status::class |
Status::Active (returned as-is) |
'invalid' |
Status::class |
null |
1 |
Priority::class |
Priority::Low |
'2' |
Priority::class |
Priority::Medium (cross-type coercion) |
99 |
Priority::class |
null |
null |
any | null |
| any value | Unit enum class | null. Unit enums not supported. |
Cross-type coercion: a string "2" will match an int-backed enum with value 2. An int 1 will try string "1" against string-backed enums. Unit enums (enums without a backing type) safely return null. No crash.
Fluent:
Comparison
Semantic equality that respects types. Comparison layers are applied in this priority order:
- Same type: strict
===for non-floats; epsilon comparison for floats - One side is native
bool: coerce both to boolean and compare - Both coercible to float: epsilon comparison
- Both coercible to string: compare as strings
- None match:
false
The boolean layer only activates when at least one operand is a native
bool. Two strings like"1"and"true"are never compared as booleans. They fall through to string comparison.Float comparisons use a relative epsilon (
|a - b| ≤ ε × max(|a|, |b|, 1)). The default epsilon isPHP_FLOAT_EPSILON. See Float Epsilon to customise it.
Blank Detection
| Input | isBlank |
isPresent |
|---|---|---|
null |
true |
false |
'' |
true |
false |
' ' (whitespace only) |
true |
false |
[] |
true |
false |
0 |
false |
true |
false |
false |
true |
'hello' |
false |
true |
[1, 2] |
false |
true |
Unlike PHP's empty(), the values 0 and false are not blank. They are valid, meaningful values.
Fluent:
toNullIfBlank() is useful for normalising optional form fields before persistence:
Format Validation
Thin wrappers over PHP's filter_var. Non-string values always return false.
Fluent:
Configuration
All configuration is optional. The package works out of the box with sensible defaults.
Number Format
Controls how numeric strings with separators are interpreted.
Auto-detection rules:
| Input pattern | Interpretation |
|---|---|
Single dot or comma only (1.5, 1,5) |
Decimal separator |
Multiple dots (1.234.567) |
Thousands separator |
Multiple commas (1,234,567) |
Thousands separator |
Both dot and comma, dot last (1,234.56) |
US format |
Both dot and comma, comma last (1.234,56) |
EU format |
Date Format
Boolean Values
Customize which strings are considered truthy or falsy:
Float Epsilon
Controls the tolerance used by equals() when comparing float values.
The comparison formula is relative, not absolute: |a - b| ≤ ε × max(|a|, |b|, 1). This prevents epsilon from becoming meaninglessly small for large numbers or inappropriately large for numbers near zero.
Throws \InvalidArgumentException if epsilon is <= 0.
Bulk Configuration
| Option | Accepted values | Default |
|---|---|---|
number_format |
'auto', 'us', 'eu' |
'auto' |
date_format |
Any PHP date format string, or 'auto' |
'auto' |
truthy_values |
list<string> |
['true', '1', 'yes', 'on'] |
falsy_values |
list<string> |
['false', '0', 'no', 'off'] |
float_epsilon |
float > 0 |
PHP_FLOAT_EPSILON |
Reset
Real-World Examples
Form Input Processing
Environment Variables
CSV / Spreadsheet Data
API Response Normalization
Safe Defaults with Fluent API
Blank vs Empty Checks
Cross-Type Enum Matching
Financial / Decimal Arithmetic
Validated User Input
Batch Array Coercion
Positive / Unsigned Guards
Architecture
All coercers and detectors are @internal. The public API surface consists of Coerce and CoerceValue only. Internal classes may change between minor versions without a semver break.
Design Principles
| Principle | Description |
|---|---|
| Null over guessing | If a conversion is ambiguous or lossy, return null. Never invent a value. |
| No exceptions | Coercion methods never throw; invalid input returns null |
| No silent truncation | toInteger(42.9) returns null, not 42 |
| Booleans are not numbers | toInteger(true) returns null, not 1 |
| Type safety | Generics on enum coercion, strict return types throughout |
| Zero dependencies | Only PHP 8.1+ standard library. No framework coupling. |
| PHPStan level 9 | Maximum static analysis strictness enforced in CI |
Edge Cases & Gotchas
| Scenario | Behavior | Rationale |
|---|---|---|
toInteger(42.0) |
null |
Float is never silently truncated to int |
toInteger(true) |
null |
Booleans must not silently become numbers |
toBoolean(2) |
null |
Only 0 and 1 are boolean integers |
toFloat(NAN) |
null |
NaN is not a meaningful number |
toFloat(INF) |
INF |
Valid IEEE 754 float, passed through |
toFloat("INF") |
null |
String "INF" is not a valid numeric string |
toFloat("1e5") |
100000.0 |
Scientific notation is valid float domain |
toInteger("1e5") |
null |
Scientific notation is not integer domain |
toInteger("007") |
null |
Leading zeros rejected (ambiguous octal) |
toDateTime("0") |
null |
Numeric strings rejected. Use toDateTime(0). |
toDateTime(0) |
1970-01-01 |
Integer zero is valid Unix epoch |
toEnum('x', UnitEnum::class) |
null |
Unit enums gracefully return null, not crash |
equals("1", "true") |
false |
Boolean layer requires a native bool operand |
equals(true, "yes") |
true |
Native bool present. Boolean layer activates. |
equals(0.1 + 0.2, 0.3) |
true |
IEEE-754 epsilon comparison, not === |
toArray("a,b,", ",") |
["a","b",""] |
Trailing separator produces empty string element |
isBlank(0) |
false |
Unlike empty(), zero is a meaningful value |
isBlank(false) |
false |
Unlike empty(), false is a meaningful value |
toPositiveInt(0) |
null |
Zero is not positive |
toUnsignedInt(-1) |
null |
Negative integers are not unsigned |
toPercent(1) |
1.0 |
Integer 1 is within [-1, 1], returned as-is (100%) — use '1%' for 1% |
toBcDecimal(INF) |
null |
Infinity has no decimal representation |
isEmail(42) |
false |
Non-string values always return false |
isUrl(null) |
false |
Non-string values always return false |
toNullIfBlank('') |
null |
Blank values become null |
toNullIfBlank(0) |
0 |
Non-blank values returned unchanged |
Testing
The test suite covers 282 tests and 410 assertions, including:
- All coercion methods with valid, invalid, and boundary inputs
- Auto-detection logic for US/EU number formats
- DateTime parsing: timestamps, strings, custom formats, edge cases
- Enum coercion: backed enums, cross-type, unit enum safety
- IEEE-754 float comparison regression (
equals(0.1 + 0.2, 0.3) === true) - Comparison semantics: boolean layer activation rules, custom epsilon
toBcDecimal,toPercent,toRoundedFloat,toPositiveInt,toUnsignedIntisEmail,isUrl,toNullIfBlank,coerceEach- Configuration mutations and reset (including
float_epsilon) - Full fluent API coverage with default fallbacks
Contributing
Contributions are welcome. Please follow these guidelines:
- Fork the repository and create a feature branch from
main - Write tests for all new behaviour. The test suite must pass.
- Ensure PHPStan level 9 reports no errors:
vendor/bin/phpstan analyse - Follow PSR-12 coding style:
vendor/bin/php-cs-fixer fix --dry-run --diff - Open a pull request with a clear description of the change and its rationale
For significant changes or new coercion domains, open an issue first to discuss the approach.
Security
If you discover a security vulnerability, please do not open a public GitHub issue. Report it privately via the repository's Security Advisories tab on GitHub.
All security reports will be acknowledged within 48 hours.
Changelog
All notable changes are documented in Semantic Versioning.
License
The MIT License (MIT). See LICENSE for full details.
Made with care by Dulitha Rajapaksha