Type Juggling and Conversion

PHP converts values between types automatically when an operation needs it — adding a number to a numeric string, using a number as a condition, comparing a string with an integer. This "type juggling" is convenient, but its rules have surprised many developers. This lesson explains the rules, how to convert explicitly, and how strict comparisons and strict types keep you safe.

Automatic conversion

Arithmetic operators convert their operands to numbers; the dot operator converts to strings:

PHP
<?php

var_dump("5" + 3);        // numeric string -> number
var_dump("2.5" * 2);
var_dump(5 . 3);          // numbers -> strings, then joined
var_dump("10" == 10);
var_dump(true + true);
Output
int(8)
float(5)
string(2) "53"
bool(true)
int(2)

Form input, URL parameters, database results and file contents usually arrive as strings, so this conversion happens all the time.

When strings aren't numbers

PHP 8 is stricter than older versions. A string that only starts with a number is used with a warning; a string that isn't numeric at all throws a TypeError:

PHP
<?php

var_dump("12 apples" + 3);
var_dump("apples" + 3);
Output
Warning: A non-numeric value encountered in main.php on line 3
int(15)

Fatal error: Uncaught TypeError: Unsupported operand types: string + int in main.php:4
Stack trace:
#0 {main}
  thrown in main.php on line 4

Explicit casts

Write the target type in parentheses to convert explicitly. Casting never throws; it always produces something:

PHP
<?php

var_dump((int) "42");
var_dump((int) "42.9");      // truncated, not rounded
var_dump((int) "abc");       // no digits: 0
var_dump((float) "3.5kg");
var_dump((string) 7.0);
var_dump((bool) "0");
var_dump((array) "solo");
Output
int(42)
int(42)
int(0)
float(3.5)
string(1) "7"
bool(false)
array(1) {
  [0]=>
  string(4) "solo"
}

The functions intval(), floatval(), strval() and boolval() do the same as the casts. Use the short cast names (int), (float), (bool) and (string) — the long forms such as (integer) and (boolean) are deprecated as of PHP 8.5.

Because casts accept anything, they're the wrong tool for validating input: (int) "abc" silently becomes 0. To check that input really is a number, use filter_var:

PHP
<?php

foreach (["42", "4.2", "abc", " 7 ", "-3"] as $input) {
    $n = filter_var($input, FILTER_VALIDATE_INT);
    echo str_pad("'$input'", 7), $n === false ? "invalid" : "valid: $n", "\n";
}
Output
'42'   valid: 42
'4.2'  invalid
'abc'  invalid
' 7 '  valid: 7
'-3'   valid: -3

Truthy and falsy values

When a value is used as a condition, PHP converts it to a boolean. These values are falsy; everything else is truthy:

  • false, 0, 0.0, null
  • the empty string "" and the string "0"
  • an empty array []
PHP
<?php

foreach ([0, 1, -1, "", "0", "0.0", " ", "false", [], [0], null] as $v) {
    echo str_pad(json_encode($v), 8), $v ? "truthy" : "falsy", "\n";
}
Output
0       falsy
1       truthy
-1      truthy
""      falsy
"0"     falsy
"0.0"   truthy
" "     truthy
"false" truthy
[]      falsy
[0]     truthy
null    falsy

Note the odd ones: "0" is falsy but "0.0" and "false" are truthy. When the distinction matters, compare explicitly instead of relying on truthiness.

== versus ===

The loose equality operator == converts types before comparing. The strict operator === requires the same type and value:

PHP
<?php

var_dump(1 == "1");          // true: string converted
var_dump(1 === "1");         // false: different types
var_dump(0 == "");           // false in PHP 8
var_dump(null == false);     // true
var_dump(null === false);    // false
var_dump("abc" == 0);        // false in PHP 8 (true in PHP 7!)
var_dump("1e3" == "1000");   // true: both numeric strings
Output
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(false)
bool(true)

PHP 8 fixed the most notorious cases — comparing a number with a non-numeric string now compares them as strings — but == still has surprises like "1e3" == "1000". Use === and !== by default, and reach for == only when you deliberately want conversion.

The same applies to functions that search: in_array and array_search compare loosely unless you pass true as the third argument.

PHP
<?php

$allowed = ["1", "2", "3"];
var_dump(in_array(1, $allowed));          // loose: true
var_dump(in_array(1, $allowed, true));    // strict: false
Output
bool(true)
bool(false)

Strict types for function calls

With declare(strict_types=1) at the top of a file, PHP stops converting arguments to match a function's declared parameter types — the one exception being that an int is allowed where a float is expected. Without it, "5" is quietly converted to 5 for an int parameter:

PHP
<?php

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

var_dump(square("5"));     // allowed in the default (coercive) mode
var_dump(square(5.0));     // a float with no fraction is accepted too
var_dump(square(5.5));
Output
int(25)
int(25)

Deprecated: Implicit conversion from float 5.5 to int loses precision in main.php on line 3
int(25)

Lesson 29 covers type declarations and strict mode in depth.

Try it yourself

Validate a list of ages typed by users: accept whole numbers from 0 to 130 and report the rest.

Try it yourself · main.php
Edit the code, copy it into main.php, then run it with php main.php (PHP 8.5). How to install PHP
Output
'34' accepted as 34
'abc' rejected
'-5' rejected
'131' rejected
'27' accepted as 27
'12.5' rejected
'' rejected