PHP's "Strict" Types That Aren’t So Strict: The Curious Case of Int-to-Float Coercion
When PHP 7 introduced strict types, many developers breathed a sigh of relief. Finally, a way to enforce strict type checking in function arguments and return types! But if you’ve ever played around with strict_types=1, you may have noticed something peculiar: passing an integer to a function expecting a float doesn’t throw an error. Instead, it just works.
Let’s unpack this.
What is strict_types=1?
When you write:
declare(strict_types=1);
PHP enables strict type checking for scalar types (int, float, string, bool) in function arguments and return values. Without strict_types, PHP tries to be “helpful” and performs loose type coercion:
- Passing a string '123'to a function expecting anintwill just work.
- Passing '3.14'to afloat-typed function? Sure, PHP will quietly convert it.
With strict_types=1, PHP stops this silent coercion. If you pass a string where an int is expected, it throws a TypeError.
But Then, Why Does int to float Still Work?
Here’s the kicker: strict_types=1 doesn’t prevent int-to-float coercion. Even in strict mode, passing 1 (an int) to a function expecting a float does not throw an error. PHP accepts it and converts the int to float under the hood.
Why?
- PHP’s type system sees int and float as numeric types that are compatible.
- Converting an integer like 1to1.0(float) is safe and lossless.
- The PHP designers made a deliberate decision: int and float are close enough for strict types to allow automatic conversion.
The Philosophy Behind It
PHP’s type system isn’t designed to be ultra-strict like some other languages (e.g., Rust or TypeScript with strict mode). Instead, it prioritizes pragmatism and developer convenience.
- Numeric types (int and float) are often interchangeable in mathematical contexts.
- Forcing developers to manually convert 1to1.0would add unnecessary friction, with little benefit.
Where strict_types Does Make a Difference
Let’s look at what strict_types does enforce strictly:
- Passing a string to a float-typed function? Throws a TypeError.
- Passing an array to an int-typed function? Also a TypeError.
- Return values of functions are strictly checked. If your function says it returns a float, returning anintis allowed, but returning astringwill trigger aTypeError.
What About the Return Types?
You might think, “if a function declares : float, shouldn’t it require an actual float return?” PHP allows returning an int when the function signature specifies float.
For example:
declare(strict_types=1);
function getFloat(): float {
    return 1; // No error
}
This works because of the same int-to-float compatibility. The integer 1 is silently converted to 1.0.
How to Enforce True Strictness (Even Int vs Float)?
If you want to reject int values where float is expected, PHP won’t do it for you automatically. You’ll need to:
- Use static analysis tools (like PHPStan or Psalm) with strict configuration rules to catch these cases during development.
Manually check types at runtime:
function f($f) {
    if (!is_float($f)) {
        throw new TypeError("Expected float, got " . gettype($f));
    }
    var_dump($f);
}
f(1); // Will throw TypeError
What Other Languages Do
In stricter languages like:
- Rust: i32andf32are completely separate; explicit casting is mandatory.
- Go: You can’t pass an intto afloat64parameter; you must explicitly convert it.
- TypeScript: Numeric literals are all number, but if you want stricter checks, you must use custom types.
PHP’s design leans towards developer flexibility over strict enforcement, which can be both a blessing and a curse.
Considerations You Might Be Missing
- Silent Errors in Production: If you rely on strict_typesto prevent unintended type coercion, remember thatinttofloat(and vice versa) will still happen silently. This can lead to subtle bugs, especially in financial or precision-sensitive calculations.
- Performance: Manually adding runtime checks for stricter typing could impact performance if overused. It’s a trade-off between safety and speed.
- Legacy Code Compatibility: Many PHP projects have legacy code where type declarations were not enforced. Be cautious when introducing strict_types=1to an older codebase, as it may not catch all issues.
Finally
PHP’s strict_types=1 is a powerful tool for improving code quality, but it’s not as rigid as you might expect. The int-to-float loophole isn’t a bug – it’s a deliberate design choice to prioritize flexibility.
If you need true, unforgiving strictness, PHP isn’t the language for it. But for many use cases, this compromise strikes a balance between developer convenience and type safety.
Comments ()