PHP 7 入门:类和接口的增强

2020 年 7 月 28 日

PHP 7 入门:类和接口的增强

本文要点


  • PHP 7为一次性对象添加了匿名类的功能,这样的例子可能是值对象以及实现了一个接口用来进行依赖注入的对象。

  • 按照设计,匿名类是一次性使用的,不需要完整的类定义。

  • 匿名类就像完整的类那样,可以扩展其他类、实现接口、定义构造器等等。

  • PHP 7引入了IntlChar类来访问Unicode字符的信息。

  • PHP 7废弃了一些特性,比如PHP 4风格的构造器。


在这个文章系列中,我们将会探索 PHP 7 的新特性。在第一篇文章中,我们准备好了环境并介绍了 PHP 7,随后讨论了其与面向对象编程相关的新特性。在本文中,我们会讨论 PHP 在类和接口方面的改进。


匿名类


有时候,短期使用、用后即可废弃的对象可以取代完整的类实例。


PHP 7.0 添加了对匿名类的支持,它们非常易于实例化,即便只使用一次。匿名类和完整类很相似,它们能够扩展其他的类、实现接口、定义构造器等。


作为样例,我们会创建一个匿名类来为服务器日志处理日志消息。创建一个_anonymous.php_脚本并定义包含setMsg(string $msg)函数的LogMsg接口,该接口允许我们设置日志消息。另外,创建一个带有 getter/setter 方法getLogMsg(): LogMsgsetLogMsg(LogMsg $logMsg)的 ServerLog 类,这个类用来设置服务器日志。


<?phpinterface LogMsg {  public function setMsg(string $msg);}class ServerLog {  private $logMsg;  public function getLogMsg(): LogMsg {       return $this->logMsg;  }  public function setLogMsg(LogMsg $logMsg) {         $this->logMsg = $logMsg;  }}$serverLog = new ServerLog;$serverLog->setLogMsg(new class implements LogMsg {  public function setMsg(string $msg) {      echo $msg;  }});var_dump($serverLog->getLogMsg());?>

复制代码


创建ServerLog类的实例并调用setLogMsg(LogMsg $logMsg)函数,其中参数是以匿名类的形式提供的。


$serverLog = new ServerLog;$serverLog->setLogMsg(new class implements LogMsg {  public function setMsg(string $msg) {      echo $msg;  }});

复制代码


如果运行脚本的话,var_dump将会打印出我们传入到SetLogMsg中的匿名类对象的引用。


object(class@anonymous)#2 (0) { }


如果在上面的样例中不使用匿名类的话,我们需要提供一个完整的实现了LogMsg接口的类。如果不使用匿名类,相同功能的代码如下所示。


<?phpinterface LogMsg {  public function setMsg(string $msg);}class ServerLogMsg implements LogMsg {  public function setMsg(string $msg) {      echo $msg;  }}class ServerLog {  private $logMsg;  public function getLogMsg(): LogMsg {       return $this->logMsg;  }  public function setLogMsg(LogMsg $logMsg) {         $this->logMsg = $logMsg;  }}$serverLog = new ServerLog;$serverLog->setLogMsg(new ServerLogMsg());var_dump($serverLog->getLogMsg());?>

复制代码


从同一个匿名类声明实例化的所有对象都是该类的实例并且彼此是完全相同的。相同比较是使用===操作符执行的,它表明要对比的对象相等并且具有相同的类型。使用===进行身份对比(identity comparison)可能一开始会让人觉得疑惑。那我们从相等操作符==开始。如果两个对象具有相同的属性和值并且是同一个类的实例,那么它们是相等的(使用==操作符进行对比)。而身份对比操作符(===)判定两个对象相同,仅在它们引用了同一个类的同一个实例时才能成立。


