PHP Doku:: Objekt-Vererbung - language.oop5.inheritance.html

Verlauf / Chronik / History: (1) anzeigen

Sie sind hier:
Doku-StartseitePHP-HandbuchSprachreferenzKlassen und ObjekteObjekt-Vererbung

Ein Service von Reinhard Neidl - Webprogrammierung.

Klassen und Objekte

<<Sichtbarkeit

Gültigkeitsbereichsoperator (::)>>

Objekt-Vererbung

Vererbung ist ein gängiges Prinzip der Programmierung und PHP verwendet dieses Prinzip in seinem Objektmodell. Dieses Prinzip beeinflusst die Art und Weise, in der mehrere Klassen und Objekte in Beziehung zueinander stehen.

Wenn man zum Beispiel von einer Klasse erbt, so erbt die Unterklasse alle Methoden der Sichtbarkeiten public und protected von der Vaterklasse. Wenn eine Klasse diese Methoden nicht überschreibt, wird die ursprüngliche Funktionalität beibehalten.

Dies ist nützlich, um Funktionalität zu definieren und zu abstrahieren und erlaubt es, zusätzliche Funktionalität in ähnlichen Objekten zu implementieren, ohne sämtliche gemeinsame Funktionalitäten neu implementieren zu müssen.

Beispiel #1 Beispiel für Vererbung

<?php

class foo
{
    public function 
printItem($string)
    {
        echo 
'Foo: ' $string PHP_EOL;
    }
    
    public function 
printPHP()
    {
        echo 
'PHP ist großartig.' PHP_EOL;
    }
}

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

$foo = new foo();
$bar = new bar();
$foo->printItem('baz'); // Ausgabe: 'Foo: baz'
$foo->printPHP();       // Ausgabe: 'PHP ist großartig.'
$bar->printItem('baz'); // Ausgabe: 'Bar: baz'
$bar->printPHP();       // Ausgabe: 'PHP ist großartig.'

?>

7 BenutzerBeiträge:
- Beiträge aktualisieren...
OZ
15.11.2010 0:24
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__.': '.$name, E_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__.': '.$property, E_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__.': '.$property, E_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__.': '.$method, E_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
23.09.2010 7:11
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
19.09.2010 18:37
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
15.04.2010 5:53
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
 
?>
php at sleep is the enemy dot co dot uk
7.04.2010 11:36
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
31.03.2010 17:47
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
27.10.2009 15:01
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!

?>



PHP Powered Diese Seite bei php.net
The PHP manual text and comments are covered by the Creative Commons Attribution 3.0 License © the PHP Documentation Group - Impressum - mail("TO:Reinhard Neidl",...)