Antworten auf deine Fragen:
Neues Thema erstellen

Vergleich zweier PHP-Skripte - Gibt es einen (funktionellen) Unterschied?

d3mueller

PC-Freak :D

Hi, also ich habe auf dieser Seite hier ein paar Tutorials gesehen. Unter anderem eben das Tutorial für ein Eventsystem. Ich hab es mir durchgelesen und den Code mal angeschaut und versucht zu verstehen.

Rein technisch verstehe ich den Code fast komplett. Aber warum er das so gemacht hat, verstehe ich manchmal nicht so ganz.

1.) Er arbeitet fast ausschließlich mit static Methoden und Variablen. Beim ersten mal durchlesen fand ich die Idee ziemlich gut, weil man dann keine Instanzen mehr erstellen muss. Aber dann bin ich auf diese Seite gestoßen, die schreibt, dass man halt mit static eher vorsichtig umgehen soll (aus meiner Meinung nach sehr nachvollziehbaren Gründen). Ist das auch hier der Fall, dass man lieber ohne static arbeiten sollte?

2.) Eine essentielle Sache an diesem Eventsystem verstehe ich gar nicht. Und zwar benutzt er eine Methode, um Events zu registrieren. Diese registrierten Events werden dann in der Klasse in einer static Variable gespeichert, damit der eventHandler später weiß, welche Methode oder Funktion er bei einem Event aufrufen muss.

Dann hat er zusätzlich noch ein Array mit den Events (das man auch aus der Datenbank z.B. holen kann). Diese kann er dann ausführen und durcharbeiten lassen

Aber wie funktioniert das in der Praxis? Woher kommen in der Praxis diese "zu registrierenden" Events(damit meine ich nicht die Events Ansicht, sondern das, was per "registerEvent" registriert werden muss), die in der Klasse gespeichert werden? Wo liest man die aus? Was bringt einem das außer unnötiger Arbeit? Wenn die unveränderlich sind und allgemeingültig (Also das für JEDES Truppenbewegungs-Event Skript A aufgerufen wird z.B.) sind, dann kann man die doch direkt in der Klasse speichern, ohne dass man sie erst registrieren muss.

Ich versteh den Sinn dahinter nicht. Aber es muss doch einen geben, oder? Ich mein, ich glaube kaum, dass ich mehr von PHP verstehe als der Autor dieser Seite.

Ich habe mal versucht, so was selbst zu schreiben und das ist meine Lösung:

PHP:
// Enthält einfach Verbindungsdaten und -methoden für die Datenbank. Enthält auch die Klasse DatabaseBased, die das Datenbank-Objekt beinhaltet, um es in mehreren Klassen verwenden zu können
include("database.php");


class EventScripts extends DatabaseBased {
   
    public function ScriptA($callTime, $params) {
        //print_r($this->database->query("SELECT * FROM test", "assoc"));
        // Hier werden dann Berechnungen angestellt etc...
        return $params;
    }
   
}


class EventSystem extends DatabaseBased {
   
    // Ich glaube, hier wäre static nicht mal zwingend notwendig
    protected static $results;
   
    public function handleEvents($events) {
        // Instanz der EventScripts wird erzeugt (Als Parameter dient das Datenbank-Objekt)
        $EventScripts = new EventScripts($this->database);

        foreach ($events as $key => $event) {
            // Momentan wird hier immer dieselbe Methode aufgerufen, aber im $events-Array sind die Daten für andere Methoden
            // Das $results-Array werde ich noch überarbeiten, dass man später auch sieht, was das für ein Event war
            self::$results[] = $EventScripts->ScriptA(4,"testparameter");
        }           
    }
   
    // Sortiert nach Datum. Ältestes zuerst
    public function eventSort($a, $b) {
        $subt = $a['time'] - $b['time'];
       
        if ($subt == 0) {
            // Umgedreht, da je höher, desto weiter vorne in der Abarbeitungsliste
            return $b['importance'] - $a['importance'];
        } else {
            return $subt;
        }
    }
   
    public function getResults() {
        return self::$results;
    }
   
   
}

try {
    $database = new Database(/* Datenbankverbindungen */ );
    $EventSystem = new EventSystem($database);
} catch (Exception $e) {
    echo $e->getMessage();
}

