# PHP Mehrfachvererbung



## jans2 (1. September 2010)

Hi,

ich denke viele haben sich gefragt wie man das machen kann, darunter auch ich:
-> eine class zu erstellen, die nicht nur "class x" sondern auch "class y" geerbt hat..

ich denke ich hab die lösung:

```
<?
class x {
}
class y extends x {
}
class endklasse extends y {
}
?>
```

das geht nach meinen kenntnissen.. ich hoffe ich konnte irgendjemandem helfen


----------



## Flex (1. September 2010)

Unter einer Mehrfachvererbung versteht man aber etwas anderes, nämlich dass eine Klasse von zwei anderen Klassen erbt und das auf einer Generationshierarchie. Du verschachtelst es ja einfach weiter.


Zudem korrigiere bitte deinen Beitrag bezüglich der Groß- und Kleinschreibung.


----------



## jans2 (1. September 2010)

aber "endklasse"
erbt x und y!


----------



## sheel (1. September 2010)

Felix Jacobi hat gesagt.:


> auf einer Generationshierarchie


 
Ist bei dir nicht der Fall.


----------



## ComFreek (1. September 2010)

sheel hat gesagt.:


> Ist bei dir nicht der Fall.


"endklasse" erbt nur von "y", wobei diese von "x" erbt. Somit erbt "endklasse" nur indirekt von "x", aber direkt von "y".

Falls ich falsch liege, korrigiert mich bitte.

Aber es würden sich noch Interfaces anbieten.


----------



## sheel (1. September 2010)

Stimmt, aber das nennt man eben nicht "Mehrfachvererbung"; worums ja hier geht.


----------



## Yaslaw (2. September 2010)

In deinem Beispiel jans2, muss ja y schon x kennen. Ergo ists keine Mehrfchvererbung. Bei Mehrfachvererbung wie du es haben willst, sollten sich x und y nicht kennen müssen


----------



## Gainwar (2. September 2010)

Ich vermute mal das jans2 sehr wohl bewusst ist das es keine richtige Mehrfachvererbung ist, schließlich wird in der PHP Dokumentation audrücklich darauf hingewiesen. Nur wollte er einen Weg aufzeigen wie man diese faken kann.


----------



## Yaslaw (2. September 2010)

Aber selbst der Fake oder Workaround bringt nix zur Mehrfachvererbung.


----------



## Tim Bureck (2. September 2010)

Ich glaube es bringt jetzt nichts hier herum zu diskutieren. Ich glaube wir sind uns alle einig, dass das keine Mehrfachvererbung ist, das Ergebnis aber durchaus vergleichbar ist (Klasse ist x und y, hat deren Methoden etc.).


----------



## Flex (2. September 2010)

So, ich mache einfach mal ein finales Wort 

Man hat eine Klasse Auto. Diese soll von seinen Bestandteilen erben, nämlich Motor, Getriebe, Bremse.

Das heißt das würde so aussehen:


```
class Auto extends Motor, Getriebe, Bremse
{

}
```

Leider gibt das einen Error, da die Mehrfachvererbung nicht funktioniert.
Möchte ich jetzt obige Methode verwenden, müsste ich also Getriebe von Bremsen erben lassen, Motor von Getriebe und letztendlich Auto von Motor. 
Nur wenn ich jetzt zu meinem imaginären TÜV fahre und die Komponenten einzeln testen möchte, habe ich das Nachsehen.... Denn das Getriebe kann plötzlich auch bremsen, etc.

Es ist also hier nicht die Frage ob es funktioniert oder nicht, oder ob es Sinn macht oder nicht, aber die Logik möchte halt nicht diese Methode haben. (IMHO  )


----------



## ComFreek (2. September 2010)

ComFreek hat gesagt.:


> Aber es würden sich noch Interfaces anbieten.


Was ist mit der Lösung?


----------



## Flex (2. September 2010)

Interfaces vererben aber keine Funktionalität sondern schreiben nur vor welche Methoden eine Klasse definieren muss.
Außerdem kann ein Interface nur öffentliche Methoden besitzen.


----------



## Yaslaw (2. September 2010)

