PHP rocks! wünscht allen Mitgliedern einen guten Rutsch ins neue Jahr 2017 !!!
Hinweis: Das Forum zieht um! Um keine Datenverluste zu haben, schalten wir zwecks Übernahme der Daten das Forum am Sonntag, den 24.04.2016 um ca. 21:00 Uhr offline und passen anschliessend die DNS-Einträge an.
www.php-rocks.de wird euch dann nach den Aktualisierungen der DNS-Server wieder wie gewohnt uneingeschränkt zur Verfügung stehen.
Danke für euer Verständnis!

Themabewertung:
  • 0 Bewertung(en) - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Objekte aus Klassen instanziieren
#1
Hallo,

Ich suche gerade nach einem halbwegs eleganten Weg, aus einem Array mit Controller-Namen jeweils eine Instanz dessen zu erzeugen.

Beispiel:
Es existiert ein Array, in der Form:
Code:
Array(
   [0] => ClassName1,
   [1] => ClassName2,
   ...
   [n] => ClassNameN
)

Daraus möchte ich bspw. über eine Iteration etwas in der Art machen:
PHP-Code:
foreach ( $aAssemblies as $assembly ) {

 
   ${$assembly} = new $assembly;


Die dahinter verborgenen Klassen sind lediglich kleine Helper, die ich bei Bedarf in das Projekt einbinde, daher ist es nicht erforderlich den Bezeichner zur Laufzeit zu kennen.

Was besseres, wie variable Variabeln fällt mir allerdings adhoc leider nicht ein. Es funktioniert so, aber ich bin nicht sonderlich glücklich mit der Lösung.
Hat jemand vielleicht einen besseren Ansatz?

Danke
Antworten
#2
Moin

wäre die Reflection-API vielleicht eine Lösung?

Zumindest böte diese den zusätzlichen Vorteil, falls in Zukunft noch Abhängigkeiten in deine Helferklassen dazu kommen sollten, du diese automatisch - wahlweise mit etwas Rekursion - instanzieren und in den Konstruktor deiner Helferklassen injezieren könntest.

Grüße

Scarabaeus
Antworten
#3
Hallo,

Danke Scarabeus, an ReflectionClass hatte ich auch kurz nachgedacht, aber wieder verworfen.
Im Prinzip hast Du aber Recht, ist vielleicht die schickere Lösung.

Die Helperklassen arbeiten prinzipiell alle gleich, haben nur unterschiedliche Aufgaben aber entsprechend per Interface erzwungene identische Methoden.
Mit der ReflectionClass ist das ganze nahezu ähnlich aber vermutlich etwas flexibler aufbaubar:
PHP-Code:
foreach ( $aAssemblies as $assembly ) {

 
   $o = new ReflectionClass$assembly );
 
   $o->run();

 
   $oSystem->perform$o->getData()->asArray() );


Danke für den Hinweis!

Was mich bei der ReflectionClass interessieren würde ist, wie teuer die gegenüber der "variable Variabeln" Lösung ist?
Oder kann man das vernachlässigen? Microoptimierungen interessieren mich nicht, dann würde ich kein PHP verwenden Smile

Gruß Arne

EDIT: Ein klein wenig umnachtet bin ich scheinbar noch. Wie führe ich denn eine Methode einer ReflectionClass aus?
Muss ich den Umweg über ReflectionMethod::invoke() gehen oder gibt es eine kürzere Lösung?
Antworten
#4
Also irgendwas kommt mir da komisch vor.

Ich habe nun eine Testdatei gemacht, die so aufgebaut ist:
PHP-Code:
class Test {

 
   public static $Identifier 'UniqueIdentifier';

 
   public function GetData() { return [1,2,3]; }

}

$assembly 'Test';

$rc = new ReflectionClass$assembly );
$sIdentifier $rc->getProperty'Identifier' )->getValue();
$aData $rc->getMethod'GetData' )->invoke( new $assembly ); 
Hier bekomme ich zumindest alle Daten, die ich benötige, also funktioniert alles.

Sobald ich aber den Code 1:1 (!) in meine Live-Datei kopiere ( auch die Testklasse habe ich dort mal eingesetzt ), kommt ein Fatal Error:
Ausgabe:
Undefined Property Identifier...

Der einzige Unterschied zum Live und Testsystem ist, dass im Live-System das Assembly über eine foreach-Iteration gesetzt wird und im Testsystem dieser Wert statisch ist.
Ich habe die Werte innerhalb der Iteration natürlich getestet und es kommt an der Stelle immer der korrekt Klassenname.
Die Property ist in der Klasse auch definitiv enthalten!

Ich könnte mir höchstens vorstellen, dass es noch am Autoload liegen könnte, denn das habe ich nur im Live-System, nicht in der Testdatei implementiert.
Aber wie kann ich das ganze dann nutzen, ohne auf Autoload verzichten zu müssen?

Danke
Arne
Antworten
#5
Mahlzeit,

hmmm...scheint mir etwas komplex zu sein. Darf ich dir folgendes vorschlagen:

PHP-Code:
$assembly     'Test';

