User Tools

Site Tools


php:kompost:multiextends

[PHP] Multi Extends

Es ist mehr eine Spielerei. Es geht dabei darum, dass man eine Klasse bilden kann, die aus mehr als einer Parent-Klasse abgeleitet ist. Mit Der unten Aufgeführten Klasse 'AbstractMultiExtends' lässt sich dies erstellen


Ausgangslage


Voraussetzungen

Die folgenden Funktionen werden zusätzlich verwendet PhpErrorsAsExceptions PhpCreateUserObject PhpVarDumpAsHtmlcomment

Code

Kleines Testspiel

exampleMultiExtends.php
<?php
/**
* mpl           by ERB software
* @author       stefan.erb(at)erb-software.com
*/
require_once(dirname(__FILE__).'/AbstractMultiExtends.php');
 
/**
 * ============================
 * Beispiel
 * ============================
 */
// Erste Parentklasse
class Mutter{
	protected $name = 'Vreni';
	public function __construct(){}
	public function gibEssen(){return 'Apfel'; }
	static 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;}
	static public function gibGeld($betrag){return "{$betrag} Franken"; }
	static 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 werden im Constructeur angehängt
//Das simuliert das Verhalten von 'class Kind extends Mutter, Vater{'
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
		//Parent definiert wurde, hat sie vorrang
		return $this->sagHallo('Max Muster');
	}
	public function getName(){
		return "Max {$this->nachname}";
	}
}
 
//Test
define('BRNL', "<br />\n");
 
$ich = new Kind();
echo $ich->name.BRNL;
echo $ich->gibEssen().BRNL;
echo $ich->gibGeld(10).BRNL;
echo $ich->getName().BRNL;
echo $ich->mutterSagtHallo().BRNL;
echo $ich->nachname;
 
/* Ausgabe:
Apfel
10 Franken
Max Muster
Mutter sagt 'Hallo Max Muster'
Muster
 */
?>

AbstractMultiExtends

AbstractMultiExtends.php
<?php
/**
* mpl           by ERB software
* @author       stefan.erb(at)erb-software.com
*/
//TODO Statische Aufrufe ebenfalls behandeln (__callStatic geht erst ab PHP 5.3)
//TODO Beim Aufruf prüfen ob die Methode im Original Protected ist
//TODO Bei doppelten Properties/Methodes bestimmen welcher Parent gilt
 
require_once(dirname(__FILE__).'/packages/functions/exceptionErrorHandler.php');
require_once(dirname(__FILE__).'/packages/functions/createUserObject.php');
require_once(dirname(__FILE__).'/packages/functions/varDumpAsHtmlcomment.php');
 
/**
 * ============================
 * Abstracte Klasse
 * ============================
 */
 
abstract class AbstractMultiExtends{
	private $methods = array();
	private $properties = array();
	private $objects = array();
 
	const C_METHOD = 'methods';
	const C_PROPERTY = 'properties';
 
	/**
	 * ein weitere ExtendesClasse hinzufügen
	 * @param String    $name   
	 * @param Array     $params
	 */
	final protected function addExtendedClass($className, $params = array()){
		//ReflectionClass erstellen
		$reflection = new ReflectionClass($className);        
		//Eine Wrapperklasse erstellen, damit die Protected-Methodes Public werden
		$wrapperClassName = self::createWrapperClass($reflection);
		//Falls nur ein Parameter angegeben wird, diesen in einen Array schreiben
		if(!is_array($params)) $params = array($params);
		//Objecktname definieren
		$objectName = self::getObjectName($wrapperClassName);
		//Eine Instance der Wrapperklasse anlegen und in den privaten Array speichern
		$this->objects[$objectName] = create_user_object_array($wrapperClassName, $params);        
		//Methodennamen der Klasse in einen privaten Array speichern
		$this->addItems($objectName, self::C_METHOD, $reflection->getMethods());
		//Propertiesnamen der Klasse in einen privaten Array speichern
		$this->addItems($objectName, self::C_PROPERTY, $reflection->getProperties());
	}
	/**
	 * 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
	 */
	final private function addItems($objectName, $list, $items){
		//nur items nehmen, die nicht private sind
		$items = array_filter($items, create_function('$item', 'return (!$item->isPrivate());'));
		//items auf den Namen reduzieren
		array_walk($items, create_function('&$item', '$item = $item->name;'));
		//Array mit den itemnamen als Key und dem Objektnamen als Value transferieren
		$newVars = array_fill_keys($items,  "\$this->objects['{$objectName}']");
		//items der itemliste hinzufügen. 
		//Bei Namensgleichheit mit bestehenden wird einer bestehende Zuordnung wird die 
		//Bestehende beibehalten
		$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){
		//Prüfen ob die Methode in der Liste der Methoden vorhanden ist      
		if(array_key_exists($name, $this->methods)){
			//dazugehöriges Elternobjekt auslesen            
			$objectPhp = $this->methods[$name];         
			$object = eval("return {$objectPhp};");
			//Methode im Elternobjekt ausführen
			return call_user_func_array(array($object, $name), $params);
		}
	}
 
