PHP7.4中的类型化属性的实例介绍

264 阅读2分钟

PHP在几个版本中都有类型提示和类型声明,但它们并不是在所有情况下都可用。在 PHP 5.x 中有基于类的方法参数类型提示,而 PHP 7.0 增加了 Scalar 类型声明。你可以在方法参数上设置一个类型,或者指定一个返回类型,但是在 PHP 7.4 中我们得到了一些非常有趣的东西。现在可以在一个类的属性上声明一个类型。

什么是类型?

从更广泛的角度来看,数据类型是一种契约,它确保一个特定的变量只能包含一种特定类型的数据。一个int 总是包含一个整数,一个string 总是一个字符串,而一个Ship 对象总是一个包含Mulder和Scully的类。

PHP在历史上是一种 "无类型 "语言,任何变量都可以包含任何类型的数据。从工程和安全的角度来看,这被证明是危险的。而且,要对每一个变量进行安全检查以确保它有正确的数据类型,这就变得很难看了。

在现代PHP中,如果不是所有的方法参数都应该声明一个类型,而方法应该有返回类型。有时你会遇到需要分支逻辑和某种 "混合 "返回的情况(有点像 Here Be Dragons 的情况)。PHP仍然给你提供了无类型编码的选择,或者说是 "老派"。

事实上,除非你明确声明启用 "严格 "类型,否则PHP会尽力帮你解决。如果你声明一个方法返回 "bool",而实际返回的是一个整数,PHP将默默地把这个整数转换成一个布尔值:0 将变成false ,而其他的都将变成true

严格的类型

想让你的代码更安全、更严格地设计吗?请看在你的PHP文件的顶部启用严格类型。

<?php
declare(strict_types=1);

这适用于标量类型(int, string, bool, 等等),并阻止 PHP 试图猜测你的意思。它禁用了隐式铸造,而支持抛出致命的TypeErrors。

编写单元测试来证明你的代码传递了正确的数据类型是很有帮助的--它可以避免你在生产中遇到这种TypeError

什么是类属性?

PHP 7.4的新类型属性功能允许在类实例变量上声明数据类型。如果你试图向这些变量写入任何不是正确数据类型的东西,PHP会抛出一个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
*/

但是如果你使用了正确的数据类型,你就可以正常工作了。

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
        )

)
*/

可置空类型

有时,即使你知道一个变量的类型,你也需要一种方法来表示这个变量是空的。PHP提供了Nullable语法糖来实现这一目的。

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
*/

如果在一个类型的属性被初始化之前试图访问它,即使该变量是Nullable,也会得到这个错误。

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

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