Typed Properties
For the longest time, PHP developers denoted the property type
using the @var
annotation in a code comment, similar to
the annotations we mentioned earlier for parameters and return
values:
class C
{
/** @var int */
private $property;
public function m(): void
{
$this->property = 'string';
}
}
$o = new C;
$o->m();
Just like the type annotations for parameters and return values
in code comments, the @var
annotation used in the
example shown above is not interpreted by PHP itself in any way.
Once again it is only used by static analysis tools, documentation
generators, and IDEs.
PHP 7.4 introduced the syntax shown below:
class C
{
private int $property;
public function m(): void
{
$this->property = 'string';
}
}
$o = new C;
$o->m();
Executing the code above will lead to the following type error:
Uncaught TypeError: Typed property C::$property must be int,
string used in ...
The interpretation of scalar types for properties that have a declared type is, of course, subject to the same rules discussed earlier for parameters and return values.
All types supported by PHP can be used in property type
declarations, except for void
and
callable
. void
is reserved for return type
declarations and would not make sense in the context of properties
anyway. The inconsistencies
in how callable
works make it impossible to support
this type for properties. Support for callable
might be
considered in the future after the problems associated with this
special type have been resolved.
Property types are invariant: the type of a public
or protected
property that is declared in a parent
class must not be changed in a child class. This includes the
addition and removal of type information to or from a property
declaration. If the property is private
in the parent
class then the child class may add, change, or remove type
information from the property declaration.
The default value for a typed property has to match the type of
the property. The only exception is that float
properties also accept integer
default values. This is
consistent with how default values are handled for typed parameter
declarations.
Typed properties cannot have a null
default value
unless the property’s type is explicitly declared to be nullable.
This is different from parameter type declarations where a
null
default value automatically implies a nullable
type.
class C
{
private int $i = null;
}
Executing the code above will lead to the following error:
PHP Fatal error: Default value for property of type int
may not be null. Use the nullable type ?int to allow
null default value in ...
If $i
in the example shown above should indeed
accept null
as a value and be initialized to that then
the ?int
syntax must be used to declare a nullable type
for $i
:
class C
{
private ?int $i = null;
}
When a typed property does not have a default value, PHP does not
implicitly assign a null
default value:
class C
{
private int $i;
public function m(): int
{
return $this->i;
}
}
$o = new C;
$o->m();
Executing the code above will lead to the following error:
Uncaught Error: Typed property C::$i must not be accessed
before initialization in ...
Using unset()
on a typed property returns it to this
uninitialized state:
class C
{
private int $i;
public function __construct(int $i)
{
$this->i = $i;
}
public function m(): int
{
unset($this->i);
return $this->i;
}
}
$o = new C(1);
$o->m();
Executing the code above will lead to the same error as before:
Uncaught Error: Typed property C::$i must not be accessed
before initialization in ...
To be consistent with how untyped properties are handled,
accessing an uninitialized property for reading will automatically
invoke a __get()
interceptor method if one is provided
by the object in question.