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.