对象继承

继承是一个成熟的编程原则,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; }
}

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

用户贡献的注释 8 注释

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 年前
不支持多重继承的概念是正确的,但使用 traits 可以重新考虑。

例如:

<?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
13 年前
我最近在扩展一个 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
14 年前
您可以使用 "abstract" 关键字强制一个类严格地成为可继承的类。当您用 abstract 定义一个类时,任何尝试实例化一个单独的实例都会导致致命错误。这在基类情况下很有用,在这种情况下,它将被多个子类继承,但您希望限制单独实例化它的能力。

示例........

<?php

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

class
Cheddar extends Cheese
{
}

$dinner = new Cheese; // 致命错误
$lunch = new Cheddar; // 工作!

?>
Anonymous
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= '') 兼容。
niemans at pbsolo dot nl
4 年前
继承在创建时起作用,即使用关键字 'new'。静态属性使我对理解感到困惑,因此为了展示可见性对继承的影响,我创建了一个简单的演示脚本,以及一些 set 和 get 魔术

<?php
class A {
private
$a = 'private';
protected
$b = 'protected';
public
$c = 'public';
static
$d = 'static';
public function
__construct()
{
$this->e = 'constructed';
}
public function
__set($property, $value)
{
echo
' set ' . $property . '=' . $value;
$this->$property=$value;
}
public function
__get($property)
{
echo
' get ' . $property;
$this->$property = 'dynamic'; // 调用 __set() !!
return $this->$property;
}
}

class
B extends A
{
public function
constructMe()
{
$this->e = 'constructed2';
}
}

class
C extends B
{
public function
__construct()
{
parent::constructMe();
}
}

echo
" \n";
$a = new A();
$b = new B();
echo
" \n";
echo
' B:c='.$b->c;
echo
" \n";
echo
' B:d=' .$b->d;
echo
" \n";

$c = new C();
echo
" \n";

print_r($a);
print_r($b);
print_r($c);

print_r(A::$d);
print_r(B::$d);
print_r(C::$d);

echo
'A class: ';
$R = new reflectionclass('A');
print_r($R->getdefaultproperties());
print_r($R->getstaticproperties());
echo
'B class: ';
$R = new reflectionclass('B');
print_r($R->getdefaultproperties());
print_r($R->getstaticproperties());

?>

这将输出

set e=constructed
B:c=public
get d set d=dynamic B:d=dynamic
set e=constructed2
A Object
(
[a:A:private] => private
[b:protected] => protected
[c] => public
[e] => constructed
)
B Object
(
[a:A:private] => private
[b:protected] => protected
[c] => public
[d] => dynamic
)
C Object
(
[a:A:private] => private
[b:protected] => protected
[c] => public
[e] => constructed2
)
staticstaticstaticA class: Array
(
[d] => static
[a] => private
[b] => protected
[c] => public
)
Array
(
[d] => static
)
B class: Array
(
[d] => static
[b] => protected
[c] => public
)
Array
(
[d] => static
)

这展示了私有变量 ($a) 如何被继承,静态变量 ($d) 如何被继承(通过类,而不是通过对象),以及在父类中更改或添加变量 ($e, $d) 不会被子类继承。
sibian0218 at gmail dot com
5 年前
我注意到有关继承的一件事......
当用私有方法声明一个抽象类时,
子类中,私有方法优先于公有方法,即使子类重写了父类的方法。
(实际上,如果你使用不同的签名重新声明一个方法)。

希望这有帮助
To Top