// Testevents
$events = array(
array("id" => 2, "name" => "Nachricht Senden", "time" => 1, "class" => "EventScripts", "method" => "ScriptA", "importance" => 1, "params" => "Funktion4"),
array("id" => 1, "name" => "Nachricht Senden", "time" => 5, "class" => "EventScripts", "method" => "ScriptA", "importance" => 1, "params" => "Funktion1"),
array("id" => 2, "name" => "Nachricht Senden", "time" => 5, "class" => "EventScripts", "method" => "ScriptA", "importance" => 3, "params" => "Funktion2"),
array("id" => 2, "name" => "Nachricht Senden", "time" => 5, "class" => "EventScripts", "method" => "ScriptA", "importance" => 5, "params" => "Funktion3"));
usort($events, array("EventSystem", "eventSort"));

$EventSystem->handleEvents($events);

print_r($EventSystem->getResults());

Meine Frage ist jetzt? Hat meine Version die gleichen Möglichkeiten wie die von der Seite? Ich hab es halt so geschrieben, dass man nicht immer die Events registrieren muss, sondern einfach die Methoden-Namen im events-Array speichert, welches man aus der Datenbank ausliest. So ist es doch viel einfacher.

Ein Unterschied ist der, dass man mit meiner Version nicht mehrere Methoden in einem einzigen Event aufrufen kann. Aber das Bedarf nur einer kleinen Änderung, und schon würde das auch gehen (ob man das braucht ist eine andere Frage).

Was bei mir auch noch ist: Er lässt momentan alle events durchlaufen, auch wenn sie in der Zukunft lägen. Aber das ist auch eine winzige Änderung, zu der ich noch nicht gekommen bin.


Stehe ich jetzt vollkommen auf dem Schlauch und vergesse was wichtiges, oder funktioniert mein Skript genauso und ist gefühlt nur halb so lang (was ich irgendwie bezweifle)?

Danke schon mal für eure Hilfe,

Lg
 

Duddle

Posting-Frequenz: 14µHz

Ist das auch hier der Fall, dass man lieber ohne static arbeiten sollte?
Ein Satz in dem verlinkten Artikel ist wichtig:
Static methods are useful as utility methods
Statische Methoden sind dann sinnvoll, wenn sie ohne Bezug auf ein bestehendes Objekt verstanden werden können, aber trotzdem in den Kontext passen. gibt es in Projekten eine direkte Util-Klasse, die alle möglichen Hilfsfunktionen anbietet. Diese sind i.d.R. statisch, weil eine konkrete Util-Instanz keinen Mehrwert bringt.
Auf der anderen Seite wäre bspw. eine statische getColor()-Methode in einer Klasse Spielzeug unsinnig, weil nur ein konkretes Spielzeug eine Farbe haben und nennen kann.

In dem Eventsystem ist es meiner Ansicht nach nicht so eindeutig. Ein MyEventSystem-Objekt wäre auch kein Beinbruch, insbesondere wenn es mehrere Eventsysteme geben soll (oder als Singleton, wenn es explizit nur eines geben kann). Es widerspricht auch nicht dem verlinkten Artikel, da dieser über eine andere, nachteilige Anwendung von statischen Methoden spricht.

Ich versteh den Sinn dahinter nicht.
Du musst zwischen den EventHandlern und den eigentlichen Events unterscheiden. Im Code ist die Bezeichnung nicht eindeutig genug, aber prinzipiell merkt sich $events die EventHandler, also "was muss getan werden, wenn folgender Event auftritt?"

Beispielsweise wäre ein Event "ein Kunde bestellt Pizza", der Eventhandler "PizzaBestellungHandler". Der Event liefert als Information, wer welche Pizza wann haben will. Der Eventhandler wiederum weiss, was er mit diesen Informationen anstellen muss: mach Pizza X, liefer X zu Zeit Y an Person Z, verlange PreisVon(X) von Z. In diesem Beispiel wäre es dann auch wohl aus Gründen der Wiederverwendbarkeit besser, mehrere Eventhandler zu erstellen: PizzaMachenHandler, LieferungHandler, AbrechnungHandler - wobei PizzaMachenHandler am Ende selbst einen Event wie "Pizza X ist fertig" melden würde, welche das EventSystem an den LieferungHandler schicken würde.

