PHP Conference Japan 2024

对象继承

继承是一个完善的编程原则,PHP在其对象模型中使用了这一原则。这一原则将影响许多类和对象彼此之间的关系。

例如,在扩展一个类时,子类将继承父类中的所有公共和受保护的方法、属性和常量。除非一个类覆盖了这些方法,否则它们将保留其原始的功能。

这对于定义和抽象功能非常有用,并且允许在类似的对象中实现附加功能,而无需重新实现所有共享的功能。

父类的私有方法子类无法访问。因此,子类可以自己重新实现私有方法,而无需考虑正常的继承规则。然而,在 PHP 8.0.0 之前,finalstatic 限制被应用于私有方法。从 PHP 8.0.0 开始,唯一执行的私有方法限制是 private final 构造函数,因为这是在使用静态工厂方法而不是构造函数时“禁用”构造函数的常用方法。

方法、属性和常量的可见性可以放宽,例如,一个 protected 方法可以标记为 public,但不能被限制,例如,将一个 public 属性标记为 private。构造函数是一个例外,其可见性可以被限制,例如,一个 public 构造函数可以在子类中标记为 private

注意:

除非使用自动加载,否则必须在使用类之前定义它们。如果一个类扩展了另一个类,那么父类必须在子类结构之前声明。此规则适用于继承其他类和接口的类。

注意:

不允许使用只读属性覆盖读写属性,反之亦然。

<?php

class A {
public
int $prop;
}
class
B extends A {
// 非法:读写 -> 只读
public readonly int $prop;
}
?>

示例 #1 继承示例

<?php

class Foo
{
public function
printItem($string)
{
echo
'Foo: ' . $string . PHP_EOL;
}

public function
printPHP()
{
echo
'PHP is great.' . PHP_EOL;
}
}

class
Bar extends Foo
{
public function
printItem($string)
{
echo
'Bar: ' . $string . PHP_EOL;
}
}

$foo = new Foo();
$bar = new Bar();
$foo->printItem('baz'); // 输出:'Foo: baz'
$foo->printPHP(); // 输出:'PHP is great'
$bar->printItem('baz'); // 输出:'Bar: baz'
$bar->printPHP(); // 输出:'PHP is great'

?>

与内部类的返回类型兼容性

在 PHP 8.1 之前,大多数内部类或方法都没有声明其返回类型,并且在扩展它们时允许任何返回类型。

从 PHP 8.1.0 开始,大多数内部方法开始“尝试性地”声明其返回类型,在这种情况下,方法的返回类型应与正在扩展的父类兼容;否则,将发出弃用通知。请注意,缺少显式返回声明也被视为签名不匹配,因此会导致发出弃用通知。

如果由于 PHP 跨版本兼容性问题而无法为覆盖方法声明返回类型,则可以添加 ReturnTypeWillChange 属性以取消弃用通知。

示例 #2 覆盖方法未声明任何返回类型

<?php
class MyDateTime extends DateTime
{
public function
modify(string $modifier) { return false; }
}

// “已弃用:MyDateTime::modify(string $modifier) 的返回类型应与 DateTime::modify(string $modifier): DateTime|false 兼容,或者应使用 #[\ReturnTypeWillChange] 属性来临时抑制此通知” 从 PHP 8.1.0 开始
?>

示例 #3 覆盖方法声明了错误的返回类型

<?php
class MyDateTime extends DateTime
{
public function
modify(string $modifier): ?DateTime { return null; }
}

// “已弃用:MyDateTime::modify(string $modifier): ?DateTime 的返回类型应与 DateTime::modify(string $modifier): DateTime|false 兼容,或者应使用 #[\ReturnTypeWillChange] 属性来临时抑制此通知” 从 PHP 8.1.0 开始
?>

示例 #4 覆盖方法声明了错误的返回类型,但没有发出弃用通知

<?php
class MyDateTime extends DateTime
{
/**
* @return DateTime|false
*/
#[\ReturnTypeWillChange]
public function
modify(string $modifier) { return false; }
}

// 没有触发通知
?>
添加注释

用户贡献笔记 6 条笔记

