Menu

PHP 接口 interface:implements、常量和示例

PHP 接口列出一个类承诺要有的方法:interface Shape { public function area(): float; },之后 class Circle implements Shape 就必须定义 area()。学习用接口做类型声明、实现多个接口、接口常量、接口继承接口、Countable 等内置接口,以及接口与抽象类的区别。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

PHP 接口声明了一个类承诺提供的方法,但不包含任何代码:interface Shape { public function area(): float; }。写了 implements Shape 的类必须定义 area(),任何以 Shape 做类型声明的函数都会接受这个类。

describe() 不知道也不关心它收到的是哪个类。它只依赖接口做出的承诺,所以新写一个 Triangle implements Shape,不用改一行代码就能和它一起使用。

面向接口编程

编写接口最常见的原因,是让代码依赖一种能力,而不是某个具体的类。下面的订单服务接受任何 Notifier,因此同一段代码在生产环境中可以发送邮件,在测试中则可以把消息收集起来:

实现多个接口

一个类最多继承一个父类,但可以按需实现任意多个接口。把它们写在 implements 后面,用逗号分隔,并定义所有接口的方法:

JsonSerializable 是 PHP 的内置接口之一:实现它就能控制 json_encode() 为该对象生成什么内容。

接口常量

接口可以声明常量。每个实现它的类都会继承这些常量,你可以通过接口名或类名来读取:

在 PHP 8.3 中,接口不能有属性,只能有方法和常量。方法可以是静态的:在接口中写 public static function fromArray(array $data): static;,每个类就必须定义这个静态方法。对于一组固定的相关值,枚举 通常比一串接口常量更合适。

接口可以继承其他接口

接口也使用 extends,而且和类不同,它可以同时继承多个接口。实现子接口的类必须定义整条继承链上的所有方法:

内置接口:Countable、IteratorAggregate、Stringable

PHP 自带一些接口,让对象能接入语言本身的功能。Countable 让 count($obj) 可用,IteratorAggregate 让 foreach 可用,而任何带有 __toString() 方法的类都会自动实现 Stringable:

漏掉一个方法

如果一个类声称实现了某个接口,却漏掉了其中一个方法,PHP 会以致命错误停止运行,并指出缺少的方法:

加上 total() 方法后,脚本就能运行了。抽象类 可以不定义接口方法,这正是错误信息中提示的另一条出路。

接口与抽象类

两者都定义了一个类必须拥有的方法,也都可以用作类型声明。区别在于它们还能包含什么,以及一个类能使用几个:

接口抽象类
方法体无(只有签名)抽象方法和已实现的方法
属性不能可以
构造函数可以声明,但很少有用可以,并带有代码
方法的可见性只能是 publicpublic、protected、private
常量可以可以
每个类能用几个任意多个(implements A, B, C)一个(extends Base)
典型用途一种能力:Exportable、Countable一组类共享的代码:Model、Controller

两者可以很好地结合:定义接口用于类型声明,再提供一个实现了该接口、包含公共代码的抽象基类。需要不同基类的类仍然可以直接实现这个接口。

常见问题

PHP 中的接口是什么?

一组没有方法体的 public 方法签名:interface Logger { public function log(string $msg): void; }。写了 implements Logger 的类必须定义其中的每个方法,任何以 Logger 做类型声明的代码都会接受这个类。

一个 PHP 类可以实现多个接口吗?

可以。把它们写在 implements 后面,用逗号分隔:class File implements Readable, Writable, Countable。这个类必须定义所有这些接口的方法。其他语言用多重继承做的事,PHP 就是这样实现的。

PHP 中接口和抽象类有什么区别?

接口只声明 public 方法(和常量),没有属性,也没有方法代码,一个类可以实现多个接口。抽象类除了抽象方法外,还可以有属性、构造函数和已实现的方法,但一个类只能继承一个。表示一种能力(“可以导出”)时用接口,为一组相关的类共享代码时用抽象类。

PHP 接口可以有属性吗?

PHP 8.3 中不可以:接口可以包含方法签名和常量(const VERSION = 2;),但不能有属性。接口中的属性声明(属性钩子)直到 PHP 8.4 才出现。

PHP 中怎么检查一个对象是否实现了某个接口?

使用 instanceof:只要对象的类直接或通过父类实现了该接口,$obj instanceof Countable 就为 true。class_implements($obj) 会返回这个类实现的所有接口的名称。

Coddy 编程语言插图

和 Coddy 一起学编程

开始