Die MyEventSystem-Klasse ist dann nur dafür da, die Events den korrekten Eventhandlern zuzuweisen.
Hat meine Version die gleichen Möglichkeiten wie die von der Seite?
Nein, weil
Ich hab es halt so geschrieben, dass man nicht immer die Events registrieren muss
Die MyEventSystem-Klasse registriert aber keine Events, sondern EventHandler. Auch ist die Gameseal-Lösung weitaus dynamischer, weil zur Laufzeit problemlos neue EventHandler im Verwaltungssystem angemeldet werden können.


Duddle, welcher vor ein paar Minuten eine Pizza bestellt hat
 

d3mueller

PC-Freak :D

Ah okay, danke :) So verstehe ich es schon eher denke ic. Also wird für jedes Event ein eigener EventHandler registriert? Oder gibt es so "Standard-EventHandler", also z.B. EventHandler, die für alle "Sende Nachricht" -Events verwendet werden?


Und wo werden diese EventHandler-Informationen dann gespeichert und ausgelesen? Auch aus einer weiteren Datenbanktabelle?

Weil jedes mal bevor man die Events durchlaufen lassen kann, muss man sie ja erst mal registrieren, oder? Und da man in der Praxis dann ja unbekannt viele Events hat, muss das dann mit ner foreach geschehen
 

Duddle

Posting-Frequenz: 14µHz

Und wo werden diese EventHandler-Informationen dann gespeichert und ausgelesen? Auch aus einer weiteren Datenbanktabelle?
Schau in handleEvent():
PHP:
//Auszug:

// Alle registrierten Funktionen/Methoden nacheinander ausführen
foreach(self::$events[$name] as $event)
{
$result = call_user_func(array($event['class_name'], $event['function_name']), $time, $params);
}
Dann schau dir http://de2.php.net/manual/de/function.call-user-func.php an. Das EventSystem macht nichts weiter, als im $events-Array alle Einträge mit dem Namen des Events nacheinander aufzurufen, wobei ein Eintrag die notwendigen Informationen (welche Klasse? welche Funktion in dieser Klasse? etc.) hat, also auf den EventHandler verweist.
Wird der EventHandler wie hier registriert
PHP:
MyEventSystem::registerEvent('Truppenankunft', 'scriptA', 'EventScripts1', 1);
dann steht in MyEventSystem::$events["Truppenankunft"] eben, dass Methode scriptA von EventScripts1 aufgerufen werden soll. Die restlichen Parameter kommen im eigentlichen Event mit, siehe später
PHP:
array('id'=>1, 'time'=>1, 'name'=>'Truppenankunft', 'description'=>'Ankunft der Truppen im Heimatdorf', 'params'=>array('soldiers'=>763, 'gold'=>10)),

Weil jedes mal bevor man die Events durchlaufen lassen kann, muss man sie ja erst mal registrieren, oder? Und da man in der Praxis dann ja unbekannt viele Events hat, muss das dann mit ner foreach geschehen
Nochmal: du musst genauer zwischen Event (Ereignis) und dem EventHandler (Ereignis-Behandlung) unterscheiden. Es werden nur die EHs registriert, die Events sind einfach nur Nachrichten, die an MyEventSystem geschickt werden.

Edit:
Oder gibt es so "Standard-EventHandler", also z.B. EventHandler, die für alle "Sende Nachricht" -Events verwendet werden?
Das kannst du selbst definieren, wie du willst. Die Aufgabe von MyEventSystem ist ja genau diese: Events korrekt verarbeiten. Was das dann für unbekannte Events o.ä. bedeutet, legst du selbst fest.


Duddle
 
Zuletzt bearbeitet:

d3mueller

PC-Freak :D

Äh ja, das meinte ich auch, man muss für jedes Event erstmal ein EventHandler registrieren.

Aber ich wollte wissen, woher diese Zeile kommt:
PHP:
MyEventSystem::registerEvent('Truppenankunft', 'scriptA', 'EventScripts1', 1);

Im Skript hat er die ja einfach nacheinander notiert und sie werden dann registriert. Aber im "Alltag" weißt du ja nicht, welche Events gerade in der Datenbank sind, somit weißt du auch nicht, welche EventHandler du registrieren musst. Das heißt, diese EventHandler müssen auch irgendwo in der Datenbank gespeichert werden, oder nicht?
 

Duddle

Posting-Frequenz: 14µHz

Du hast eine vorher definierte Menge an möglichen Events. Deine Applikation selber erzeugt sie ja, also weisst du welche es geben kann. Diese fängst du ab.
Erweiterst du deine Applikation, dann definierst du (hoffentlich) gleichzeitig die notwendigen Handler für die neuen Events.

Es gibt übrigens auch andere Wege, dieses Problem zu lösen, die gezeigte Lösung ist nicht unbedingt das non plus ultra. Zum Beispiel könnte sich jeder EventHandler bei allen für ihn "interessanten" Klassen registrieren (sofern diese ein entsprechendes Interface implementieren). Passiert dann etwas in dieser Klasse, schickt sie eine Mitteilung an alle lauschenden Objekte, alá "hier ist etwas passiert: Event FooBar mit Wert 42". Dann kann der EventHandler entscheiden, ob dieser Event für ihn relevant ist und entsprechend handeln.

Allgemein empfehle ich die Recherche nach Entwurfsmustern, wenn du dich sowieso mit OOP auseinandersetzt. Der gerade beschriebene Weg wäre z.B. ein Observer. Die Klasse MyEventSystem ähnelt einem Mediator.


Duddle
 

d3mueller

PC-Freak :D

Du hast eine vorher definierte Menge an möglichen Events. Deine Applikation selber erzeugt sie ja, also weisst du welche es geben kann. Diese fängst du ab.
Erweiterst du deine Applikation, dann definierst du (hoffentlich) gleichzeitig die notwendigen Handler für die neuen Events.

Das heißt also, es gibt EventHandler für für bestimmte Events. Ich mein damit folgendes:

Es gibt keine Events, wie:
name=
NachrichtSendenEins oder
NachrichtSendenZwei oder
NachrichtSendenAnIrgendWen

Sondern nur:

name=
NachrichtSenden

und das einzige, was sich bei unterschiedlichen Events unterscheidet sind die enthaltenen Informationen. Das heißt bei meinem Beispiel hier hätte ich nur einen EventHandler für:
NachrichtSenden

und nicht 3 EventHandler für :
NachrichtSendenEins oder
NachrichtSendenZwei oder
NachrichtSendenAnIrgendWen

Stimmt das?

Sorry, dass ich jetzt so blöd frage, aber das verwirrt mich gerade ziemlich^^

Es gibt übrigens auch andere Wege, dieses Problem zu lösen, die gezeigte Lösung ist nicht unbedingt das non plus ultra. Zum Beispiel könnte sich jeder EventHandler bei allen für ihn "interessanten" Klassen registrieren (sofern diese ein entsprechendes Interface implementieren). Passiert dann etwas in dieser Klasse, schickt sie eine Mitteilung an alle lauschenden Objekte, alá "hier ist etwas passiert: Event FooBar mit Wert 42". Dann kann der EventHandler entscheiden, ob dieser Event für ihn relevant ist und entsprechend handeln.

Hm, das klingt auch interessant. Werde ich mir mal anschauen, danke :)
 

Duddle

Posting-Frequenz: 14µHz

Ja.

Als Beispiel sind
PHP:
array('id'=>1, 'time'=>1, 'name'=>'Truppenankunft', 'description'=>'Ankunft der Truppen im Heimatdorf', 'params'=>array('soldiers'=>763, 'gold'=>10)),
array('id'=>2, 'time'=>13, 'name'=>'Truppenankunft', 'description'=>'Ankunft der Truppen in Berlin', 'params'=>array('soldiers'=>5000, 'gold'=>90)),
array('id'=>3, 'time'=>19, 'name'=>'Truppenankunft', 'description'=>'Ankunft der Truppen in Dortmund', 'params'=>array('soldiers'=>2, 'gold'=>42)),
drei verschiedene Events, die allesamt vom EventHandler für "TruppenAnkunft" verarbeitet werden.


Duddle
 

d3mueller

PC-Freak :D

Okay, dann habe ich das glaube ich jetzt einigermaßen verstanden :)

Ich habe mein Skript jetzt auch mit static umgeschrieben, was aber ein Problem verursacht: Die Datenbankanbindung.

Ohne static war es kein Problem, weil ich beim erstellen einer Instanz von EventSystem das DatenbankObjekt (auch vorher instanziert) gleich "mitgebe" sozusagen.

Jetzt erstelle ich ja keine Instanz mehr. Ist es sinnvoll, so eine Datenbank-Klasse auch static zu machen? Wenn ja, dann könnte ich z.B. am Anfang der handleEvent()-Methode eine Verbindung öffnen und wenn alle Events durch sind, kann ich die Datenbankverbindung schließen. Würde das so gehen? Weil dann hätten die Skripte, die dann über die EventHandler aufgerufen werden, Zugriff zur Datenbank.
 

Duddle

Posting-Frequenz: 14µHz

Ist es sinnvoll, so eine Datenbank-Klasse auch static zu machen? Wenn ja, dann könnte ich z.B. am Anfang der handleEvent()-Methode eine Verbindung öffnen und wenn alle Events durch sind, kann ich die Datenbankverbindung schließen. Würde das so gehen? Weil dann hätten die Skripte, die dann über die EventHandler aufgerufen werden, Zugriff zur Datenbank.
Exakt der Fall wird in dem von dir verlinkten Artikel beschrieben - und zu Recht kritisiert.

Was spricht dagegen, das Datenbankobjekt beim Aufruf einer der statischen Methoden mitzugeben? Oder es entsprechend zu holen, wenn die Klasse ein Singleton ist.


Duddle
 

d3mueller

PC-Freak :D

Hm, stimmt das wäre eine Möglichkeit. Dann müsste ich halt überall immer das Datenbank-Objekt mitgeben, auch bei den einzelnen Skripten, die von EventHandler aufgerufen werden.

Um das zu vereinfachen, würde folgendes gehen?

Ich habe eine Datenbank-Klasse, von der ich eine Instanz erstelle: $database

Dann habe ich eine zweite Klasse namens DatabaseBased, die alle anderen Klassen von EventSystem und so erweitert. In dieser Klasse befindet sich eine statische, protected variable $database, die von der statischen Methode "public static function setDatabase(Database $database)" gesetzt wird. Diese Methode kann ich dann statisch, entweder in der __construct()-Methode der Database-Klasse aufrufen oder einfach nachdem ich die Instanz erstellt habe. So kann ich dann in jeder der Event-Klassen die Datenbank über self::$database aufrufen.

Kann man das so machen? Weil wenn es Probleme mit der Datenbank gibt, dann kann ja auch die setDatabase-Methode nicht ausgeführt werden, weil der übergebene Parameter keine Database-Instanz ist. Oder?

Was mir dagegen einfällt, ist dann man dann immer noch manuell sozusagen eine Verbindung aufbauen muss, und wenn man es nicht macht, dann passiert das, was auf der verlinkten Seite ja beschrieben wird. Könnte man das nicht umgehen, indem man z.B. in der setDatabase-Methode gleich eine Verbindung aufbaut und nötigenfalls eine Fehlermeldung ausspuckt?
 

Duddle

Posting-Frequenz: 14µHz

Du scheinst die Sache viel zu kompliziert anzugehen. Klassen sollten allgemein lose gekoppelt sein. Du baust starke Abhängigkeiten ein, wenn du plötzlich Vererbungen mit nur vage zusammenhängenden Klassen implementierst.

Wenn es eine Klasse Datenbank gibt, dann sollte auch nur diese für die Datenbank-relevanten Abläufe zuständig sein. Natürlich kannst du innerhalb anderer Klassen die Datenbank benutzen, aber eben nur benutzen - und nicht von ihr erben o.ä. (es sei denn, es sind tatsächlich Kindklassen einer Datenbank).

Dein Programm sollte so entworfen werden, dass du in der Implementation nicht darüber nachdenken musst, ob in Klasse Foo schon das darin benutzte Objekt x der Klasse Bar korrekt initialisiert wurde.

