类与对象
在线手册:中文  英文

对象继承

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

比如,当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。

继承对于功能的设计和抽象是非常有用的,而且对于类似的对象增加新功能就无须重新再写这些公用的功能。

Note:

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

Example #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'); // Output: 'Foo: baz'
$foo->printPHP();       // Output: 'PHP is great' 
$bar->printItem('baz'); // Output: 'Bar: baz'
$bar->printPHP();       // Output: 'PHP is great'

?>

类与对象
在线手册:中文  英文

用户评论:

Walter Dolce (2012-08-11 18:02:19)

I don't know (and remember) if it's stated in the documentation but I thought were worth to post this note in Object Inheritance instead of in "Constructors and Destructors".

This note talks about inheritance and constructors (and maybe destructors? I haven't checked yet).

Assuming that you have two classes, the second inherits from the first:

Class-1.php
<?php

class First_Class
{

    protected 
$_bar "PHP Rocks!";

    public function 
__construct($mandatoryArg) {

        echo 
"Here is the mandatory argument: $mandatoryArg";

    }

    
// Other methods..

}

?>

Class-2.php
<?php

class Second_Class extends First_Class
{

    public function 
foo() {

        echo 
"Method foo() fired! " $this->_bar;

    }

}

?>

Now, if you then need to use the second class BUT this method make use of a property coming from the First_Class, so you then write:

index.php
<?php

include 'First-Class.class.php';
include 
'Second-Class.class.php';

$second = new Second_Class;
$second->foo();

?>

PHP will raise an error because by instantiating the Second_Class that extends the First_Class THAT HAVE a constructor WITH a $mandatoryArg setted...it doesn't find this argument. So here the error.

Solution: in the Second_Class that extends the First_Class add an 'empty __construct'.

Example:

<?php

class Second_Class extends First_Class
{

    public function 
__construct() { return; }

    
// Same as above..

}

?>

I'm opened to clarifications because today I've discovered this behaviour for the first time (+4 years PHP development).

Hope it will helps. Kind regards.

W.D.

msg2maciej at aol dot com (2011-01-28 16:08:45)

PHP supports single class inheritance. My bare idea on accessing protected methods with power of abstracts and sort of "multi-class inheritance SIMULATION":

<?php
error_reporting
(E_ALL);

abstract class 
Base {
    abstract protected function 
__construct ();
    abstract protected function 
hello_left ();
    abstract protected function 
hello_right ();
}

abstract class 
NotImplemented_Left extends Base {
protected function 
hello_right () {
echo 
'well, wont see that'; }}

abstract class 
NotImplemented_Right extends Base {
protected function 
hello_left () {
echo 
'well, wont see that'; }}

class 
Left extends NotImplemented_Left {
protected function 
__construct () {        # limited visibility, no access from "outside"
echo __CLASS__.'::protected __construct'"\n"; }
protected function 
hello_left () {        # limited visibility, no access from "outside"
echo 'protected hello_left in ' __CLASS__ "\n"; }}

class 
Right extends NotImplemented_Right {
protected function 
__construct () {        # limited visibility, no access from "outside"
echo __CLASS__.'::protected __construct'"\n"; }
protected function 
hello_right () {        # limited visibility, no access from "outside"
echo 'protected hello_right in ' __CLASS__ "\n"; }
protected function 
hello_left () {
echo 
"wont see that, and easy to get rid of it from here\n"; }}

class 
Center extends Base {
private 
$left;
private 
$right;
public function 
__construct () {
echo 
'welcome in ' __CLASS__ "\n";
echo 
'Center::'$this->left = new Left;
echo 
'Center::'$this->right = new Right;
echo 
" oh and\n";
$this->hello_left();
$this->hello_right();
}
public function 
hello_left () {            # calling class Left
echo __CLASS__.'::'$this->left->hello_left(); }
public function 
hello_right () {        # calling class Right
echo __CLASS__.'::'$this->right->hello_right(); }
}

$c = new Center;
?>

Produces:

welcome in Center
Center::Left::protected __construct
Center::Right::protected __construct
 oh and
Center::protected hello_left in Left
Center::protected hello_right in Right

OZ (2010-11-14 15:24:19)

Model for Mixins pattern:

<?php
interface IMixinsCaller
{
    public function 
__mixin_get_property($property);

    public function 
__mixin_set_property($property$value);

    public function 
__mixin_call($method$value);
}

abstract class 
MixinsCaller implements IMixinsCaller
{
    protected 
$mixins = array();

    public function 
__call($name$arguments)
    {
        if (!empty(
$this->mixins))
        {
            foreach (
$this->mixins as $mixin)
            {
                if (
method_exists($mixin$name))
                {
                    return 
call_user_func_array(array($mixin$name), $arguments);
                }
            }
        }
        
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$nameE_USER_WARNING);
    }

    public function 
__mixin_get_property($property)
    {
        if (
property_exists($this$property))
        {
            return 
$this->$property;
        }
        
trigger_error('Non-existent property was get in class '.__CLASS__.': '.$propertyE_USER_WARNING);
    }

    public function 
__mixin_set_property($property$value)
    {
        if (
property_exists($this$property))
        {
            return 
$this->$property $value;
        }
        
trigger_error('Non-existent property was set in class '.__CLASS__.': '.$propertyE_USER_WARNING);
    }

    public function 
__mixin_call($method$value)
    {
        if (
method_exists($this$method))
        {
            return 
call_user_func_array(array($this$method), $value);
        }
        
trigger_error('Non-existent method was called in class '.__CLASS__.': '.$methodE_USER_WARNING);
    }

    public function 
AddMixin($mixin)
    {
        
$this->mixins[] = $mixin;
    }
}