$rc           = new ReflectionClass$assembly );
$testInstance $rc->newInstanceArgs([]);

$testInstance->GetData(); 

Ich weiß leider nicht ob ReflectionClass::newInstanceArgs() zwingend in der Test-Klasse einen Konstruktor voraussetzt. Aber ansonsten bekommste dadurch eine vollwertige Klasseninstanz mit der du weiterarbeiten kannst.

Was nun die Ressourcenbelastung bei Nutzung der ReflectionClass betrifft: Ich kann da nur aus meiner Erfahrung mit Laravel sprechen, da deren IoC-Container ebenfalls von der ReflectionClass gebrauch macht, um Instanzen zu bilden und zu injezieren. Die Performance wird - zumindest in den Anwendungen die ich bisher geschrieben habe - nicht sonderlich tangiert und das Memory Limit wird auch nicht allzu sehr belastet. Von daher würde ich behaupten, dass dieser Ansatz noch gut nutzbar bleibt.

Grüße
Antworten
#6
Hi Scarabeus,

Danke für Deine Hinweise und Vorschläge.
Es ist tatsächlich über newInstance() realisierbar, worauf ich hätte kommen können. Ich bin nicht über die Methode gestolpert...

Es wirkt insgesamt imho auch sauberer, wie die Lösung der "variablen Variablen":
PHP-Code:
foreach ( $aAssemblies as $assembly ) {

 
   $rClass = new ReflectionClass$assembly );
 
   $aData $rClass->newInstance()->GetData();


Das funktioniert dann auch in Verbindung mit Autoloading.


Danke und Gruß
Arne
Antworten
#7
Keine Ursache Smile

Wobei der Vollständigkeit halber die ReflectionClass eigentlich primär dazu gedacht ist, Informationen über eine (unbekannte) Klasse zu bekommen und nicht unbedingt um Instanzen von Klassen zu bilden. Aber Feature ist Feature würde ich mal sagen. Big Grin

Vielleicht wäre es auch noch eine Möglichkeit gewesen, innerhalb der Foreach-Schleife ein Array mit den erstellten Instanzen zu füllen und als Schlüssel den Namen der Klasse zu nutzen. So umginge man zumindest die Geschichte mit den variablen Variablen. Oder man hätte auch in eine andere Klasse das ArrayAccess-Interface implementieren können um eine Klasse wie ein Array zu nutzen.

Grüße
Antworten
#8
Für diesen Fall benötige ich das nicht zwingend. Es geht grob überschlagen darum, anhand bestimmter Tags in meinen Templates einen Controller zu instanziieren, seine Prozesse anzustoßen und das Ergebnis ( immer ein Array ) an den HauptController zu übergeben.

Bspw. steht in meinen Templates folgendes:
Code:
<dp:use controller="SomeController"/>
dann wird vom Parser das Assembly SomeController instanziiert und über den Konstruktor die Standard-Aufgaben durchgeführt.
Per GetData() hole ich mir dann die verarbeiteten Daten als Array und übergebe das wieder an den Parser zur weiteren Verarbeitung.

Das habe ich gemacht, um in kleineren Projekten ein kleines bisschen Templating-Logik implementieren zu können.
Bspw. kann ich auf die Weise im Template entscheiden, welcher Controller benötigt wird.

Für mich passt das soweit... Smile

Gruß
Antworten
#9
Mahlzeit

Zitat:Es geht grob überschlagen darum, anhand bestimmter Tags in meinen Templates einen Controller zu instanziieren, seine Prozesse anzustoßen und das Ergebnis ( immer ein Array ) an den HauptController zu übergeben.
Sowas würde ich aufgrund des EVA Prinzipes nicht wirklich machen wollen, es sein denn dass deine Daten, deine Templates und dein Parser alles zusammen in ein neues "Dokument" überführen und dann die Ausgabe erfolgt (ähnlich XSLT).


Zitat:Das habe ich gemacht, um in kleineren Projekten ein kleines bisschen Templating-Logik implementieren zu können.
Bspw. kann ich auf die Weise im Template entscheiden, welcher Controller benötigt wird.
Unter Templating-Logik verstehe ich eigentlich eher Ausgabensteuerung im Sinne von If-Else oder Foreach, While und dazwischen vielleicht mal ein Include. Auch würde anders herum ein besserer Schuh drauß, wenn der Controller entscheidet welches Template benötigt wird und komplexere Template-Logik in eine separate Presenter-Klasse ausgelagert wird, die vom Controller angesprochen wird.

Aber das wäre schon wieder Stoff für ein anderes Thema Smile

Grüße
Antworten
#10
(23.05.2016, 15:07)Arne Drews schrieb: Es wirkt insgesamt imho auch sauberer, wie die Lösung der "variablen Variablen":

PHP-Code:
foreach ( $aAssemblies as $assembly ) {

 
   $rClass = new ReflectionClass$assembly );
 
   $aData $rClass->newInstance()->GetData();



Ich sehe nicht so recht, was daran besser ist als an:

PHP-Code:
foreach ( $aAssemblies as $assembly ) {

    
$rClass = new $assembly;
    
$aData $rClass->GetData();


Antworten


Gehe zu: