对象接口

对象接口允许您创建代码,该代码指定一个类必须实现哪些方法,而无需定义这些方法是如何实现的。接口与类和特征共享命名空间,因此它们不能使用相同的名称。

接口的定义方式与类相同,但使用 interface 关键字替换 class 关键字,并且没有任何方法定义其内容。

在接口中声明的所有方法都必须是公有的;这是接口的本质。

在实践中,接口服务于两个互补的目的

  • 允许开发人员创建不同类的对象,这些对象可以互换使用,因为它们实现了相同的接口或接口。一个常见的例子是多个数据库访问服务、多个支付网关或不同的缓存策略。不同的实现可以在无需对使用它们的代码进行任何更改的情况下进行互换。
  • 允许函数或方法接受并对符合接口的参数进行操作,而不关心对象可能执行的其他操作或其实现方式。这些接口通常命名为 IterableCacheableRenderable 等,以描述行为的意义。

接口可以定义 魔术方法 以要求实现类实现这些方法。

注意:

虽然它们受支持,但强烈建议不要在接口中包含 构造函数。这样做会显着降低实现接口的对象的灵活性。此外,构造函数不受继承规则的约束,这会导致不一致且意外的行为。

实现

为了实现一个接口,使用 implements 运算符。类中必须实现接口中的所有方法;否则会导致致命错误。如果需要,类可以通过逗号分隔每个接口来实现多个接口。

警告

实现接口的类可以使用与接口不同的参数名称。但是,从 PHP 8.0 开始,语言支持 命名参数,这意味着调用者可以依赖接口中的参数名称。因此,强烈建议开发人员使用与所实现接口相同的参数名称。

注意:

接口可以使用 extends 运算符像类一样扩展。

注意:

实现接口的类必须使用 兼容签名 声明接口中的所有方法。一个类可以实现多个接口,这些接口声明了同名方法。在这种情况下,实现必须遵循所有接口的 签名兼容性规则。因此,可以应用 协变和逆变

常量

接口可以包含常量。接口常量的工作方式与 类常量 完全相同。在 PHP 8.1.0 之前,它们不能被继承它们的类/接口覆盖。

示例

示例 #1 接口示例

<?php

// 声明接口 'Template'
interface Template
{
public function
setVariable($name, $var);
public function
getHtml($template);
}

// 实现接口
// 这将起作用
class WorkingTemplate implements Template
{
private
$vars = [];

public function
setVariable($name, $var)
{
$this->vars[$name] = $var;
}

public function
getHtml($template)
{
foreach(
$this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}

return
$template;
}
}

// 这将不起作用
// 致命错误:类 BadTemplate 包含 1 个抽象方法
// 因此必须声明为抽象类 (Template::getHtml)
class BadTemplate implements Template
{
private
$vars = [];

public function
setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
?>

示例 #2 可扩展接口

<?php
interface A
{
public function
foo();
}

interface
B extends A
{
public function
baz(Baz $baz);
}

// 这将起作用
class C implements B
{
public function
foo()
{
}

public function
baz(Baz $baz)
{
}
}

// 这将不起作用,并导致致命错误
class D implements B
{
public function
foo()
{
}

public function
baz(Foo $foo)
{
}
}
?>

示例 #3 多个接口的变异兼容性

<?php
class Foo {}
class
Bar extends Foo {}

interface
A {
public function
myfunc(Foo $arg): Foo;
}

interface
B {
public function
myfunc(Bar $arg): Bar;
}

class
MyClass implements A, B
{
public function
myfunc(Foo $arg): Bar
{
return new
Bar();
}
}
?>

示例 #4 多个接口继承

<?php
interface A
{
public function
foo();
}

interface
B
{
public function
bar();
}

interface
C extends A, B
{
public function
baz();
}

class
D implements C
{
public function
foo()
{
}

public function
bar()
{
}

public function
baz()
{
}
}
?>

示例 #5 带有常量的接口

<?php
interface A
{
const
B = 'Interface constant';
}

// 输出:Interface constant
echo A::B;


class
B implements A
{
const
B = 'Class constant';
}

// 输出:Class constant
// 在 PHP 8.1.0 之前,这将无法正常工作,因为不允许重写常量。
echo B::B;
?>

示例 #6 带有抽象类的接口

<?php
interface A
{
public function
foo(string $s): string;

public function
bar(int $i): int;
}

// 抽象类可以只实现接口的一部分。
// 扩展抽象类的类必须实现剩余的部分。
abstract class B implements A
{
public function
foo(string $s): string
{
return
$s . PHP_EOL;
}
}

class
C extends B
{
public function
bar(int $i): int
{
return
$i * 2;
}
}
?>

示例 #7 同时扩展和实现

<?php

class One
{
/* ... */
}

interface
Usable
{
/* ... */
}

interface
Updatable
{
/* ... */
}

// 这里关键字的顺序很重要。'extends' 必须放在前面。
class Two extends One implements Usable, Updatable
{
/* ... */
}
?>

接口结合类型声明,提供了一种很好的方式来确保特定对象包含特定的方法。请参阅 instanceof 运算符和 类型声明

添加注释

用户贡献的注释 4 notes

thanhn2001 at gmail dot com
13 年前
PHP 阻止接口常量被直接继承它的类/接口重写。但是,进一步继承是允许的。这意味着接口常量并不像之前评论中提到的那样是最终的。这是 Bug 还是功能?

<?php

interface a
{
const
b = 'Interface constant';
}

// 输出:Interface constant
echo a::b;

class
b implements a
{
}

// 这可以正常工作!
class c extends b
{
const
b = 'Class constant';
}

echo
c::b;
?>
vcnbianchi
2 年前
就像所有接口方法都是公开的,所有接口方法也是抽象的。
williebegoode at att dot net
10 年前
在他们的《设计模式》一书中,Erich Gamma 及其合著者(又称“四人帮”)将“接口”和“抽象类”互换使用。在使用 PHP 和设计模式时,接口虽然明确地定义了实现中应该包含的内容,但也对代码重用和修改提供了有益的指导。只要实现的变更遵循接口(无论是接口还是带有抽象方法的抽象类),大型复杂程序就可以安全地更新,而无需重新编写整个程序或模块。

在使用 PHP 编码时,同时考虑对象接口(作为关键字)以及更广义的“接口”(包括对象接口和抽象类),将“松散绑定”(松散绑定的对象)用于方便更改和重用的理念是理解“接口”这两个含义的有益方式。重点从“契约”转移到“松散绑定”,以实现协作开发和重用。
xedin dot unknown at gmail dot com
3 年前
此页面指出,如果扩展多个具有相同方法的接口,则签名必须兼容。但这并不是全部:`extends` 的顺序很重要。这是一个已知问题,虽然它是否是一个 Bug 有争议,但应该注意它,并以此为基础编写接口。

https://bugs.php.net/bug.php?id=67270
https://bugs.php.net/bug.php?id=76361
https://bugs.php.net/bug.php?id=80785
To Top