jackdracona at msn dot com
14 年前
这里有一些关于 PHP 继承的澄清——网络上有很多错误的信息。PHP 确实支持多级继承。(我使用 5.2.9 版本进行了测试)。它不支持多重继承。

这意味着您不能让一个类扩展另外两个类(参见 extends 关键字)。但是,您可以让一个类扩展另一个类,该类又扩展另一个类,依此类推。

示例

<?php
class A {
// 更多代码在此
}

class
B extends A {
// 更多代码在此
}

class
C extends B {
// 更多代码在此
}


$someObj = new A(); // 没有问题
$someOtherObj = new B(); // 没有问题
$lastObj = new C(); // 仍然没有问题

?>
Mohammad Istanbouly
7 年前
我认为初学者理解继承的最佳方法是通过一个真实的例子,所以这里有一个简单的例子,我可以提供给您

<?php

class Person
{
public
$name;
protected
$age;
private
$phone;

public function
talk(){
// 在这里执行操作
}

protected function
walk(){
// 在这里执行操作
}

private function
swim(){
// 在这里执行操作
}
}

class
Tom extends Person
{
/*由于 Tom 类扩展了 Person 类,这意味着
类 Tom 是子类,而类 Person 是
父类,子类将继承所有公共
和受保护的成员(属性和方法)来自
父类*/

/*所以 Tom 类将具有这些属性和方法*/

//public $name;
//protected $age;
//public function talk(){}
//protected function walk(){}

//但它不会继承私有成员
//这就是对象继承的全部含义
}
akashwebdev at gmail dot com
9 年前
不支持多重继承的说法是正确的,但使用特性可以重新审视这一点。

例如

<?php
trait custom
{
public function
hello()
{
echo
"hello";
}
}

trait
custom2
{
public function
hello()
{
echo
"hello2";
}
}

class
inheritsCustom
{
use
custom, custom2
{
custom2::hello insteadof custom;
}
}

$obj = new inheritsCustom();
$obj->hello();
?>
strata_ranger at hotmail dot com
14 年前
我最近在扩展一个 PEAR 类时遇到了一种情况,我想要调用类层次结构中两级之上的构造函数,忽略直接父类。在这种情况下,您需要使用 :: 运算符显式引用类名。

幸运的是,就像使用 'parent' 关键字一样,PHP 正确地识别出您是在对象类层次结构内的受保护上下文中调用函数。

例如

<?php
class foo
{
public function
something()
{
echo
__CLASS__; // foo
var_dump($this);
}
}

class
foo_bar extends foo
{
public function
something()
{
echo
__CLASS__; // foo_bar
var_dump($this);
}
}

class
foo_bar_baz extends foo_bar
{
public function
something()
{
echo
__CLASS__; // foo_bar_baz
var_dump($this);
}

public function
call()
{
echo
self::something(); // self
echo parent::something(); // parent
echo foo::something(); // grandparent
}
}

error_reporting(-1);

$obj = new foo_bar_baz();
$obj->call();

// 输出类似于:
// foo_bar_baz
// object(foo_bar_baz)[1]
// foo_bar
// object(foo_bar_baz)[1]
// foo
// object(foo_bar_baz)[1]

?>
jarrod at squarecrow dot com
15 年前
您可以通过使用“abstract”关键字强制一个类严格成为可继承的类。当您使用 abstract 定义一个类时,任何尝试实例化该类的单独实例的操作都将导致致命错误。这对于基类等情况很有用,在这些情况下,它将被多个子类继承,但您希望限制单独实例化的能力。

示例........

<?php

abstract class Cheese
{
// 只能被另一个类继承
}

class
Cheddar extends Cheese
{
}

$dinner = new Cheese; // 致命错误
$lunch = new Cheddar; // 可行!

?>
匿名用户
5 年前
PHP7 如果在子类中重新声明一个函数且参数不同,则会发出警告。例如

class foo {
function print($text='') {
print text;
}
}

class bar extends foo {
function print($text1='',$text2='') {
print text1.text2
}
}

将发出 PHP 警告:bar::print($text1 = '', $text2 = '') 的声明应与 foo::print($text= '') 兼容。
To Top