Type coercion vs type casting — implicit vs explicit conversion
Definition
Type coercion is an implicit, automatic conversion PHP performs when an operation requires a type different from the one provided — the runtime decides the target type based on context, and you never wrote a conversion instruction. Type casting is an explicit, programmer-authored conversion using a cast operator ((int), (string), (bool), etc.) that expresses intent clearly in source code. The distinction matters because coercion is invisible and context-dependent, making it a source of subtle bugs; casting is visible and deterministic.
In Practice
<?php
// TYPE COERCION — PHP converts automatically, you wrote nothing
// Arithmetic context coerces string to int/float:
$result = "5" + 3; // 8 (int) — PHP coerced "5" to int 5
$result = "5.5" + 1; // 6.5 (float)
$result = "5 items" + 1; // 6 (int) — warning in PHP 8, silently in PHP 7
// Boolean context coerces in conditionals:
if ("") { /* not reached — empty string coerces to false */ }
if ("0") { /* not reached — "0" is falsy in PHP! */ }
if (0) { /* not reached */ }
if ([]) { /* not reached — empty array is falsy */ }
if ("false") { /* IS reached — non-empty non-"0" string is truthy */ }
// Loose comparison (==) coerces before comparing:
var_dump(0 == "foo"); // PHP 7: true (!!), PHP 8: false — changed
var_dump(0 == ""); // PHP 7: true, PHP 8: false — changed
var_dump("1" == "01"); // true — both coerced to int 1
// TYPE CASTING — explicit, programmer-authored, visible
$str = "42 items";
$num = (int) $str; // 42 — truncates at first non-numeric char
$float = (float) "3.14px"; // 3.14
$bool = (bool) ""; // false
$arr = (array) "hello"; // ["hello"]
$obj = (object) ['a' => 1]; // stdClass with property $a = 1
// Strict mode prevents argument coercion:
declare(strict_types=1);
function double(int $n): int {
return $n * 2;
}
double(5); // 10
double("5"); // TypeError in strict mode — no coercion allowed
double(5.9); // TypeError in strict mode — float not accepted as intIn Laravel, coercion appears at the model layer: Eloquent casts ($casts array) perform explicit, configured casting when you access an attribute. Without a cast, a boolean column stored as 0/1 in SQLite returns a string — a silent type mismatch that PHP coerces silently but that fails strict comparisons.
In Context
Interviews raise this topic when discussing PHP 8 migration, bugs in legacy code, or type safety. The critical historical fact is that PHP 7's loose comparison behavior was a security vulnerability vector: 0 == "admin" was true, meaning hash comparisons with == instead of === could be bypassed. PHP 8 changed this — 0 == "non-numeric-string" is now false. Knowing this shows you track security implications of language changes.
A common misconception is that declare(strict_types=1) makes PHP fully strongly typed. It only affects user-defined function argument coercion within that file — it does not affect arithmetic operations, loose comparisons, or type coercion in internal PHP functions.
Related terms: "type juggling" is the informal name for PHP's implicit coercion behavior. "Type safety" refers to a language design property where type errors are caught at compile time — PHP is not type-safe in this sense; it is type-checked at runtime. "Strict comparison" (===) checks both value and type without coercion and is almost always the correct choice.