Irgendwie dachte ich, dass kanns doch nicht sein das es nicht machbar ist.... Nun, hab mal ein wenig herumgespielt.
Das ganze ist noch nicht fertig, jedoch währe das mMn nur noch eine Fleissübung


```
<?php
/**
 * ============================
 * Beispiel
 * ============================
 */
// Erste Parentklasse
class Mutter{
    public function __construct(){}
    public function gibEssen(){return 'Apfel'; }
    protected function sagHallo($name){return "Mutter sagt 'Hallo {$name}'";}
    private function tschuess(){}
}
// Zweite Parentklasse
class Vater{
    public $nachname;
    public function __construct($vorname, $nachname){$this->name = $name; $this->nachname = $nachname;}
    public function gibGeld($betrag){return "{$betrag} Franken"; }
    protected function sagHallo($name){return "Vater sagt 'Hallo {$name}'";}
}

//Klasse Kind. Abgeleitet von Vater und Mutter
//Die Ableitungen werden durch die Abstrakte Klasse AbstractMultiExtends durchgeführt
//Die Parentklassen wren im Constructeur angehängt

class Kind extends AbstractMultiExtends{
    public function __construct(){
        parent::addExtendedClass('Mutter', 48);
        parent::addExtendedClass('Vater', array('Thomas', 'Muster'));
    }
    public function mutterSagtHallo(){
        //Die Funktion sagHallo() ist in beiden Parents definiert. Da Mutter aber zuerst als
        //Paretn definiert wurde, hat sie vorrang
        return $this->sagHallo('Max Muster');
    }
    public function getName(){
        return "Max {$this->nachname}";
    }
}

$ich = new Kind();
echo $ich->gibEssen().'<br />';
echo $ich->gibGeld(10).'<br />';
echo $ich->getName().'<br />';
echo $ich->mutterSagtHallo().'<br />';
echo $ich->nachname;

/* Ausgabe:
Apfel
10 Franken
Max Muster
Mutter sagt 'Hallo Max Muster'
Muster
 */
?>
```

Das war der einfache Teil. und nun der Code der das ganze zum laufen bringt...

