PHP Mehrfachvererbung

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:

PHP:
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 ;) )
 
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.
 
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:
<?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:
<?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);    
}
?>
 
Hätte nicht gedacht das das Thema so "lang" wird.. Nun mal zu euren ganzen Posts :D..
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 :D
3. Dein Code versteh ich nicht.. das ist mir zu hoch ^^

Danke für die posts !
jans2
 
Zurück