Diskussion / Test: Beliebig erweiterbare Klassen

Hallo ihr beiden,

ich habe mich jetzt ebenfalls mit diesem Thema näher auseinander gesetzt und eine eigene Variante geschrieben, welche eines der beiden Probleme von Zodiac löst: die argumentlosen Methoden und die klassenübergreifende Variablendeklaration.

ExtensionStorage
PHP:
<?php
    class ExtensionStorage {
        private static $extensions = array();

        public static function get($extensionable) {
            if(isset(self::$extensions[$extensionable]))
                return self::$extensions[$extensionable];
            return false;
        }

        public static function has($extensionable) {
            return isset(self::$extensions[$extensionable]);
        }

        public static function extend($extensionable, $extension) {
            self::$extensions[$extensionable][$extension] = $extension;
        }
    }
?>
Registry
PHP:
<?php
    class Registry {}
?>
Extensionable
PHP:
<?php
    class Extensionable {
        private $__methods = array();
        private $__variables = null;

        public function __construct($variables = null) {
            if($variables instanceof Registry)
                $this->__variables = $variables;
            else
                $this->__variables = new Registry;
            $this->__get_parent(get_called_class());
        }

        private function __get_parent($parent) {
            if($extensions = ExtensionStorage::get($parent)) {
                foreach($extensions as $extension) {
                    if(ExtensionStorage::has($extension)) {
                        $this->__get_parent($extension);
                    }
                    else {
                        if(($methods = get_class_methods($extension)) !== null && $methods !== array()) {
                            $instance = new $extension($this->__variables);
                            foreach($methods as $method) {
                                if($method[0] . $method[1] === '__')
                                    continue;
                                if(isset($this->__methods[$method]))
                                    throw new Exception('Cannot redeclare method "' . $method . '"');
                                else
                                    $this->__methods[$method] = $instance;
                            }
                        }
                    }
                }
            }
        }

        public function __call($method, $parameters) {
            if(isset($this->__methods[$method]))
                return call_user_func_array(array($this->__methods[$method], $method), $parameters);
        }

        public function __get($name) {
            if(isset($this->__variables->{$name}))
                return $this->__variables->{$name};
            return null;
        }

        public function __set($name, $value) {
            $this->__variables->{$name} = $value;
        }
    }
?>

Und damit das Ganze auch noch hübsch zu erweitern ist:
PHP:
<?php
    function extend($extensionable, $extension) {
        ExtensionStorage::extend($extensionable, $extension);
    }
?>
Im Gegensatz zu der Variant Zodiacs erben alle angesprochenen Klassen von der Klasse Extensionable.
PHP:
<?php
    class A extends Extensionable {}
    class B extends Extensionable {}
    class C extends Extensionable {}

    extend('A', 'B'); # erweitert A um B
    extend('A', 'C'); # erweitert A um C
?>
Weiterhin bleibt das Problem, dass man dem Konstruktor keine spezielle Funktionalität zuweisen kann. Man könnte dies allerdings über ein Callback zu einem Alias-Konstruktor (beispielsweise "initialize") lösen, welchem man die Argumente des Konstruktors übergibt.
 
Zuletzt bearbeitet:
Damit schließt du aber auch nette Funktionen wie z.B. __toString() aus. Eine Sache, die ich sehr bedauere, denn ich nutze diese häufig in Templates um z.B. Forms und dergleichen zu erstellen.

Edit:
Oder auch so tolle Sachen wie __sleep() oder __wakeup() zum automatischen serialisieren und deserialisieren.
 
Zuletzt bearbeitet:
Nun gut, ich habe nur der Einfachheit wegen die Prüfung des Methodennamen über die zwei Unterstriche am Namensanfang vorgenommen, was aber letztendlich kein Problem sein sollte dies zu ändern, um auch andere magische Methoden zu erlauben. Diese würde ich dann aber anders implementieren, als über die Methode __call(). Aber davon abgesehen, wie gefällt dir diese Lösung?
 
Sieht nett aus, funktioniert auch. Aber mir ist beim Testen grad was aufgefallen:

Ich bin ein sehr fauler Entwickler und lasse meine IDE viel erledigen. Das heißt, ich nutze Code-Hinting und Auto-Completion in Eclipse. Mit dieser Art der - ich nenn es einfach mal - umgekehrten Vererbung habe ich diese Vorzüge leider nicht mehr. Ich muss immer wissen, was meine Klasse kann und was alle Klassen können, die die derzeitige Klasse erweitern. Für den Entwicklungsprozess ist das etwas hinderlich.

Bitte nicht falsch verstehen, ich will es nicht kaputt und zu Tode diskutieren, ich zeige nur Nachteile auf die durch dieses Vorgehen entstehen.

Das bedeutet nicht, das der Ansatz schlecht wäre.
 
Zurück