热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

详解PHP7.4的类型属性

PHP7.4中增加了类型化类属性,对php的类型系统进行了重大改进。这些更改完全是自愿加入的,不会破坏以前的版本。在本文中,我们将深入了解

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值仍不是整数值的情况下,PHP 也只是会在访问$bar时才会报错:

var_dump($foo->bar);Fatal error: Uncaught Error: Typed property Foo::$bar
must not be accessed before initialization

从错误消息中可以看出,出现了一种新的变量状态:未初始化 (uninitialized)

 

$bar 属性无论是否声明了类型,值都可以为 null。因此,无法确定类型属性是否设置。这就是增加变量「未初始化」状态的原因。

 

未初始化有四个方面需要注意:

 

  • 无法读取未初始化的属性,一旦这么做,将引发致命错误;
  • 由于在访问属性时会检查未初始化状态,所以即使是不可为空的对象也可以使用未初始化属性;
  • 在读取未初始化属性时候之前可以对其进行写入;
  • unset 操作会让类型属性变成未初始化状态,而非类型属性只会变成值为 null;

特别要注意在对象实例化之后设置未初始化的类型属性是合法的:

class Foo
{public int $a;
}$foo = new Foo;$foo->a = 1; // 合法
$foo->a = null; // 非法

虽然只会在读取属性值时检查未初始化状态,但在写入属性时会进行类型验证。这意味着任何无效的属性值都不会被设置。

 

默认值和构造函数

让我们仔细看看如何初始化类型属性值。对于标量类型,可以直接提供一个默认值

class Foo
{public int $bar = 4;public ?string $baz = null;// 错误写法 public string $baz = null;public array $list = [1, 2, 3];
}

类型属性不能显示设置为null,除非是可空类型。这看上去显而易见的,但是一些旧行为却允许这种操作

function passNull(int $i = null)
{ /* … */ }passNull(null);

幸运的是,类型属性不允许这种令人疑惑的行为。
还要注意,属性类型的默认值不可能为对象或者类,你应当使用构造器来设置这些值。

最明显的用来设置默认值的地方就是构造函数

class Foo
{private int $a;public function __construct(int $a){$this->a = $a;}
}

但也要记住我之前提到的内容:在构造函数之外写入未初始化 (uninitialized) 的属性是有效的。只要没有读取属性值的操作,编译器就不会执行未初始化的相关检查。

 

类型

那么到底哪些类型可以指定,又如何指定呢?我已经提到了指定属性类型只能在类中进行 (当前如此),并且它们需要一个访问修饰符或是属性前面的 var 关键字。

 

对于可用类型,几乎所有类型都可以使用,除了 void 和 callable 类型.

 

因为 void 意味着没有值,所以它不能用于指定一个值的类型也就说得过去了。然而 callback 就有一点细微不同了。

 

可见,PHP 中的 "callback" 可以这样写

$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)();

在此例中,$callback 引用了私有的 Bar::method,但是是在 Foo 的上下文中被调用的。基于这个问题,决定不添加 callback 类型的支持。

 

不过这并不是什么大问题,因为 Closure(闭包) 是一种有效类型,它会记住构建它的 $this 上下文。

 

顺带一说,以下是所有可用类型的列表:

 

  • bool
  • int
  • float
  • string
  • array
  • iterable
  • object
  • ? (nullable)
  • self & parent
  • Classes & interfaces

强制和严格类型

PHP,是我们既喜欢又反感的动态语言,它会尽可能地强制或转换类型。假设你在一个期望接受 int 的地方传入字符串,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'; // 1Fatal 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;
}

在运行代码之前,PHP 将在背后用它所引用的具体实现类来替换self。这意味着在此本例中将抛出相同的错误。解决此问题的唯一方法是执行以下操作:

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,则可能需要阅读 完整列表 中所做的更改和添加的功能。老实说,这是很长一段时间以来最好的发行版之一,值得您花时间!

 

更多学习内容请访问:

八重樱:怎么从一名码农成为架构师的必看知识点:目录大全(不定期更新)​zhuanlan.zhihu.com图标

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的官方群点击此处。


推荐阅读
  • 时域|波形_语音处理基于matlab GUI音频数据处理含Matlab源码 1734期
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了语音处理基于matlabGUI音频数据处理含Matlab源码1734期相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • C++中的三角函数计算及其应用
    本文介绍了C++中的三角函数的计算方法和应用,包括计算余弦、正弦、正切值以及反三角函数求对应的弧度制角度的示例代码。代码中使用了C++的数学库和命名空间,通过赋值和输出语句实现了三角函数的计算和结果显示。通过学习本文,读者可以了解到C++中三角函数的基本用法和应用场景。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 1简介本文结合数字信号处理课程和Matlab程序设计课程的相关知识,给出了基于Matlab的音乐播放器的总体设计方案,介绍了播放器主要模块的功能,设计与实现方法.我们将该设 ... [详细]
  • 工作经验谈之-让百度地图API调用数据库内容 及详解
    这段时间,所在项目中要用到的一个模块,就是让数据库中的内容在百度地图上展现出来,如经纬度。主要实现以下几点功能:1.读取数据库中的经纬度值在百度上标注出来。2.点击标注弹出对应信息。3 ... [详细]
  • 开发笔记:图像识别基于主成分分析算法实现人脸二维码识别
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了图像识别基于主成分分析算法实现人脸二维码识别相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 6(自)、交换机之关键字模式
    上一节中的我们的日志系统将所有消息广播给所有消费者,对此我们想做一些改变,例如我们希望将日志消息写入磁盘的程序仅接收严重错误(error),而不存储那些警告(warnning)或者 ... [详细]
  • 依赖注入_php 依赖注入容器
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了php依赖注入容器相关的知识,希望对你有一定的参考价值。原文: http://blog.csdn.net/r ... [详细]
  • 作者一直强调的一个概念叫做oneloopperthread,撇开多线程不谈,本篇博文将学习,怎么将传统的IO复用pollepoll封装到C++类中。1.IO复用复习使用p ... [详细]
  • 系列目录Guava1:概览Guava2:Basicutilities基本工具Guava3:集合CollectionsGuava4:GuavacacheGuava6:Concurre ... [详细]
author-avatar
Ocean哔哔芭比波哔
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有