update page now

对象继承

继承已为大家所熟知的一个程序设计特性,PHP 的对象模型也使用了继承。继承将会影响到类与类,对象与对象之间的关系。

比如,当扩展一个类,子类就会继承父类所有 public 和 protected 的方法,属性和常量。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。

继承有助于功能的设计和抽象,在实现类似的对象、增加新功能时,无须重复编写这些公用的功能。

子类无法访问父类的私有方法。因此,子类无需考虑正常的继承规则而重新实现私有方法。 然而,在 PHP 8.0.0 之前, finalstatic 的限制会应用于 private 方法。 从 PHP 8.0.0 开始,仅 private final 的构造器是唯一受限的 private 方法; 想要“禁用”构造器,我们通常用静态工厂方法作为代替。

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

注意:

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

注意:

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

<?php

class A {
public
int $prop;
}
class
B extends A {
// Illegal: read-write -> readonly
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 The overriding method does not declare any return type

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

// "Deprecated: Return type of MyDateTime::modify(string $modifier) should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice" as of PHP 8.1.0
?>

示例 #3 The overriding method declares a wrong return type

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

// "Deprecated: Return type of MyDateTime::modify(string $modifier): ?DateTime should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice" as of PHP 8.1.0
?>

示例 #4 The overriding method declares a wrong return type without a deprecation notice

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

// No notice is triggered
?>
添加备注

用户贡献的备注 7 notes

up
215
jackdracona at msn dot com
15 years ago
Here is some clarification about PHP inheritance – there is a lot of bad information on the net.  PHP does support Multi-level inheritance.  (I tested it using version 5.2.9).  It does not support multiple inheritance.
 
This means that you cannot have one class extend 2 other classes (see the extends keyword).  However, you can have one class extend another, which extends another, and so on. 
 
Example:
 
<?php
class A {
        // more code here
}
 
class B extends A {
        // more code here
}
 
class C extends B {
        // more code here
}
 
 
$someObj = new A();  // no problems
$someOtherObj = new B(); // no problems
$lastObj = new C(); // still no problems
 
?>
up
104
Mohammad Istanbouly
8 years ago
I think the best way for beginners to understand inheritance is through a real example so here is a simple example I can gave to you 

<?php

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

    public function talk(){
        //Do stuff here
    }

    protected function walk(){
        //Do stuff here
    }

    private function swim(){
        //Do stuff here
    }
}

class Tom extends Person
{
    /*Since Tom class extends Person class this means 
        that class Tom is a child class and class person is 
        the parent class and child class will inherit all public 
        and protected members(properties and methods) from
        the parent class*/

     /*So class Tom will have these properties and methods*/

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

     //but it will not inherit the private members 
     //this is all what Object inheritance means
}
up
1
ignacio at inek dot com dot ar
1 year ago
In case you have a public readonly property in a class you need to extend, adding other properties, this can be a way to do it:

<?php

class A {
    public function __construct(
        public readonly int $prop
    ) {}
}

class B extends A {
    public function __construct(
        int $prop,
        public readonly int $prop2
    ) {
        parent::__construct($prop);
    }
}
?>
up
20
akashwebdev at gmail dot com
10 years ago
The Idea that multiple inheritence is not supported is correct but with tratits this can be reviewed.

for e.g.
 
<?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();
?>
up
15
jarrod at squarecrow dot com
16 years ago
You can force a class to be strictly an inheritable class by using the "abstract" keyword. When you define a class with abstract, any attempt to instantiate a separate instance of it will result in a fatal error. This is useful for situations like a base class where it would be inherited by multiple child classes yet you want to restrict the ability to instantiate it by itself.

Example........

<?php

abstract class Cheese
{
      //can ONLY be inherited by another class
}

class Cheddar extends Cheese
{
}

$dinner = new Cheese; //fatal error
$lunch = new Cheddar; //works!

?>
up
17
strata_ranger at hotmail dot com
15 years ago
I was recently extending a PEAR class when I encountered a situation where I wanted to call a constructor two levels up the class hierarchy, ignoring the immediate parent.  In such a case, you need to explicitly reference the class name using the :: operator.

Fortunately, just like using the 'parent' keyword PHP correctly recognizes that you are calling the function from a protected context inside the object's class hierarchy.

E.g:

<?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();

// Output similar to:
// foo_bar_baz
// object(foo_bar_baz)[1]
// foo_bar
// object(foo_bar_baz)[1]
// foo
// object(foo_bar_baz)[1]

?>
up
0
alex_kernel at proton dot me
4 days ago
Documentation says: Private methods of a parent class are not accessible to a child class. As a result, "child classes may reimplement a private method themselves without regard for normal inheritance rules."

So it is not very obvious what this will output:

<?php 
class Bar {
    private function one() {
        echo "Bar::one()\n";
    }
    public function run() {
        echo "Bar::run()\n";
        $this->one();
    }
}
class Foo extends Bar {
    private function one() {
        echo "Foo::one()\n";
    }
}
(new Foo())->run();
?>
Bar::run()
Bar::one()
 
A common sense next step would be to try make the method "one" in Foo public, 
but this does not work either if the caller is the parent class. 

For the parent to be able to direct the child class 
both need to be public or you must call the method explicitly on the child class: 
<?php
(new Foo())->one();
?>
To Top