PHP is dynamically typed: a value has a type, and a variable can hold any type. Types can be declared on parameters, return values, properties and constants, and values are converted automatically in many contexts. The rules are in the manual’s Types chapter.
The types
| Type | Values | Group |
|---|---|---|
null | One value: null | Special |
bool | true or false | Scalar |
int | Signed integer; 64-bit on a 64-bit build | Scalar |
float | IEEE 754 double precision | Scalar |
string | A sequence of bytes | Scalar |
array | An ordered map from int or string keys to values | Compound |
object | An instance of a class | Compound |
callable | Anything that can be called | Compound |
iterable | An alias for array|Traversable (a compile-time alias since 8.2) | Compound |
resource | A handle to an external resource, such as an open file | Special |
The functions gettype() and, since PHP 8.0, get_debug_type()
report the type of a value. get_debug_type() uses the same names as the
declarations: int, float, string,
bool, array, null, and the class name of an
object.
null
null is the only value of its type. A variable has it when it has been
assigned null, has not been assigned anything, or has been
unset(). Reading an undefined variable gives a warning (a notice before
PHP 8.0) and the value null.
bool
Values that convert to false: the boolean false, the
integer 0, the floats 0.0 and -0.0, the empty
string and the string "0", an empty array, and null. Every
other value is true, including "0.0", "00",
[0], any object and NAN.
int
Limits are given by PHP_INT_SIZE, PHP_INT_MIN and
PHP_INT_MAX. Integer overflow does not wrap: if a literal or a
calculation goes outside the range, the result becomes a float.
/ gives a float unless both operands are integers and the result is
exact; use intdiv() for integer division. See
Integer literals.
float
A float is usually a 64-bit IEEE 754 number. PHP_FLOAT_MAX,
PHP_FLOAT_MIN (the smallest positive normal value),
PHP_FLOAT_DIG and PHP_FLOAT_EPSILON describe the format.
INF and NAN are constants. Do not compare with
NAN: ==, ===, < and
> are false against any number, and != and
!== are true. Comparing with a boolean converts
NAN to true. Use is_nan() instead.
string
A string is a run of bytes. It is indexed from 0 with $s[0], and negative
offsets count from the end (PHP 7.1). Reading or writing an offset is by byte, not
by character. The older $s{0} syntax was removed in PHP 8.0.
See Strings.
array
An array is an ordered map, used as a list, a dictionary, a stack and a queue alike. Its syntax and key rules are on the Expressions page.
callable
A callable is a value that can be called. It may be a Closure, the name
of a function as a string, an array of an object or a class name and a method name,
'Class::method', or an object with __invoke(). Language
constructs such as echo and isset are not callables.
See Callables.
resource
A resource is a special value holding a reference to something external, such as a
file handle. is_resource() and get_resource_type() inspect
one. Many extensions have replaced their resources with opaque objects in PHP 8. A
resource cannot be used as a type in a declaration.
Type juggling
A value is converted automatically according to the context (manual).
| Context | Result |
|---|---|
| Numeric (arithmetic) | If either operand is a float or cannot be read as an int, both are floats and the result is a float. Otherwise both are integers. An operand that cannot be read as a number throws TypeError since 8.0 |
String (echo, ., interpolation) | The value is converted to a string, or TypeError is thrown |
Logical (if, ?:, !, &&) | The value is converted to bool |
| Integral and string (bitwise) | If all operands are strings, the result is a string, byte by byte. Otherwise they are integers |
| Comparison | See Comparison |
| Function (parameters, return values, properties) | See Coercive typing |
Numeric strings
WHITESPACES \s*
LNUM [0-9]+
DNUM ([0-9]*[.]{LNUM}) | ({LNUM}[.][0-9]*)
EXPONENT_DNUM (({LNUM} | {DNUM}) [eE][+-]? {LNUM})
INT_NUM_STRING {WHITESPACES} [+-]? {LNUM} {WHITESPACES}
FLOAT_NUM_STRING {WHITESPACES} [+-]? ({DNUM} | {EXPONENT_DNUM}) {WHITESPACES}
NUM_STRING ({INT_NUM_STRING} | {FLOAT_NUM_STRING})
A string matching NUM_STRING is numeric. A string that begins with a
numeric string and continues with other characters is leading-numeric. In a numeric
context, since PHP 8.0:
| String | Example | Result |
|---|---|---|
| Numeric | "12" " 1.5e3 " | Its number: int if it is integer-shaped and fits, otherwise float |
| Leading-numeric | "12 apples" | The leading number, with a warning |
| Non-numeric | "apples" | TypeError |
Before 8.0 trailing white space made a string only leading-numeric, a non-numeric string counted as 0 with a warning, and a leading-numeric string gave only a notice.
Conversions
| To | From | Result |
|---|---|---|
int | bool | false is 0, true is 1 |
float | Truncated toward zero. Outside the range the result is undefined; a float that cannot be represented as an int warns since 8.5 | |
string | A numeric or leading-numeric string gives its value, otherwise 0 | |
null | 0 | |
resource | The resource number | |
| Other | Undefined; do not rely on it | |
float | string | As for integer, as a float |
int, bool, null | Through int | |
| Other | Undefined; do not rely on it | |
string | true | "1" |
false, null | The empty string | |
float | Up to the number of significant digits set by precision (14 by default), or exponent form when large; the decimal point is always . since 8.0 | |
array | "Array", with a warning | |
object | The result of __toString(); otherwise Error | |
resource | "Resource id #N" | |
array | null | An empty array |
| A scalar or resource | An array of one element at key 0 | |
object | The properties as elements. Private and protected property keys are prefixed with null bytes, "\0Class\0prop" and "\0*\0prop" | |
object | array | A stdClass with one property per key |
null | An empty stdClass | |
| Other | A stdClass with a property named scalar |
Since PHP 8.1 an implicit conversion of a float with a fractional part to an
integer is deprecated, and since 8.5 converting NAN to any other type
warns.
Casts
A cast is a type name in parentheses before a value. White space inside the parentheses is ignored.
| Cast | Result | Status |
|---|---|---|
(int) | int | (integer) deprecated in 8.5 |
(bool) | bool | (boolean) deprecated in 8.5 |
(float) | float | (double) deprecated in 8.5; (real) removed in 8.0 |
(string) | string | (binary) deprecated in 8.5 |
(array) | array | |
(object) | object | |
(unset) | null | Removed in 8.0 |
(void) | Discards a value | Added in 8.5; used with #[\NoDiscard] |
settype() changes a variable’s type in place. intval(),
floatval(), strval() and boolval() do the same
conversions as function calls.
Comparison
== converts its operands. === compares type and value and
never converts.
| Operand 1 | Operand 2 | Rule |
|---|---|---|
null or string | string | Convert null to "", then compare numerically or as text |
bool or null | Anything | Convert both to bool; false is less than true |
object | object | Built-in classes define their own rules. Objects of different classes cannot be compared; same-class objects compare property by property |
string, resource, int or float | string, resource, int or float | Convert strings and resources to numbers and compare numerically |
array | array | The array with fewer elements is smaller. A key in the first missing from the second makes them incomparable. Otherwise compare value by value |
object | Anything | object is always greater |
array | Anything | array is always greater |
Numbers and strings since PHP 8.0
Comparing a number with a string compares numerically only if the string is numeric. Otherwise the number is converted to a string and they are compared as text.
| Expression | PHP 7 | PHP 8 |
|---|---|---|
0 == "a" | true | false |
"1" == "01" | true | true |
"10" == "1e1" | true | true |
100 == "1e2" | true | true |
Two numeric strings still compare as numbers, so "1e3" == "1000" is
true. Compare hashes and other digit strings with ===.
empty, isset and bool
| Expression | gettype() | empty() | is_null() | isset() | if ($x) |
|---|---|---|---|---|---|
$x = ""; | string | true | false | true | false |
$x = null; | NULL | true | true | false | false |
var $x; | NULL | true | true | false | false |
$x is undefined | NULL | true | true | false | false |
$x = []; | array | true | false | true | false |
$x = ['a', 'b']; | array | false | false | true | true |
$x = false; | bool | true | false | true | false |
$x = true; | bool | false | false | true | true |
$x = 1; | int | false | false | true | true |
$x = 42; | int | false | false | true | true |
$x = 0; | int | true | false | true | false |
$x = -1; | int | false | false | true | true |
$x = "1"; | string | false | false | true | true |
$x = "0"; | string | true | false | true | false |
$x = "-1"; | string | false | false | true | true |
$x = "php"; | string | false | false | true | true |
$x = "true"; | string | false | false | true | true |
$x = "false"; | string | false | false | true | true |
Loose comparison with ==
PHP 8 results. T is true, F is false.
true | false | 1 | 0 | -1 | "1" | "0" | "-1" | null | [] | "php" | "" | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
true | T | F | T | F | T | T | F | T | F | F | T | F |
false | F | T | F | T | F | F | T | F | T | T | F | T |
1 | T | F | T | F | F | T | F | F | F | F | F | F |
0 | F | T | F | T | F | F | T | F | T | F | F | F |
-1 | T | F | F | F | T | F | F | T | F | F | F | F |
"1" | T | F | T | F | F | T | F | F | F | F | F | F |
"0" | F | T | F | T | F | F | T | F | F | F | F | F |
"-1" | T | F | F | F | T | F | F | T | F | F | F | F |
null | F | T | F | T | F | F | F | F | T | T | F | T |
[] | F | T | F | F | F | F | F | F | T | T | F | F |
"php" | T | F | F | F | F | F | F | F | F | F | T | F |
"" | F | T | F | F | F | F | F | F | T | F | F | T |
With === a value is equal only to itself of the same type. Two arrays are
=== only if they have the same key and value pairs in the same order with
the same types, and two objects only if they are the same instance.
Type declarations
Types can be declared on parameters, return values, properties (PHP 7.4) and class,
interface, trait and enum constants (PHP 8.3). A value that does not fit is converted if
coercion allows it; otherwise a TypeError is
thrown (manual).
Every type except resource can be used.
| Type | Where | Since |
|---|---|---|
| Class, interface or enum name | Anywhere | 5.0 (enums: 8.1) |
self, parent | In a class | 5.0 |
array | Anywhere | 5.1 |
callable | Parameters and return values, not properties | 5.4 |
bool int float string | Anywhere | 7.0 |
iterable | Anywhere | 7.1 |
void | Return only | 7.1 |
object | Anywhere | 7.2 |
mixed | Anywhere | 8.0 |
static | Return only | 8.0 |
null, false | In a union from 8.0; on their own from 8.2 | 8.0 |
never | Return only | 8.1 |
true | Anywhere | 8.2 |
voidandneverare standalone: they cannot be nullable or part of a union.voidmeans the function returns nothing, so it may have onlyreturn;. Aneverfunction must not return at all: it throws or exits.mixedalready includesnull, so?mixedis an error. A function with return typemixedmust return a value: reaching the end without one is aTypeError.selfandparentrefer to the class where the declaration is written.staticrefers to the class called at run time, which may be a subclass.- Names such as
integerandbooleanare not type aliases. They mean a class of that name, and PHP warns about the likely mistake.
Nullable types
?T means T or null (PHP 7.1) and is the same as
the union T|null. Writing T $x = null used to make a
parameter implicitly nullable. That is deprecated since PHP 8.4; write
?T $x = null instead.
Union types
A union (PHP 8.0) is written
int|string. The compiler rejects redundant forms:
- Each name-resolved type may appear only once, so
int|string|INTis an error. A class in a union and its alias throughusecount as the same name. boolcannot be combined withfalseortrue,falseandtruecannot be combined, anditerablecannot be combined witharrayorTraversable.objectcannot be combined with a class type.mixedandnevercannot appear in a union.- Before 8.2
nullandfalsecould not stand alone, andfalse|nulland?falsewere not allowed.
Only redundancy that can be seen without loading classes is an error, so
A|B is legal even when B extends A.
Intersection types
An intersection (PHP 8.1)
is written Countable&Traversable and requires a value to be an
instance of all the listed types. Members must be class-like types: scalars,
object, mixed and static are errors, and each name
may appear only once.
DNF types
Disjunctive normal form (PHP 8.2) is a union of intersections, with each intersection in parentheses:
function save((Countable&Traversable)|array|null $items): void {}
nullAn intersection written without parentheses inside a union is a parse error. A type
already covered by a more general member, such as A|(A&B), is an
error, and so is the same intersection listed twice.
Coercive typing
By default a scalar value of the wrong type is converted to the declared scalar type, if the conversion is defined:
| Declared | Accepts and converts |
|---|---|
int | Whole floats (1.0), numeric strings, booleans. A float with a fractional part still converts but is deprecated since 8.1 |
float | Integers, numeric strings, booleans |
string | Integers, floats, booleans and objects with __toString() |
bool | Integers, floats and strings |
For an int or float parameter a string must be fully
numeric, with white space allowed on either side: "5" and
" 5 " are accepted, and "5 apples" is a
TypeError. Passing null to a scalar parameter of an internal
function is deprecated since PHP 8.1. A user-defined function rejects it with a
TypeError unless its type allows null.
For a union, if the exact type is present it is used. Otherwise conversions are
tried in the order int, float, string,
bool, taking the first that works. A numeric string with both
int and float available goes by its own numeric type:
"42" becomes an integer and "42.0" a float. A string that
fits neither falls back to bool if the union has it.
Strict typing
declare(strict_types=1);
(PHP 7.0) turns off
coercion for calls made from that file. The check is made at the call, so it is the
caller’s file that decides. The only conversion left is int to
float.
<?php
declare(strict_types=1);
function half(int $n): float { return $n / 2; }
half(4); // 2.0
half("4"); // TypeError: argument must be of type int, string given
half(1.5); // TypeError: argument must be of type int, float given
- Internal callback calls are not affected, because they are not made from the file.
- Strict mode applies only to scalar types. A class type is always checked exactly.
- A file with
declare(strict_types=1)calling a function defined in a non-strict file is still checked strictly.
Return types and variance
A return type goes after the parameter list with a colon. It is checked against the value actually returned, under the file’s typing mode. Function, method and property types follow the variance rules when overriding:
- Return types are covariant: a child may return a narrower type.
- Parameter types are contravariant: a child may accept a wider type.
- Property types are invariant: a child must declare the same type.