Typed Properties In PHP
It shows how to define the type of properties in PHP classes introduced in PHP 7.4. Since PHP 7.4, we can define the data type of a variable while declaring it.
PHP was considered as the weakly typed language before the release of PHP 7. As PHP is moving forward, it’s focusing more on type safety. Now we can define the data type of a variable while declaring it. It makes it much easier to identify the type of values that a variable can have. Also, this new feature introduced in PHP 7.4 does not break existing code, hence making it easy to migrate from previous versions. The rest of the sections of this tutorial explains the usage of property types in PHP.
Typed Property Rules
This section clears the usage of typed properties. We can use typed properties only if below-listed conditions are met.
- The PHP version is at least PHP 7.4
- Typed properties are only allowed in classes
- The typed properties must be used only with access modifiers including var, public, protected and private
- We can use all types including Class Type except void and callable
Example Code
This section provides an example of a class using typed properties. It shows that the appropriate types are defined for all the properties of the Class Vehicle while declaring them.
abstract class Vehicle
public int $id;
public string $name;
public string $color;
public string $bodyType;
public float $fuelCapacity;
public int $numberOfDoors;
>
class Car extends Vehicle
public bool $roofLoad;
>
We can see that the type int is used for the property id. It means that the id property can only be assigned with integer values. It will throw an exception in case any other type of value is assigned to id. This ensures that the object properties are assigned with the relevant values. It will also avoid functions like is_int() to check the type of values.
Type Of Types
We can use the below-listed types for typed properties. Using void and callable as property type is not allowed.
Uninitialized State
A new variable state i.e. uninitialized is introduced with typed properties. If we try to access the property without initializing it, Fatal error will be thrown as shown below.
$car = new Car();
echo $car->numberOfDoors;
// Result:
Fatal error: Uncaught Error: Typed property Car::$numberOfDoors
must not be accessed before initialization
We can avoid such errors by initializing the propery as shown below.
$car = new Car();
$car->numberOfDoors = 4;
echo $car->numberOfDoors;
Below listed are the rules of uninitialized typed properties.
- The uninitialized typed properties cannot be accessed as shown above
- We can create objects having uninitialized properties since the check will be made while accessing the property
- We must assign appropriate value to the typed properties before accessing it
- We can use the keyword unset to uninitialized the typed properties
Default Values
We can also assign the default values to the properties while declaring them as shown below.
abstract class Vehicle
public int $id; // No default value, it must be initialized before accessing it
public ?string $name = null; // Only null value is allowed for nullable
public string $color = 'Black'; // The default color
public string $bodyType; // No default value
public float $fuelCapacity = 16.85; // Default float value
public int $numberOfDoors = 2; // Default integer value
>
class Car extends Vehicle
public string $bodyType = 'Sedan'; // Default string value
public bool $roofLoad = false; // Default boolean value
>
The default values are not allowed for the types including object, classes, and interfaces.
Static Properties
We can also define the type of static properties as shown below.
class Car extends Vehicle
public static int $count; // Typed static variable
public bool $roofLoad;
>
Constructors
We can use the constructors to assign the default values to the properties as shown below.
class Driver
public string $name;
public function __construct(string $name)
$this->name = $name; // Initializing string property
>
>
abstract class Vehicle
public int $id;
public string $name;
public string $color;
public string $bodyType;
public float $fuelCapacity;
public int $numberOfDoors;
public function __construct(int $id, string $name)
$this->id = $id; // Initializing integer property
$this->name = $name; // Initializing string property
>
>
class Car extends Vehicle
public bool $roofLoad;
public Driver $driver;
public function __construct(int $id, string $name, string $bodyType, string $driverName)
parent::__construct($id, $name);
$this->bodyType = $bodyType; // Initializing boolean property
$this->driver = new Driver($driverName); // Initializing class property
>
>
// Example
$car = new Car(1125, 'Chevrolet', 'A Body', 'Rick');
echo "Id: " . $car->id;
echo "Driver: " . $car->driver->name;
This is how we can initialize the properties using constructors.
Inheritance
Most of the object-oriented programming languages including Java allows changing the type in child classes which is not true with PHP 7.4 as of now. The below-mentioned example clears it.
class Driver
public string $name;
>
class TrainedDriver extends Driver
public string $institute;
>
abstract class Vehicle
public int $id;
public string $name;
public string $color;
public string $bodyType;
public float $fuelCapacity;
public int $numberOfDoors;
public Driver $driver;
>
class Car extends Vehicle
public string $bodyType = 'Sedan'; // It's absolutely fine since the type is same
public TrainedDriver $driver; // Not valid since type changed
public bool $roofLoad;
>
In the above example, though TrainedDriver is also Driver, but the change in type is not allowed in child classes.
Type Coercion and Strict Types
PHP tries to convert the values where possible as shown below.
// The string will be auto-convered to integer value
$car->id = '1231';
We can also disable this behavior as shown below.
declare(strict_types=1);
.
.
$car->id = '1231';
// It will throw error
Fatal error: Uncaught TypeError:
Typed property Car::$id must be int, string used
Type Hint
With the introduction of typed properties, there is no need to add comments for type hinting as shown below.
// Class without typed properties
class Foo
/** @var int $id */
private $id;
/** @var string $name */
private $name;
>
// Class with typed properties
class Foo
private int $id;
private string $name;
>
Summary
With the introduction of typed properties, it will make it easy to define the classes with more clarity. It won’t leave any confusion while assigning the values to the typed properties. In the older version of PHP, the developers were forced to use getter and setter methods to enforce type contracts. Also, there is no need to add comments for type hinting.
Php declare property type
While waiting for native support for typed arrays, here are a couple of alternative ways to ensure strong typing of arrays by abusing variadic functions. The performance of these methods is a mystery to the writer and so the responsibility of benchmarking them falls unto the reader.
PHP 5.6 added the splat operator (. ) which is used to unpack arrays to be used as function arguments. PHP 7.0 added scalar type hints. Latter versions of PHP have further improved the type system. With these additions and improvements, it is possible to have a decent support for typed arrays.
function typeArrayNullInt (? int . $arg ): void >
function doSomething (array $ints ): void (function (? int . $arg ) <>)(. $ints );
// Alternatively,
( fn (? int . $arg ) => $arg )(. $ints );
// Or to avoid cluttering memory with too many closures
typeArrayNullInt (. $ints );
function doSomethingElse (? int . $ints ): void /* . */
>
$ints = [ 1 , 2 , 3 , 4 , null ];
doSomething ( $ints );
doSomethingElse (. $ints );
?>
Both methods work with all type declarations. The key idea here is to have the functions throw a runtime error if they encounter a typing violation. The typing method used in doSomethingElse is cleaner of the two but it disallows having any other parameters after the variadic parameter. It also requires the call site to be aware of this typing implementation and unpack the array. The method used in doSomething is messier but it does not require the call site to be aware of the typing method as the unpacking is performed within the function. It is also less ambiguous as the doSomethingElse would also accept n individual parameters where as doSomething only accepts an array. doSomething’s method is also easier to strip away if native typed array support is ever added to PHP. Both of these methods only work for input parameters. An array return value type check would need to take place at the call site.
If strict_types is not enabled, it may be desirable to return the coerced scalar values from the type check function (e.g. floats and strings become integers) to ensure proper typing.
same data type and same value but first function declare as a argument type declaration and return int(7)
and second fucntion declare as a return type declaration but return int(8).
function argument_type_declaration(int $a, int $b) return $a+$b;
>
function return_type_declaration($a,$b) :int return $a+$b;
>
PHP Typed Properties
Summary: in this tutorial, you wil learn how to define typed properties by adding type hints to class properties.
Introduction to the PHP typed properties
The following example defines the BankAccount class with a property called $balance :
class BankAccount < public $balance; >
Code language: PHP (php)
The default value of the $balance property is null .
$account = new BankAccount(); var_dump($account->balance); // null
Code language: PHP (php)
PHP 7.4 allows you to type hints the class properties with any types except void and callable . For example:
class BankAccount < public float $balance; >
Code language: PHP (php)
In this example, the $balance property has the type float . When you add the float type to $balance property, the following code causes an error:
$account = new BankAccount(); var_dump($account->balance); // null
Code language: PHP (php)
Fatal error: Uncaught Error: Typed property BankAccount::$balance must not be accessed before initialization
Code language: plaintext (plaintext)
It doesn’t work because the $balance property now becomes uninitialized. The default value of the $balance property is not null like before. Notice that you can still create a new object with typed properties uninitialized.
To read from a typed property, you need to initialize it first. For example:
class BankAccount < public float $balance; > $account = new BankAccount(); $account->balance = 0; var_dump($account->balance); // 0
Code language: PHP (php)
For properties with scalar types, you can initialize them in the declaration. For example:
class BankAccount < public float $balance = 0; > $account = new BankAccount(); var_dump($account->balance); // 0
Code language: PHP (php)
Alternatively, you can initialize the typed properties in the constructor of the class:
class BankAccount < public float $balance = 0; public function __construct(float $balance) < $this->balance = $balance; > > $account = new BankAccount(100); var_dump($account->balance); // 100
Code language: PHP (php)
If you unset a typed property, its status will change back to uninitialized. Note that for an untyped property, its value will become null after unset. For example:
class BankAccount < public float $balance = 0; public function __construct(float $balance) < $this->balance = $balance; > > $account = new BankAccount(0); var_dump($account->balance); // 0 unset($account->balance); var_dump($account->balance); // error
Code language: PHP (php)
Typed properties and strict types
In the following example, the constructor of the BankAccount expects a float . However, you can pass a string. In this case, PHP coerces the string to a float:
class BankAccount < public float $balance = 0; public function __construct(float $balance) < $this->balance = $balance; > > $account = new BankAccount("100.5"); var_dump($account->balance); // 100.5
Code language: PHP (php)
If you don’t want this behavior, you can disable it by declaring strict_types at the beginning of the file as follows:
declare(strict_types=1); class BankAccount < public float $balance = 0; public function __construct(float $balance) < $this->balance = $balance; > > $account = new BankAccount("100.25"); // error var_dump($account->balance);
Code language: PHP (php)
Fatal error: Uncaught TypeError: Argument 1 passed to BankAccount::__construct() must be of the type float, string given.
Code language: plaintext (plaintext)
Summary
- Typed properties include modifiers ( private , protected , and public ) and types (except void and callable ).
- Typed properties have uninitialized states, not null like untyped properties.