whateverthing.com

Typed Properties in PHP 7.4

PHP has had type hints and type declarations for several versions, but they haven't really been available in all contexts. Class-based type hints on method parameters arrived in PHP 5.x, and PHP 7.0 added Scalar type declarations. You can set a type on a method parameter, or specify a return type, but in PHP 7.4 we got something really fun: Now you can declare a type on a class property.

What is a type?

Taking a wider view for a moment, a data type is a contract that ensures a specific variable will only ever contain a specific type of data. An int will always contain an integer number, a string will always be a string, and a Ship object will always be a class that contains Mulder and Scully.

PHP has historically been a "typeless" language, where any variable could contain any type of data. This has proven to be dangerous, from both engineering and security points of view. Also, it gets kind of ugly having to safety-check every variable to ensure it's got the right kind of data in it.

With modern PHP, most if not all method parameters should declare a type, and methods should have return types. Sometimes you get into a situation where you need branched logic and some kind of "mixed" return (kind of a Here Be Dragons scenario). PHP still gives you the option to code typelessly, or "old school".

In fact, unless you explicitly declare that "strict" types are enabled, PHP will try to help you out. If you declare that a method returns "bool", and what you actually return is an integer, PHP will silently cast the integer to a boolean: 0 will become false, and anything else will become true.

Strict Types

Want your code to be safer and more stringently engineered? Look at enabling strict types at the top of your PHP files:

<?php
declare(strict_types=1);

This works for scalar types (int, string, bool, etc) and stops PHP from trying to guess what you meant. It disables implicit casting in favour of throwing fatal TypeErrors.

Writing unit tests to prove that your code is passing around the correct data types is helpful - it saves you from hitting such TypeErrors in production.

What is a class property?

PHP 7.4's new Typed Properties feature allows you to declare data types on class instance variables. If you try to write anything to those variables that isn't the proper data type, PHP will throw a TypeError.

class Ship {
    public Character $mulder;
    public Character $scully;
}

$badData = 'esther-nairn';
$ship = new Ship;
$ship->scully = $badData; // TypeError!

/*
Fatal error: Uncaught TypeError: Typed property Ship::$scully must be an instance of Character, string used in test.php:10
*/

But if you do use the correct data types, you're good to go.

class Ship {
    public Character $mulder;
    public Character $scully;
}

class Character {
    public string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }
}

$ship = new Ship;

$goodData = new Character('Frohike');
$ship->mulder = $goodData; // No errors!

$scully = new Character('Scully');
$ship->scully = $scully; // Still no errors!

print_r($ship);

/*
Ship Object
(
    [mulder] => Character Object
        (
            [name] => Frohike
        )

    [scully] => Character Object
        (
            [name] => Scully
        )

)
*/

Nullable Types

Sometimes, even when you know the type of a variable, you need a way of indicating that the variable is empty. PHP offers the Nullable syntactic sugar to make that happen:

class Ship {
    public ?Character $mulder;
    public Character $scully;
}

class Character {
    public string $name;

    public function __contruct(string $name) {
        $this->name = $name;
    }
}

$ship = new Ship;
$ship->mulder = new Character('Langly');
$ship->scully = new Character('Karen Hamby');

$ship->mulder = null; // No Errors
$ship->scully = null; // TypeError!

/*
Fatal error: Uncaught TypeError: Typed property Ship::$scully must be an instance of Character, null used in test.php:22
*/

If you try to access a typed property before it has been initialized, you will get this error, even if the variable is Nullable:

$ship = new Ship;
print_r($ship->mulder);

/*
Fatal error: Uncaught Error: Typed property Ship::$mulder must not be accessed before initialization
*/

That's All For Now

Thanks for reading. I hope this info will help you on your PHP learning journey. Please don't hesitate to ask me questions on Twitter or by email :)

Published: May 16, 2020

Categories: coding

Tags: dev, development, coding, php, php-tips