In PHP, what does declare(strict_types=1) change about how scalar parameter and return type declarations are checked?
answer
- coercive mode is the default
- only int, float, string, bool affected
- mismatch throws TypeError
- one exception: int widens to float
- must be the file's first statement
basics
~20 sBy default PHP coerces a wrong-typed scalar into the declared type when it can, so "12" passed to an int parameter becomes 12. With declare(strict_types=1) the value must already match, or a TypeError is thrown; only int-to-float widening survives.
solid answer
~40 sPHP checks scalar declarations (`int`, `float`, `string`, `bool`) in one of two modes. The default, **coercive mode**, converts a value that can be converted: `"12"` becomes `12`, `true` becomes `1`, `5` becomes `"5"`; a value that cannot be converted, such as `"abc"` for `int` or an array for `string`, still throws `TypeError`. `declare(strict_types=1)` switches the file to **strict mode**: the value's type must match the declaration exactly, otherwise `TypeError`, and the single allowed conversion is an `int` passed where a `float` is declared. The declaration must be the first statement in the file and applies to that file only. Class, interface and `array` declarations were never coerced, so the flag changes nothing for them.
code
php · 16 lines<?php
// no strict_types declaration: coercive mode
function fineCents(int $days): int
{
return $days * 25;
}
var_dump(fineCents("4")); // int(100): "4" coerced to 4
var_dump(fineCents(true)); // int(25): true coerced to 1
try {
fineCents("four");
} catch (TypeError $e) {
echo $e->getMessage(), PHP_EOL; // not coercible even here
}go deeper
Recall the two modes: coercive converts "12" to 12, strict throws TypeError. Know that int still passes a float declaration and that the declare line goes first in the file.
Explain which values coercive mode rejects anyway, why class types are unaffected, and that the flag covers built-in function calls and return values, not operators.
Show how strict mode moves type failures to the call site and forces explicit conversion at input boundaries, and why that pairs with static analysis in CI.
Weigh the cost of enforcing strict_types on every file in a large codebase — boundary conversions, test churn, tooling — against the silent-coercion bugs it removes.
## Two ways PHP can check a scalar declaration A **type declaration** is the type written in front of a parameter or after a function's parameter list: `function fineCents(int $days): int`. PHP checks it at runtime every time the function is called and every time it returns. For **scalar types** — `int`, `float`, `string` and `bool` — the check runs in one of two modes: - **Coercive mode** (the default, sometimes called weak mode): if the value has a different scalar type but can be converted, PHP converts it and the function receives the converted value. - **Strict mode** (`declare(strict_types=1)`): the value must already have the declared type. A mismatch throws `TypeError`. `TypeError` is an `Error`, not an `Exception`, so a `catch (Exception $e)` block does not see it. ## What coercive mode accepts Coercion is not "anything goes". It follows fixed rules, and some values are rejected in both modes: | Value passed | Declared `int` | Declared `float` | Declared `string` | Declared `bool` | |---|---|---|---|---| | `"12"` | `12` | `12.0` | `"12"` | `true` | | `"abc"` | `TypeError` | `TypeError` | `"abc"` | `true` | | `7.0` | `7` | `7.0` | `"7"` | `true` | | `7.5` | `7`, plus a deprecation | `7.5` | `"7.5"` | `true` | | `true` | `1` | `1.0` | `"1"` | `true` | | `[1, 2]` | `TypeError` | `TypeError` | `TypeError` | `TypeError` | Two details from recent releases matter here: 1. Since **PHP 8.1**, passing a float with a fractional part to an `int` declaration in coercive mode emits `Deprecated: Implicit conversion from float 7.5 to int loses precision` and truncates. 2. `null` is never coerced for a user-defined function: a non-nullable `int` parameter given `null` throws `TypeError` in both modes. (Built-in functions have their own, older rule for `null`.) ## What strict mode changes In strict mode the conversion column collapses. A value either already has the declared type, or the call fails: ```php <?php declare(strict_types=1); function fineCents(int $days): int { return $days * 25; } fineCents(4); // int(100) fineCents("4"); // TypeError: Argument #1 ($days) must be of type int, string given ``` The one conversion strict mode keeps is **int-to-float widening**: an `int` passed to a `float` parameter, or returned from a function declared `: float`, is accepted and becomes a float. The reverse is not: `7.0` passed to `int` is a `TypeError` in strict mode even though it has no fractional part. Strict mode applies to the same places coercive mode does: - **arguments** passed to user-defined functions and methods; - **arguments** passed to built-in functions such as `strlen()` — `strlen(123)` throws `TypeError` in a strict file and returns `3` in a coercive one; - **return values** checked against a function's return declaration. Which file's mode governs a given call is its own rule: for arguments it is the file that makes the call, for return values the file that declares the function. ## Writing the declaration correctly The engine is strict about the declaration itself: - It must be the **very first statement** in the file. Only the opening `<?php` tag, whitespace, comments and other `declare` statements may come before it; a `namespace` line comes after it. Anything else is a compile error: `strict_types declaration must be the very first statement in the script`. - It cannot use block mode — `declare(strict_types=1) { ... }` is a compile error. - Its value must be `0` or `1`. - It is **per file**. An included or required file does not inherit it from the file that includes it, and there is no ini directive that turns it on globally. ## What the flag does not touch Strict typing is only defined for scalar declarations. Class and interface types, `array`, `callable`, `iterable`, `object` and `mixed` have no coercion in either mode — a string never becomes an object — so for them the flag changes nothing. It also does not change how operators behave: `"5" + 3` is still `8` in a strict file, and `==` still juggles types. Strict types govern declarations, not expressions. ## Why most modern code turns it on Coercion hides mistakes at the point where they are cheapest to catch. A controller that forgets to validate and convert a request value still "works" in coercive mode because `"4"` quietly becomes `4`; so do `true` becoming `1`, `"1e3"` becoming `1000`, and a fractional float truncated to `int` with only a deprecation notice. Strict mode moves that failure to the call site and makes the declared type a promise about the value, which is also what static analysers assume. The price is that every boundary — request input, which always arrives as strings, CSV fields, some database result sets — needs an explicit, validated conversion before the value is passed on.
- In PHP, does declare(strict_types=1) make the + operator or == comparisons strict as well?No. `strict_types` governs only argument and return values checked against scalar type declarations. Arithmetic still converts numeric strings, so `"5" + 3` is `8`, and `==` still juggles operand types exactly as in a coercive file. To avoid juggling in comparisons you use `===`; to avoid it in arithmetic you convert and validate input before computing.
- In PHP, can declare(strict_types=1) be placed after a namespace statement or wrapped in a block?Neither. It must be the very first statement of the file, before `namespace`; only the opening tag, whitespace, comments and other `declare` statements may precede it, otherwise compilation fails with `strict_types declaration must be the very first statement in the script`. Block mode, `declare(strict_types=1) { ... }`, is also a compile error, and the value must be `0` or `1`.
- In PHP strict mode, is the float 7.0 accepted by an int parameter?No. Strict mode allows only one conversion, `int` to `float`. A float is rejected by an `int` declaration even when it has no fractional part, so `7.0` throws `TypeError`. In coercive mode `7.0` becomes `7` silently, and `7.5` becomes `7` with a PHP 8.1+ deprecation notice about losing precision.
Coercive mode is a cashier who accepts a banknote in the wrong currency and converts it at the till; strict mode is a cashier who takes only the currency on the sign and refuses everything else, with one standing exception: whole coins are accepted where the price has decimals, just as an int is accepted for a float.
saying these in an interview costs you the question
- Says strict_types is a php.ini setting that can be enabled for the whole application.
- Thinks coercive mode accepts anything, including "abc" for an int parameter.
- Believes strict mode also rejects an int passed to a float parameter.
- Claims strict_types makes == behave like === or stops arithmetic on numeric strings.
- Expects an included file to inherit strict mode from the file that includes it.
- Catches the mismatch with catch (Exception $e), not realising TypeError is an Error.