为了充分理解这一点,我们创建一个_anonymous-class-objects.php_脚本并定义一个返回匿名类对象的函数。现在,我们借助get_class函数,调用这个函数两次,获取所实例化的两个不同的匿名对象,然后得到它们的类名并进行比较。如下所示,两个名字是相同的。


<?phpfunction a_class(){  return new class {};} if(get_class(a_class())===get_class(a_class())){echo "Objects are instances of same class ".get_class(a_class());}else{echo "Objects are instances of different classes";}echo "</br>";var_dump(get_class(a_class()));echo "</br>";var_dump(get_class(a_class()));?>

复制代码


运行_anonymous-class-objects.php_将会生成一条输出消息,表明对象是同一个类的实例,它的名字以class@anonymous开头。这里返回了相同的实例class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031,这表明是两个相同的类。匿名类的名字是由 PHP 引擎分配的,依赖于实现。


Objects are instances of same class class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031string(98) "class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031"string(98) "class@anonymousC:\PHP7.4\php-7.4-ts-windows-vc15-x64-r6c9821a\scripts\sumints.php000000000626B031"
复制代码


匿名类也可以使用 extends 扩展其他的类。


为了阐述这一点,创建一个_anonymous-extend-class.php_脚本,定义带有$msg字段及其 get/set 函数的LogMsg类。现在,定义带有getLogMsg(): LogMsgsetLogMsg(LogMsg $logMsg)函数的ServerLog类。最后,创建ServerLog类的实例并调用setLogMsg(LogMsg $logMsg)函数,将扩展了 LogMsg 的一个匿名类提供给LogMsg参数:


$serverLog = new ServerLog;$serverLog->setLogMsg(new class extends LogMsg {  public function setMsg(string $msg) {        $this->msg = $msg;  }});
复制代码


_anonymous-extend-class.php_脚本如下所示:


<?phpclass LogMsg {private $msg;  public function getMsg() {        return  $msg;  }}class ServerLog {  private $logMsg;  public function getLogMsg(): LogMsg {       return $this->logMsg;  }  public function setLogMsg(LogMsg $logMsg) {         $this->logMsg = $logMsg;  }}$serverLog = new ServerLog;$serverLog->setLogMsg(new class extends LogMsg {  public function setMsg(string $msg) {        $this->msg = $msg;  }});var_dump($serverLog->getLogMsg());?>
复制代码


运行脚本并检查输出。我们会看到LogMsg类型的msg字段被设置成了NULL


object(class@anonymous)#2 (1) { ["msg":"LogMsg":private]=> NULL }

复制代码


正如我们所预期的,匿名类的构造器可以传入参数。


为了阐述该功能,创建_anonymous-extend-class-add-constructor.php_脚本,并定义了像前面样例那样的LogMsgServerLog类。唯一的差异在于有个参数传递到了匿名类的构造器之中:


$serverLog->setLogMsg(new class('Log Message') extends LogMsg {  public function __construct($msg)  {        $this->msg = $msg;  }}
复制代码


_anonymous-extend-class-add-constructor.php_脚本如下所示。


<?phpclass LogMsg {private $msg;  public function getMsg() {        return  $msg;  }}class ServerLog {  private $logMsg;  public function getLogMsg(): LogMsg {       return $this->logMsg;  }  public function setLogMsg(LogMsg $logMsg) {         $this->logMsg = $logMsg;  }}$serverLog = new ServerLog;$serverLog->setLogMsg(new class('Log Message') extends LogMsg {  public function __construct($msg)  {        $this->msg = $msg;  }  public function setMsg(string $msg) {        $this->msg = $msg;  }});var_dump($serverLog->getLogMsg());?>
复制代码


运行脚本并校验传递给传递给匿名类构造器的日志消息,它会被getLogMsg()返回并打印出来。


object(class@anonymous)#2 (2) { ["msg":"LogMsg":private]=> NULL ["msg"]=> string(11) "Log Message" }
复制代码


匿名类可能会被其他类嵌套,但是它不能使用外部类的 protected 或 private 函数或属性。要想使用外部类的 private 属性,我们要像上面的例子那样将属性作为参数传递到匿名类的构造器中。


为了阐述该功能,创建一个_inner-class-private.php_脚本并定义一个外部类Outer,它有一个私有属性。添加一个inner()函数,该函数返回一个匿名类对象。来自Outer类的私有属性传递到了匿名类构造器中,并设置为匿名类的 private 属性。现在,使用在匿名类中定义的函数,我们就能返回从Outer类传递到匿名内部类的 private 属性的值:


return new class($this->a) extends Outer {             private $a;            public function __construct($a)          {                $this->a = $a;          }            public function getFromOuter()          {                echo $this->a;          }      };
复制代码


要打印由Outer传递给匿名内部类的 private 属性的值,我们需要创建一个Outer类的实例并调用 inner()函数,该函数会创建匿名类,然后调用匿名类中返回 private 属性值的函数:


echo (new Outer)->inner()->getFromOuter();
复制代码


inner-class-private.php 脚本如下所示。


<?phpclass Outer{  private $a = 1;  public function inner()  {        return new class($this->a) {             private $a;            public function __construct($a)          {                $this->a = $a;          }                      public function getFromOuter()          {              echo $this->a;            }        };  }}echo (new Outer)->inner()->getFromOuter();?>
复制代码


运行脚本,检查私有属性的值(1)从Outer传递到了内部类中并打印到了浏览器上。


接下来,我们要阐述Outer类的 protected 函数如何在匿名类中进行调用。为了调用外部类中定义的 protected 函数,匿名内部类需要扩展这个外部类。


为了阐述该功能,创建一个_inner-class-protected.php_脚本并定义名为Outer的外部类,该类包含一个 protected 字段和 protected 函数。现在,定义另外一个函数,该函数会创建一个扩展 Outer 类的匿名类,并在匿名类中定义一个函数,让该函数调用我们最早定义的外部类的 protected 函数。因为匿名类扩展了 Outer 类,所以它继承了 Outer 类的 protected 字段和函数,这就意味着可以使用this访问 protected 的函数和字段。和前面一样,匿名类的函数可以通过首先创建Outer类的实例来进行调用:


echo (new Outer)->inner()->getFromOuter();
复制代码


_inner-class-protected.php_脚本如下所示:


<?phpclass Outer{  protected $a = 1;  protected function getValue()  {      return 2;  }  public function inner()  {      return new class extends Outer {            public function getFromOuter()          {                echo $this->a;                echo "<br/>";                echo $this->getValue();          }      };  }}echo (new Outer)->inner()->getFromOuter();?>
复制代码


运行脚本并检查Outer类的 protected 字段以及Outer类的函数所返回的值,如下面的输出所示。


1


2


我们使用了两个样例。分别阐述了如何从嵌入式的匿名类中调用外部类的 private 字段以及如何调用 protected 的字段与函数。我们可以将这两个样例合并到一起,让匿名的嵌套类扩展外部类,便于继承外部类 protected 的字段和函数,同时传递外部类的 private 字段到匿名类的构造器中,如下所示:


return new class($this->prop) extends Outer {}
复制代码


为了阐述该功能,我们创建_inner-class.php_脚本。


<?phpclass Outer{  private $prop = 1;  protected $prop2 = 2;  protected function func1()  {      return 3;  }  public function func2()  {      return new class($this->prop) extends Outer {            private $prop3;            public function __construct($prop)          {                $this->prop3 = $prop;          }            public function func3()          {                return $this->prop2 + $this->prop3 + $this->func1();          }      };  }}echo (new Outer)->func2()->func3();?>

复制代码


运行脚本将会输出 6,这是通过调用外部类的字段和函数实现的。


用于 Unicode 字符的新 IntlChar 类


PHP 7.0 引入了名为IntlChar的新类,它提供了多个工具方法用来访问 Unicode 字符的信息。注意,要使用IntlChar类,需要安装Intl扩展,这可以通过在php.ini配置文件中解除对如下代码行的注释来实现:


extension=intl


IntlChar 类中的一些方法如下表所示。


IntlChar 类的方法


方法描述
IntlChar::charFromName根据名称返回Unicode字符的代码点(code point)的值。
IntlChar::charName返回unicode字符的名称.
IntlChar::charType返回unicode代码点的通用类别的值。例如,对于标题大小写字符种类,会返回IntlChar::CHAR_CATEGORY_TITLECASE_LETTER。对于十进制数字种类,返回IntlChar::CHAR_CATEGORY_DECIMAL_DIGIT_NUMBER。如果字符不在任何预定义的类别中的话,那么返回的种类将是IntlChar::CHAR_CATEGORY_UNASSIGNED
IntlChar::chr根据代码点的值返回Unicode字符。
IntlChar::getNumericValue返回unicode代码点的数字值。
IntlChar::isdefined返回boolean值以表明某个字符是否已定义。


我们现在创建一个_Intlchar.php_脚本来测试其中的一些方法。在如下的样例中,我们通过 IntlChar::UNICODE_VERSION常量输出 unicode 的版本,探查LATIN CAPITAL LETTER B的 unicode 代码点,并检查\u{00C6}是否已定义。脚本_Intlchar.php_如下所示。


<?phpprintf('Unicode Version : ');echo "<br/>";echo IntlChar::UNICODE_VERSION;echo "<br/>";echo IntlChar::charFromName("LATIN CAPITAL LETTER B");echo "<br/>";var_dump(IntlChar::isdefined("\u{00C6}")); ?>
复制代码


运行脚本将会产生如下的输出:


Unicode Version :12.166bool(true)
复制代码


废弃的特性


PHP 7 还废弃了一些特性。


在 PHP 7.0.x 废弃的属性中,包括 PHP 4、“老式”风格的构造器,也就是构造器方法和类的名字是相同的。


举例来讲,创建 constructor.php 脚本并复制如下的代码清单到文件中。


<?phpclass Catalog {  function Catalog() {  }}?>
复制代码


脚本声明了一个Catalog类,并且带有一个名称同样为Catalog的方法。运行脚本将会看到如下的输出:


**Deprecated**: Methods with the same name as their class will not be constructors in a future version of PHP; Catalog has a deprecated constructor


除此之外,在 PHP 7.0.0 中,以静态方式调用非静态方法也被废弃了。我们创建一个 static.php 脚本并复制如下的代码清单到文件中,我们声明了一个带有非静态函数getTitle()的类,现在尝试对这个函数进行静态调用:


<?phpclass Catalog {  function getTitle() {  }}Catalog::getTitle();?>
复制代码


运行脚本将会看到输出如下的消息:


**Deprecated**: Non-static method Catalog::getTitle() should not be called statically


在 PHP 7.1.x 中,mcrypt扩展被废弃了。PHP 7.2 废弃的特性包括unquoted strings__autoload()方法、create_function()、强制转换为unset、不带第二个参数使用parse_str()gmp_random()函数、each()函数、带有字符串参数的assert()以及read_exif_data()函数。PHP 7.3 废弃的特性包括大小写不敏感的常量以及在命名空间中声明assert()


作为阐述废弃大小写不敏感常量的样例,运行如下的样例,其中define()在调用的时候,将case_insensitive参数设置成了 true:


<?phpdefine('CONST_1', 10, true); var_dump(CONST_1); var_dump(const_1);?>
复制代码


这将会输出如下的消息:


Deprecated: define(): Declaration of case-insensitive constants is deprecated on line 2


int(10)


Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "CONST_1" on line 4


小结


在关于 PHP 7 系列的第二篇文章中,我们探讨了类和接口方面的新特性。最值得关注的新特性就是支持匿名类。通过一个新的类 IntlChar, Unicode 也得到了提升,该方法可以用来获取关于 Unicode 字符的信息。


在下一篇文章中,我们将会探讨 PHP 类型系统方面的新特性。


作者介绍:


Deepak Vohra 是一位 Sun 认证的 Java 程序员和 Sun 认证的 Web 组件开发人员。Deepak 在 WebLogic Developer’s Journal、XML Journal、ONJava、java.net、IBM developerWorks、Java Developer’s Journal、Oracle Magazine 和 devx 上都发表过 Java 和 Java EE 相关的技术文章。Deepak 还出版过五本关于 Docker 的书,他是 Docker 导师。Deepak 还发表了多篇关于 PHP 的文章,以及一本面向 PHP 和 Java 开发人员的 Ruby on Rails 图书。


原文链接:


Classes and Interfaces Improvements


相关阅读:


PHP 7 入门:新特性简介


2020 年 7 月 28 日 09:001258

评论 1 条评论

发布
用户头像
没有编辑审核么,人家原文的缩进有这里这么恶心吗
2020 年 07 月 31 日 14:04
回复
没有更多评论了
发现更多内容

毫无意义的人生唯有编织图案

xyz

幂等问题及解决方案

Joker

幂等 解决方案

Nginx 入门及命令行操作

子杨

nginx 运维

基于 Markdown 的中文文档排版规范

Murphy

markdown 排版规范 GitHub GFM 物联网学前班

Cassandra可调一致性的使用及原理

老任物联网杂谈

大数据 分布式 Cassandra 可调一致性

大厂为什么不招30岁以上程序员,看这篇就够了

金刚小书童

职业规划 技术管理 程序员成长 程序员次第 职业成长

2020智源-京东多模态对话挑战赛开战 产学研联合推动AI技术发展

DT极客

Mysql常用删除方式比较

云也退

MySQL

我们可能都误解了什么是情商

七镜花园-董一凡

情绪

k8s 上运行我们的 springboot 服务之——大文件读写

柠檬

Java nio

广告的发展历程

子悠

广告 计算广告 广告系统 互联网广告 RTB

终于找到了一篇文章!通俗地讲解计算机工作原理

图灵社区

cpu 存储器 编译器 计算机工作原理

工厂模式(三)泛型工厂的概念以及示例代码

LSJ

备案问题汇总

云也退

网站 备案

平台化服务的基石:权限模型设计

孤岛旭日

企业架构 用户权限 数据建模

阿里巴巴为什么让初始化集合时必须指定大小?

王磊

Java 性能

图片与标题的Ken Burns动效

寇云

CSS css3

JUC整理笔记四之梳理VarHandle(上)

JFound

Java

贴吧电纸书资深用户,从7个方面详谈BOOX Poke2上手体验!

DT极客

ARTS_20200529

凌轩

Java ARTS 打卡计划

一位测试工程师的自我介绍

姬翔

测试

为什么你要学习 Go?

司徒公子

go golang 编程语言 谷歌Google

谈谈控制感(11):这样提升控制感,谁都能做到

史方远

心理 成长

Spring Bean生命周期——初始化和销毁

xiaoxi666

Java spring

Django ListView DetailView等基于类的视图如何添加装饰器?

Young先生

Python django LiveView 装饰器

只需CSS的下拉式导航菜单

寇云

CSS css3

奈学干货分享:分布式CAP实践分析

奈学教育

分布式

Nginx 基础原理和命令行的真相

子杨

nginx 运维

Spring源码-BeanFactory创建Bean

云淡风轻

spring 源码

Vol.10 Java 25岁了!

Lanpeng20

Java jdk 编程语言 Java25周年

产业区块链:产业是本质,区块链是工具

CECBC区块链专委会

新基建 CECBC 区块链技术 中国电子

PHP 7 入门:类和接口的增强-InfoQ