	/**
	 * Aufruf eines Property. Der Aufruf wird an das entsprechende Parentobjekt weitergeleitet
	 * @param String $name
	 * @return Variant
	 */
	public function __get($name){
		//Prüfen ob das Property in der Liste der Properties vorhanden ist
		if(array_key_exists($name, $this->properties)){
			//dazugehöriges Elternobjekt auslesen
			$obj = $this->properties[$name];
			//Property auslesen
			return eval("return {$obj}->{$name};");
		}
	}
 
	/**
	 * @param String $className
	 * @return String
	 */
	static final private function getObjectName($className){return "obj{$className}";}
 
	/**
	 * @param String $className
	 * @return String
	 */
	static final private function getWrapperName($className){return "{$className}Wrapper";}
 
	/**
	 * Erstellen einer Wrapperlasse um die ParentClass
	 * @param String    $className
	 * @return String   Name der Wrapperklasse
	 */
	final private function createWrapperClass(&$reflection){
		$className = $reflection->name;
		//ReflectionObject der Klasse zur weiteren Analyse anlegen
		$wrapperName = self::getWrapperName($className);
		$objName = self::getObjectName($wrapperName);
		//Die Klasse zusammenstellen    
		$lines[] = 
<<<EOT
class $wrapperName extends $className{
	public function __construct(){
		\$pStrings  = \$params = func_get_args();    
		array_walk(\$pStrings, create_function('&\$item, \$key', '\$item = "\\\$params[\'{\$key}\']";'));
		eval('parent::__construct(' . implode(',', \$pStrings) . ');');
	}
EOT;
		//Die Methoden hinzufügen
		$this->createWrapperMethodes($lines, $reflection);
 
		//Properties hinzufügen
		$this->createWrapperProperies($lines, $reflection);
 
		//Klasse schliessen
		$lines[] = 
<<<EOT
	}
EOT;
		//Aus allen Zeilen ein String erstellen
		$classPhp = implode("\n", $lines);
var_dump_as_htmlcomment($classPhp);        
		//Die Klasse laden 
		eval($classPhp);
		return $wrapperName;
	}
 
	/**
	 * Erstellen der Wrappermethoden für die Wrapperklasse
	 * @param $lines
	 * @param $reflection
	 */
	final private function createWrapperMethodes(&$lines, ReflectionClass &$reflection){
		foreach($reflection->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[] =
<<<EOT
	{$modifiers} function {$method->name}({$paramString}){
		return parent::{$method->name}({$paramString});
	}        
EOT;
			}
		}
	}
 
	/**
	 * Erstellen der der __get() im Warapper um auf die Potected/Public-Properties der Parentklasse zuzugreiffen
	 * @param $lines
	 * @param $reflection
	 */
		final private function createWrapperProperies(&$lines, ReflectionClass &$reflection){
		$properties = array();
		foreach($reflection->getProperties() as $property){            
			if(!$property->isPrivate() && !$property->isStatic()) {
				$properties[] = "'{$property->name}'";
			}
 
		}
		$propertiesS = implode(', ', $properties);
		$lines[] = 
<<<EOT
	public function __get(\$name){
		\$protectedProperties = array({$propertiesS});
		if(in_array(\$name, \$protectedProperties)) return \$this->\$name;
	}
EOT;
	 }
}
?>

Kategorien

php/kompost/multiextends.txt · Last modified: 11.12.2013 11:48:12 (external edit)