PHPType Juggling & Casting

Type Juggling & Casting

PHP is a weakly typed language, which means it will often convert a value from one type to another automatically, without you asking it to. This automatic conversion is called type juggling. It happens constantly — every time you compare a string to a number, concatenate a number into a string, or use a value in an if condition. Understanding the rules behind it turns what looks like unpredictable behavior into something you can reason about, and it explains why PHP 8 tightened several of these rules compared to earlier versions.

Implicit conversion in arithmetic

When you use an arithmetic operator on a string that "looks like" a number, PHP converts the string to an int or float before doing the math.

Numeric strings in arithmetic

PHP
<?php
$a = "10";
$b = "4";

var_dump($a + $b);    // int(14) - both strings converted to int
var_dump("10" + 4.5); // float(14.5)
var_dump("10 apples" . " and " . 5 . " oranges");
int(14)
float(14.5)
string(24) "10 apples and 5 oranges"

Note the difference: the + operator triggers numeric conversion, while the . (concatenation) operator triggers the opposite — converting numbers into strings.

Implicit conversion in boolean contexts

Every value can be interpreted as a boolean when used in an if, while, or logical operator. PHP calls a value falsy if it converts to false, and everything else is truthy. The Booleans & Truthiness page covers this in full detail, but the short version is: 0, 0.0, "", "0", empty arrays [], and null are the falsy values; every other value, including the string "0.0", is truthy.

Truthiness in an if condition

PHP
<?php
$values = [0, "0", "0.0", "", "false", [], [0]];

foreach ($values as $value) {
    echo var_export($value, true) . ' => ' . ($value ? 'truthy' : 'falsy') . "\n";
}
0 => falsy
'0' => falsy
'0.0' => truthy
'' => falsy
'false' => truthy
array (
) => falsy
array (
  0 => 0,
) => truthy
Explicit casting

Rather than relying on an operator to trigger a conversion, you can force one directly by writing the target type in parentheses in front of a value. This is the clearest way to convert a type when the surrounding code does not make the intent obvious on its own.

Explicit casts

PHP
<?php
$raw = "42.9";

var_dump((int) $raw);     // int(42) - truncates, does not round
var_dump((float) $raw);   // float(42.9)
var_dump((string) 42.9);  // string(4) "42.9"
var_dump((bool) "");      // bool(false)
var_dump((array) "hi");   // array(1) { [0]=> string(2) "hi" }
int(42)
float(42.9)
string(4) "42.9"
bool(false)
array(1) {
  [0]=>
  string(2) "hi"
}
(int) truncates, it does not round
Casting `4.9` to `int` gives `4`, not `5`. If you need rounding behavior, use `round()` before casting, e.g. `(int) round(4.9)` gives `5`. This distinction quietly causes bugs in code that assumes a cast behaves like mathematical rounding.
PHP 8's stricter numeric strings

Before PHP 8.0, a "leading numeric string" like "123abc" was treated leniently: using it in arithmetic silently used the leading 123 and threw away the rest, often without so much as a notice. PHP 8.0 tightened this: a string is only considered a proper numeric string if the entire string (aside from surrounding whitespace) parses as a number. Leading-numeric strings that are not fully numeric now trigger a warning when used in arithmetic, even though they still coerce to their leading numeric portion for backward compatibility.

Leading-numeric strings in PHP 8

PHP
<?php
var_dump("123" + 1);     // int(124) - fully numeric, no warning
var_dump("123abc" + 1);  // int(124) - Warning: A non-well formed numeric value
var_dump("abc" + 1);     // TypeError in PHP 8: Unsupported operand types
Fully non-numeric strings now throw
In PHP 8, `"abc" + 1` is not just a warning — it throws a `TypeError` because `"abc"` has no numeric portion at all to fall back on. Only purely non-numeric strings are affected this way; strings with at least a usable numeric prefix still coerce, with a warning.
Opting out with declare(strict_types=1)

By default, PHP applies type juggling to scalar type hints on function parameters and return types too — passing an int where a function expects a string parameter just converts it silently. Adding declare(strict_types=1); as the very first statement in a file disables that coercion for calls made from that file: from then on, scalar type declarations must be matched exactly, or a TypeError is thrown.

strict_types in action

PHP
<?php
declare(strict_types=1);

function double(int $n): int
{
    return $n * 2;
}

echo double(5);        // 10 - exact type, works fine
echo double("5");      // TypeError: Argument #1 ($n) must be of type int, string given
  • declare(strict_types=1) must be the first statement in the file, before even a blank line of other code, or PHP raises a fatal error.

  • Strict typing only affects scalar type declarations (int, float, string, bool); it never changes how +, ., or if juggle types on their own.

  • An int passed where a float parameter is expected is still allowed under strict types, because widening an int to a float loses no information.

From

To

Result

"42"

int

42

"42.9"

int

42 (truncated)

42

string

"42"

""

bool

false

"0.0"

bool

true

[]

bool

false

Tip
Add `declare(strict_types=1);` to new PHP files by default. It catches type mistakes at the exact call site instead of letting a silently-coerced value travel deep into your program before causing a confusing failure somewhere else.