```
<?php
//TODO Property ebenfalls behandeln
//TODO Statische Aufrufe ebenfalls behandeln
//TODO Beim Aufruf prüfen ob die Methode im Original Protected ist
//TODO Bei doppelten Properties/Methodes bestimmen welcher Parent gilt


/**
 * ============================
 * Abstracte Klasse
 * ============================
 */

abstract class AbstractMultiExtends{
    private $methods = array();
    private $properties = array();
    private $objs = array();
    
    const C_METHOD = 'methods';
    const C_PROPERTY = 'properties';
    /**
     * ein weitere ExtendesClasse hinzufügen
     * @param String    $name   
     * @param Array     $params
     */
    protected function addExtendedClass($className, $params = array()){
        //Eine Wrapperklasse erstellen, damit die Protected-Methodes Public werden
        $name = self::createWrapperClass($className);
        //Falls nur ein Parameter angegeben wird, diesen in einen Array schreiben
        if(!is_array($params)) $params = array($params);
        //Objecktname defineiren
        $objName = "obj{$name}";
        //Eine Instance der Wrapperklasse anlegen und in den privaten Array speichern
        $this->objs[$objName] = create_user_obj_array($name, $params);        
        //Methodennamen der Klasse in einen privaten Array speichern
        $this->addItems($objName, self::C_METHOD, get_class_methods($name));
        //Propertiesnamen der Klasse in einen privaten Array speichern
        $this->addItems($objName, self::C_PROPERTY, array_keys(get_class_vars($name)));
    }
    /**
     * Methoden oder Properties der Extended-Klasse in den Index einfügen
     * @param String        $name   Objektname
     * @param Constante     $var    Art der items
     * @param Array<String> $array  Liste der items
     */
    private function addItems($name, $list, $array){
        $newVars = array_fill_keys($array,  "\$this->objs['{$name}']");
        $this->$list = array_merge($newVars, $this->$list);            
    }
    
    /**
     * Aufruf einer Methode. Der Aufruf wird an das entsprechende Parentobjekt weitergeleitet
     * @param String $name
     * @param Array $params
     * @return Variant
     */
    public function __call($name, $params){
        if(array_key_exists($name, $this->methods)){
            $obj = $this->methods[$name];         
            $obj = eval("return $obj;");
            return call_user_func_array(array($obj, $name), $params);
        }
    }
    
    /**
     * Aufruf eines Property. Der Aufruf wird an das entsprechende Parentobjekt weitergeleitet
     * @param String $name
     * @return Variant
     */
        public function __get($name){
        if(array_key_exists($name, $this->properties)){
            $obj = $this->properties[$name];
            return eval("return {$obj}->{$name};");
        }
    }

    /**
     * Erstellen einer Wrapperlasse um die ParentClass
     * @param String    $className
     * @return String   Name der Wrapperklasse
     */
    final private static function createWrapperClass($className){
        //ReflectionObject der Klasse zur weiteren Analyse anlegen
        $ref = new ReflectionClass($className);
        $wrapperName = "{$className}Wrapper";
        //Die Classe zusammenstellen
        $lines[] = "class {$wrapperName} extends {$className}{";
        $lines[] = '
    public function __construct(){
        $pStrings  = $params = func_get_args();    
        array_walk($pStrings, create_function(\'&$item, $key\', \'$item = "\$params[{$key}]";\'));
        eval(\'parent::__construct(\' . implode(\',\', $pStrings) . \');\');
    }
        ';
        //Die Methoden hinzufügen
        self::createWrapperMethodes($lines, $ref);
        $lines[] = '}';
        //Aus allen Zeilen ein String erstellen
        $classPhp = implode("\n", $lines);
        //Die Klasse ausführen  
        eval($classPhp);
        return $wrapperName;
    }
    
    /**
     * Erstellen der Wrappermethoden für die Wrapperklasse
     * @param $lines
     * @param $ref
     */
    final static function createWrapperMethodes(&$lines, ReflectionClass $ref){
        foreach($ref->getMethods() as $method){
            if($method->isProtected()){
                $params = array();
                $modifiers = $method->getModifiers() - ReflectionMethod::IS_PROTECTED + ReflectionMethod::IS_PUBLIC;            
                $modifiers = implode(' ', Reflection::getModifierNames($modifiers));
                foreach($method->getParameters() as $param){
                    $params[] = $param->getName();
                }
                array_walk($params, create_function('&$item, $key', '$item = "\${$item}";'));
                $paramString = implode(', ', $params);
                $lines[] = "
    {$modifiers} function {$method->name}({$paramString}){
        return parent::{$method->name}({$paramString});
    }        
                ";
            }
        }
    }
}


/**
 * erstellt ein Object einer Klasse mit dem einer freien Anzahl Paramtern
 * @param   String  $className
 * @param   Variant
 * @return  Object
 * 
 * @example $obj = create_user_obj('myClass', $paramter1, $paramter2);
 */
function create_user_obj($className){
    $params = func_get_args();
    $className = array_shift($params);
    return create_user_obj_array($className, $params);
}

/**
 * erstellt ein Object einer Klasse mit dem Array $params als Argumente
 * @param   String  $className
 * @param   Array   $params
 * @return  Object
 * 
 * @example $obj = create_user_obj('myClass', array($paramter1, $paramter2));
 */
function create_user_obj_array($className, $params = array()){
    $pStrings  = $params = array_values($params);    
    array_walk($pStrings, create_function('&$item, $key', '$item = "\$params[{$key}]";'));
    $php = 'return new $className(' . implode(',', $pStrings) . ');';       
    return eval($php);    
}
?>
```


----------



## jans2 (2. September 2010)

Hätte nicht gedacht das das Thema so "lang" wird.. Nun mal zu euren ganzen Posts ..
also erstmal:
1. Ihr habt recht: eine wirkliche Mehrfachvererbung ist das ganze nicht
2. Es ist aber, in meinem Fall nicht wirklich das Problem -> bei mir gibt es keinen tüv 
3. Dein Code versteh ich nicht.. das ist mir zu hoch ^^

Danke für die posts !
jans2


----------