abstract class 
Mixin
{
    
/** @var IMixinsCaller $parent_object */
    
private $parent_object;

    public function 
__construct(IMixinsCaller $parent_object)
    {
        
$this->parent_object $parent_object;
    }

    public function 
__get($property)
    {
        return 
$this->parent_object->__mixin_get_property($property);
    }

    public function 
__set($property$value)
    {
        return 
$this->parent_object->__mixin_set_property($property$value);
    }

    public function 
__call($method$value)
    {
        return 
$this->parent_object->__mixin_call($method$value);
    }
}
?>

janturon at email dot cz (2010-09-22 22:11:54)

Here is an easy way how to implement multiple inheritance in PHP using magic methods. Let's have this class:
class Extender {
private $objs = array();
public function __construct() {
$drones = func_get_args();
foreach($drones as $drone) $this->objs[$drone] = new $drone();
foreach($this->objs as $obj) $obj->hive = $this;
}
public function __get($attr) {
foreach($this->objs as $obj)
if(isset($obj->$attr))
return $obj->$attr;
}
public function __set($attr,$val) {
if($attr=="hive") return;
foreach($this->objs as $obj)
if(isset($obj->$attr))
$obj->$attr = $val;
}
public function __call($meth,$args) {
foreach($this->objs as $obj)
if(method_exists($obj,$meth))
return call_user_func_array(array($obj,$meth),$args);
}
}
Now multiple inheritance can be written this way ($hive is reference to child classes)
class c1 {
public $p1 = "x";
function f1() { echo $this->p1; }
}
class c2 {
public $p2 = "x";
function f2() { echo $this->p2; }
}
class c_extends_c1_c2 {
static $hive;
function test() {
$this->hive->p1 = "hello,";
$this->hive->p2 = "world!";
return $this->hive->f1() . $this->hive->f2();
}
}
And here is how to use it:
$obj = new Extender("c1","c2","c_extends_c1_c2");
echo $obj->test(); //"hello,world!"

strata_ranger at hotmail dot com (2010-09-19 09:37:58)

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]

?>

jackdracona at msn dot com (2010-04-14 20:53:15)

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 {
        
// more code here
}
 
class 
extends {
        
// more code here
}
 
class 
extends {
        
// more code here
}
 
 
$someObj = new A();  // no problems
$someOtherObj = new B(); // no problems
$lastObj = new C(); // still no problems
 
?>

php at sleep is the enemy dot co dot uk (2010-04-07 02:36:31)

Here's fun, an attempt to make some degree of multiple inheritance work in PHP using mixins. It's not particularly pretty, doesn't support method visibility modifiers and, if put to any meaningful purpose, could well make your call stack balloon to Ruby-on-Rails-esque proportions, but it does work.

<?php
abstract class Mix {
    
    protected 
$_mixMap = array();
    
    public function 
__construct(){
        
         
$this->_mixMap $this->collectMixins($this);
    }
    