Dein EventSystem sollte wohl auch garnichts mit der Datenbank machen müssen (obwohl ich dein Design bzw. deine Featureliste nicht kenne). Das EventSystem delegiert lediglich Events an EventHandler, nichts weiter. Falls der EventHandler selbst dann in die Datenbank muss, dann muss die Datenbank eben dem EventHandler bekannt gemacht werden.

Nichtsdestotrotz:
Kann man das so machen?
Ja, kannst du. Ob es eine gute Idee ist, ist eine andere Frage.


Duddle
 

d3mueller

PC-Freak :D

Danke :)
Habe es jetzt denke ich einigermaßen hinbekommen. Nur was mir jetzt noch auffällt, wenn ich über einzelne Events nachdenke, ist folgendes:

Angenommen, ich habe ein Event in der Datenbank, welches in 2 Minuten ausgeführt werden soll und welches ein Gebäude auf den Status "Wird aufgerüstet" setzt und dann ein weiteres Event (um das Gebäudelevel dann um 1 zu erhöhen und den Status zurückzusetzen) 30 Minuten später erzeugt und in der Datenbank speichert. Was ist aber, wenn der Spieler die Seite dann erst nach 2 Stunden aktualisiert?

Dann wird ja erst nachträglich das erste Event durchgerechnet, das dann das Gebäude auf "wird aufgerüstet" setzt. Dann erstellt er zwar ein weiteres Event für in 30 Minuten und speichert es in der Datenbank, aber das Event wird ja dann nicht mehr in dem Durchgang ausgeführt, obwohl der auslösezeitpunkt immernoch in der Vergangenheit liegt. Aber Die Events wurden ja schon längst ausgelesen und deshalb weiß das Skript ja nicht, dass noch ein Event hinzugekommen ist.

Ich hoffe, du versteht, was ich meine. Und ja, vermutlich ist es nicht sinnvoll, ein Event zu machen, das das Gebäude auf "Aufrüsten" setzt, aber das soll die Problematik jetzt nur verdeutlichen, wenn innerhalb eines Events weitere Events erstellt werden, deren Auslöser aber immer noch in der Vergangenheit liegen
 

Duddle

Posting-Frequenz: 14µHz

Aber Die Events wurden ja schon längst ausgelesen und deshalb weiß das Skript ja nicht, dass noch ein Event hinzugekommen ist.
Das passiert nur so, wenn du es so programmierst. Es spricht nichts dagegen, die Warteschlange nach jeder Eventbehandlung neu zu lesen.


Duddle
 

d3mueller

PC-Freak :D

Auch wenn man dann jedes Mal die Daten neu aus der Datenbank holen muss? Weil die Daten direkt in das $events-Array zu packen geht ja nicht so einfach aus einer Methode in einer Klasse heraus. Und das würde doch ziemlich auf die Performance gehen, wenn man ständig neue Datenbankzugriffe hat.

Oder meinst du es anders, das man es nur "lokal" sozusagen neu ausliest?
 

Duddle

Posting-Frequenz: 14µHz

Denk noch nicht über sowas nach, sondern rein über dein Design. Erst wenn das robust ist kommt die Implementation, in der du dir Gedanken über Datenstrukturen, Algorithmen, Performance usw. machst.


Duddle
 
Bilder bitte hier hochladen und danach über das Bild-Icon (Direktlink vorher kopieren) platzieren.
Antworten auf deine Fragen:
Neues Thema erstellen

Willkommen auf PSD-Tutorials.de

In unseren Foren vernetzt du dich mit anderen Personen, um dich rund um die Themen Fotografie, Grafik, Gestaltung, Bildbearbeitung und 3D auszutauschen. Außerdem schalten wir für dich regelmäßig kostenlose Inhalte frei. Liebe Grüße senden dir die PSD-Gründer Stefan und Matthias Petri aus Waren an der Müritz. Hier erfährst du mehr über uns.

Stefan und Matthias Petri von PSD-Tutorials.de

Nächster neuer Gratisinhalt

03
Stunden
:
:
25
Minuten
:
:
19
Sekunden

Neueste Themen & Antworten

Flatrate für Tutorials, Assets, Vorlagen

Zurzeit aktive Besucher

Statistik des Forums

Themen
118.565
Beiträge
1.538.066
Mitglieder
67.488
Neuestes Mitglied
Andrew56524
Oben