KingCrunchs kleine Welt

Gedanken über PHP und was mich sonst bewegt…

"runtime class creation"

| Keine Kommentare

OK, gelungene Namen waren nie meine Stärke. Es geht darum Klassen speziell deren Bezeichner dynamisch zur Laufzeit zu definieren. Eben habe ich eine lang gepflegt Idee ausgetestet und es geht. Jetzt stellt sich mit allerdings die Frage, wie sinnvoll das ist.

class Array {}
eval ('class Array___Integer extends Array{}');
$bla = new Array___Integer();
echo get_class ($bla);

Ausgabe ist erwartungsgemäß "Array___Integer". Wozu aber? Es geht um die von mir hin und wieder schon mal beschworenen Type Hints. Type Hints funktionieren natürlich nur auf Klassen, die auch existieren. Existiert die Klasse nicht, gibt es zwar per se keinen Fehler, allerdings wird man kein Objekt erstellen können, dass den Type Hint erfüllt.

Das obige Beispiel sollte eigentlich schon andeuten, für was dynamisch erstellte Klassen sinnvoll sein können: Erstellt man sich ein Exemplar der Klasse Array, so ist der Typ der Elemente zwar (je nach Implementierung) fest und vorgegeben, man müsste allerdings manuell darauf prüfen, weil type Hints nicht rekursiv möglich prüfen (können).

Eine solche dynamische Generierung würde nun erlauben, dass man sich Arrays erstellt, die auch per Type Hint direkt auf deren Inhalt prüfen lässt. Ebenso kann nun das Array-Objekt anhand seines eigenen Namens den Typ seiner Elemente feststellen und nötigenfalls bei Verstoß Alarm schlagen. Und das alles für pauschal alle denkbaren Klassen, genauso wie für Arrays selbst.

Ich überlege mir jetzt erstmal eine schlüssige Implementierung, sowieso eine Lösung zu dem Problem Namenskonflikte zwischen realen und virtuellen Klassen zu umgehen. Die Variante mit 3 Unterstrichten wäre denkbar, muss aber nicht zwangsläufig kollisionsfrei sein.

Meine grundsätzliche Idee zur Implementierung arbeitet über ReflectionClass, weil damit eine Prüfung, ob die Klasse existiert bzw geladen werden kann, entfallen kann. So mal exemplarisch aussm Bauch raus:

$baseClass = new ReflectionClass ('Array');
$typeClass = new ReflectionClass ('Integer');
$typedArrayClass = Typed::getClass ($baseClass, $typeClass);
$typedArray = $typedArrayClass->newInstance();

Der Overhead ist nicht abzusehen, weil ich nicht so ganz abschätzen kann, wie oft so ein Vorgehen in einer typischen Applikation überhaupt auftritt ;) Ebenso muss dafür dringend ein Format für den Bezeichner der neuen Klasse definiert werden, weil wenn man nicht weiß, worauf man hinten soll, wird das Type-Hint etwas schwierig ;)

Nun am Ende nochmal ein funktionsfähiges Beispiel:

class ArrayClass {}
eval ('class ArrayClass___Integer extends ArrayClass{}');
$bla = new ArrayClass___Integer();
function fusel(ArrayClass___Integer $bla) {
    echo get_class ($bla);
}
fusel ($bla);

PS:

class Opl_util_SubType
{
    const SEPARATOR = '___';
    public static function create (ReflectionClass $base, ReflectionClass $subType) {
        $subTyped = $base->getName() . self::SEPARATOR . $subType->getName();
        if (!class_exists($subTyped)) {
            eval ("class $subTyped extends ".$base->getName()."{}");
        }
        return new ReflectionClass($subTyped);
    }
}

Die Klasse wird demnächst in das SVN-Repo aufgenommen.

Hinterlasse eine Antwort

Pflichtfelder sind mit * markiert.

*


Bad Behavior has blocked 126 access attempts in the last 7 days.

Page optimized by WP Minify WordPress Plugin