    public function 
__call($method$args){
        
        
// doesn't pass scope
        //return call_user_func_array(array($className, $method), $args);
        
        // Error: Given object is not an instance of the class this method was declared in
        //$method = new ReflectionMethod($className, $method);
        //return $method->invokeArgs($this, $args);
        
        
$payload $this->buildMixinPayload($this->_mixMap$method$args);
        if(!
$payload) throw new Exception('Method ' $method ' not found');
        
        list(
$mixinMethod, list($method$args)) = $payload;
        
        return 
$this->$mixinMethod($method$args);
        
    }
    
    protected function 
collectMixins($class){
        
        static 
$found = array();
        static 
$branch = array();
        
        if(empty(
$branch)) $branch[] = get_class($this);
        
$mixins = array();
        
        foreach(
array_reverse(get_class_methods($class)) as $method){
            if(
preg_match('/^mixin(\w+)$/'$method$matches)){
                
                
$className $matches[1];
                
                if(
in_array($className$branch))
                    throw new 
Exception('Circular reference detected ' implode(' > '$branch) . ' > ' $className);
                    
                if(!
in_array($className$found)){
                
                    if(!
class_exists($className)) throw new Exception('Class ' $className ' not found');
            
                    
// populate props from mixin class
                    
foreach(get_class_vars($className) as $key => $value){        
                        if(!
property_exists($this$key)) $this->$key $value;
                    }
                    
                    
$found[] = $branch[] = $className;                
                    
$mixins[$className] = $this->collectMixins($className);
                }
                
                
$branch = array(get_class($this));
            }
        }
        
        return 
$mixins;
    }
    
    protected function 
buildMixinPayload($mixins$method$args){
        
        foreach(
$mixins as $className => $parents){
            
            
$mixinMethod 'mixin' $className;
            
            if(
method_exists($className$method)) return array($mixinMethod, array($method$args));
            
            if(!empty(
$parents) && $return $this->buildMixinPayload($parents$method$args)){
                return array(
$mixinMethod$return);
            }
        }
        
        return 
false;
    }
    
}
?>

php at sleep is the enemy dot co dot uk (2010-03-31 07:47:36)

Here's some example usage of the mixin class.

<?php

class Lunch extends Mix {
    
    public 
$edible true;
    
    
/*
     * Circular references are, of course, illegal and will be detected
     */
    /*
    public function mixinSteakAndKidneyPie($method, $args){
        return SteakAndKidneyPie::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
    }
    //*/
    
    
public function isEdible($affirm$negate){
        return 
$this->edible $affirm $negate;
    }
}

class 
Pie extends Mix {
    
    
/*
     * class tokens are bound at compile time so need to be explicitly declared
     * Need to make sure there are enough argument placeholders to cover all mixed in methods of Lunch
     * Late static binding may improve this situation
     */
    
public function mixinLunch($method$args){
        return 
Lunch::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
    }
    
    public function 
buildPie($sep ','){
        return 
'Crust' $sep $this->getFilling() . $sep 'More Crust';
    }
    
    public function 
getFilling(){
        
$this->edible false;
        return 
'Baking beans';
    }
}

class 
SteakAndKidney extends Mix {
    
    public function 
mixinLunch($method$args){
        return 
Lunch::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
    }
    
    
/*
     * everything to be mixed in must be public
     * protected/private methods called from within mixed in methods will fail
     */
    
public $filling 'Steak and Kidney';
    
    public function 
getFilling(){
        
$this->edible true;
        return 
$this->filling;
    }
}

class 
SteakAndKidneyPie extends Mix {
    
    
    
/*
     * order of mixin declaration significant
     * later declarations override earlier ones
     */
    
public function mixinSteakAndKidney($method$args){
        return 
SteakAndKidney::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
    }
    
    public function 
mixinPie($method$args){
        return 
Pie::$method(@$args[0], @$args[1], @$args[2], @$args[3], @$args[4], @$args[5]);
    }
    
    
/*
     * Pick specific methods like so:
     */
    //*
    
public function getFilling(){
        return 
SteakAndKidney::getFilling();
    }
    
//*/
    
}

$pie = new SteakAndKidneyPie();
echo 
$pie->buildPie(' | ');
echo 
'<br/>Pie ' $pie->isEdible('is''is not') . ' Edible';

/*
OUTPUTS:
Crust | Steak and Kidney | More Crust
Pie is Edible
*/

?>

jarrod at squarecrow dot com (2009-10-27 06:01:45)

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!

?>

易百教程