php没有属性的类型,PHP 7.4中的类型化属性

类型化类属性已在PHP 7.4中添加,并为PHP的类型系统提供了重大改进。这些更改是完全可选的,并且不破坏以前的版本。

在本文中,我们将深入研究该功能,但首先让我们首先总结最重要的要点:

它们自PHP 7.4起可用,该版本于2019年11月发布。

他们只在类 上使用,并且需要访问修饰符:public,protected或private; 或var

允许所有类型,除了void和callable

他们的实际情况是这样的:

class Foo

{

public int $a;

public ?string $b = 'foo';

private Foo $prop;

protected static string $static = 'default';

}

如果不确定类型带来的好处,建议您先阅读这篇文章。

未初始化

在看有趣的东西之前,有一个关于类型化属性的重要方面,这是必须首先讨论的。

尽管您可能会乍一看,但以下代码仍然有效:

class Foo

{

public int $bar;

}

$foo = new Foo;

即使$bar的值在创建 Foo 对象后不是整数,PHP 仅在访问$bar时才会引发错误:

var_dump($foo->bar);

Fatal error: Uncaught Error: Typed property Foo::$bar must not be accessed before initialization

从错误消息中可以看到,有一种新的“变量状态”:未初始化。

如果$bar没有类型,则其值将为null。但是类型可以为空,因此无法确定是否设置了类型为空的属性,或者只是将其忘记了。这就是为什么添加了“未初始化”的原因。

关于未初始化,要记住四件事:

您无法从未初始化的属性读取,否则将导致致命错误。

由于访问属性时会检查未初始化状态,因此即使其类型为不可为空,您也可以使用未初始化属性创建对象。

您可以先写入未初始化的属性,然后再读取它。

unset在类型化的属性上使用会使它未初始化,而对未类型化的属性进行unset则会使它初始化null。

特别要注意的是,以下代码在构造对象之后设置了未初始化的,不可为空的属性,这是有效的

class Foo

{

public int $a;

}

$foo = new Foo;

$foo->a = 1;

尽管仅在读取属性值时才检查未初始化状态,但在写入属性时会进行类型验证。这意味着您可以确保没有任何无效类型最终会成为属性的值.

默认值和构造函数

让我们仔细看看如何初始化键入的值。对于标量类型,可以提供一个默认值:

class Foo

{

public int $bar = 4;

public ?string $baz = null;

public array $list = [1, 2, 3];

}

请注意,仅当类型实际为可为空时,才能将 null 用作默认值。这似乎是显而易见的,但允许以下情况的参数默认值存在一些遗留行为:

function passNull(int $i = null)

{ /* … */ }

passNull(null);

幸运的是,类型属性不允许这种令人困惑的行为。

另请注意,不可能有object或class类型的默认值。应使用构造函数设置其默认值。

初始化类型化值的明显地方当然是构造函数:

class Foo

{

private int $a;

public function __construct(int $a)

{

$this->a = $a;

}

}

但也请记住我之前提到的内容:在构造函数之外写入未初始化的属性是有效的。只要没有任何内容从属性中读取,就不会执行未初始化的检查。

类型的类型

那么究竟可以类型化什么以及如何类型化?我已经提到过,类型化属性仅在类中起作用(目前),并且它们需要访问修饰符或var它们前面的关键字

从可用类型开始,几乎可以使用除void和callable之外的所有类型。

因为 void 表示缺少值,因此不能用于类型化。然而,callable是有点细微差别。

PHP中的“ callable” 可以这样写:

$callable = [$this, 'method'];

假设您有以下(无效)代码:

class Foo

{

public callable $callable;

public function __construct(callable $callable)

{ /* … */ }

}

class Bar

{

public Foo $foo;

public function __construct()

{

$this->foo = new Foo([$this, 'method'])

}

private function method()

{ /* … */ }

}

$bar = new Bar;

($bar->foo->callable)();

在此例中, $callable 指的是  private Bar::method, 但是它在 Foo环境中被调用. 因为这个问题, 它决定不添加 callable 支持.

这没什么大不了的 , 因为 Closure 是一个有效类型,   它将记住从哪里构造它的$this上下文。

顺便说一句,这是所有可用类型的列表:

bool

int

float

string

array

iterable

object

? (nullable)

self & parent

Classes & interfaces

强制和严格类型

PHP是我们喜欢和讨厌的一种动态语言,它将尽可能地强制转换类型。假设您在期望整数的地方传递了一个字符串,PHP将尝试自动转换该字符串:

function coerce(int $i)

{ /* … */ }

coerce('1'); // 1

相同的原则适用于类型化属性。以下代码有效,并将转换'1'为1。

class Bar

{

public int $i;

}

$bar = new Bar;

$bar->i = '1'; // 1

如果您不喜欢这种行为,可以通过声明严格类型来禁用它:

declare(strict_types=1);

$bar = new Bar;

$bar->i = '1'; // 1

Fatal error: Uncaught TypeError:

Typed property Bar::$i must be int, string used

类型差异和继承

即使PHP 7.4引入了改进的类型差异,但类型化属性仍然不变。这意味着以下无效

class A {}

class B extends A {}

class Foo

{

public A $prop;

}

class Bar extends Foo

{

public B $prop;

}

Fatal error: Type of Bar::$prop must be A (as in class Foo)

如果上面的示例似乎并不有意义,则应查看以下内容:

class Foo

{

public self $prop;

}

class Bar extends Foo

{

public self $prop;

}

self在运行代码之前,PHP将用它所引用的具体类在后台替换它。这意味着在此示例中将引发相同的错误。处理此问题的唯一方法是执行以下操作:

class Foo

{

public Foo $prop;

}

class Bar extends Foo

{

public Foo $prop;

}

说到继承,您可能会发现很难提出任何好的用例来覆盖继承属性的类型。

虽然我同意这种看法,但值得注意的是,可以更改继承属性的类型,但前提是访问修饰符也从 private 到 protected 或 public.。

以下代码有效:

class Foo

{

private int $prop;

}

class Bar extends Foo

{

public string $prop;

}

但是,不允许将类型从可为空的类型更改为不可为空或反之也是。

class Foo

{

public int $a;

public ?int $b;

}

class Bar extends Foo

{

public ?int $a;

public int $b;

}

Fatal error: Type of Bar::$a must be int (as in class Foo)

还有更多!

就像本文开头所说的那样,类型化属性是PHP的主要补充。关于它们还有很多要说的。我建议您阅读RFC,以了解所有整洁的小细节。

如果您不是PHP 7.4的新手,您可能想阅读所做的更改和添加的功能的完整列表。老实说,这是很长一段时间以来最好的发行版之一,值得您花时间!

来自https://stitcher.io/blog/typed-properties-in-php-74

PHP 7.4中的类型化属性

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值