EventHandler

Dieser Bereich dient dazu, neue Features zu diskutieren und für die Entwicklung zu dokumentieren. // This area is dedicated to new features including proposals and documentation.

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 21.07.2011, 12:13:51

Ich hab mal grob etwas zusammengetextet.

Sessionweite oder persistente Events habe ich NICHT umgesetzt, das halte ich für zu speziell und kompliziert in dem Fall. Allerdings kann man das durch Erweiterung immer noch realisieren wenn nötig. Deshalb würde ich auch vorschlagen den EventDispatcher als DiServiceObject anzusprechen, damit dieser leicht durch eine erweiterte Version ausgetauscht werden kann.
Weiterhin habe ich noch nicht die Möglichkeit von Namespaces innerhalb der Events umgesetzt, weil ich da an der Logik gescheitert bin im ersten Anlauf, eventuell hat da von euch einer eine Idee wie man das realisieren kann.
Gedacht war, dass man wie in Javascript "EventName.SubName" angeben kann. Wenn man aber beliebig viele Level realisieren will ist die Umsetzung nicht mehr so ganz trivial.
Gefeuert werden können soll dann "EventName.SubName", aber auch nur "EventName", wobei dann alle Handler der Subevents angesprochen werden.

Hier mal mein grober Vorschlag, ungetestet:

Code: Alles auswählen

<?php
interface EventHandler {
    public function handle(Event &$Event);
}
?>

Code: Alles auswählen

<?php

/**
 * Description of Event
 *
 * @author Ralf Schubert 
 */
class Event {
    protected $Name;
    protected $Info;
    protected $Context;
    protected $Language = 'de';
    protected $Cancelled = false;
    
    public function __construct($Name, $Context, $Language = 'de', $Info = null) {
        $this->Name = $Name;
        $this->Info = $Info;
        $this->Context = $Context;
        $this->Language = $Language;
    }
    
    public function getContext() {
        return $this->Context;
    }

    public function setContext($Context) {
        $this->Context = $Context;
        return $this;
    }

    public function getLanguage() {
        return $this->Language;
    }

    public function setLanguage($Language) {
        $this->Language = $Language;
        return $this;
    }

    public function getName() {
        return $this->Name;
    }

    public function setName($Name) {
        $this->Name = $Name;
        return $this;
    }
    
    public function isCancelled() {
        return $this->Cancelled;
    }
    
    public function cancel() {
        $this->Cancelled = true;
        return $this;
    }
    


}

?>

Code: Alles auswählen

<?php

/**
 *
 * @author Ralf Schubert 
 * @version 0.1,  21.07.2011<br />
 */
class EventDispatcher extends APFObject {
    protected $Handlers = array();
    protected $Events = array();
    
    public function addHandler($EventName, EventHandler $Handler, $CatchPreviousEvents = true){
        if(!isset($this->Handlers[$EventName])){
            $this->Handlers[$EventName] = array();
        }
        $this->Handlers[$EventName][] = $Handler;
        
        // handle events which were fired before attaching this eventhandler
        if($CatchPreviousEvents){
            if(isset($this->Events[$EventName])){
                foreach($this->Events[$EventName] as $Event){
                    $Handler->handle($Event);
                }
            }
        }
        
        return $this;
    }
    
    public function &trigger($EventName, $Context = null, $Language = null, $Info = null){
        if($Context === null){
            $Context = $this->getContext();
        }
        if($Language === null){
            $Language = $this->getLanguage();
        }
        $Event = new Event($EventName, $Context, $Language, $Info);
        
        return $this->triggerEvent($Event);
    }
    
    public function &triggerEvent(Event &$Event){
        $Name = $Event->getName();
        
        // send event to all relevant eventhandlers
        if(isset($this->Handlers[$Name])){
            foreach($this->Handlers[$Name] as $Handler){
                $Handler->handle($Event);
                if($Event->isCancelled()){
                    break;
                }
            }
        }
        
        // save event for handlers which get attached later
        if(!$Event->isCancelled()){
            if(!isset($this->Events[$Name])){
                $this->Events[$Name] = array();
            }
            $this->Events[$Name] = $Event;
        }
        
        return $Event;
    }
    
}

?>
Was haltet ihr davon?


Ansiedeln würde ich das ganze übrigens in core::event, damit dass möglichst überall verwendet werden kann.

Well
Beiträge: 263
Registriert: 25.10.2009, 11:00:19
Wohnort: Beuren
Kontaktdaten:

Re: EventHandler

Beitrag von Well » 21.07.2011, 16:46:45

Halb-OT: Ist der Referenzoperator seit PHP 5 bei Objekten nicht überflüssig?

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 21.07.2011, 16:55:40

Bei lesendem Zugriff auf jeden Fall, aber ich bin mir nicht sicher wie das bei schreibendem Zugriff ist. Daher mach ichs sicherheitshalber immer mit ;)

Benutzeravatar
dr.e.
Administrator
Beiträge: 4527
Registriert: 04.11.2007, 16:13:53

Re: EventHandler

Beitrag von dr.e. » 21.07.2011, 20:28:43

Hallo Ralf,

danke für deinen Vorschlag! Könnte man sich dabei nicht vielleicht an der JS-Variante orientieren und dem EventDispatcher das Event plus dem jeweils auszuführenden Call am Target-Objekt mitgeben? Innerhalb eines Requests sollte das sicher möglich sein, Probleme sehe ich - und das ist gleichzeitig auch der Nachteil - bei Session-übergreifenden Events... :roll:

Was mir noch ein wenig fehlt ist der Gültigkeitsbereich von Events. Diese würde ich mit Request- und Session-Scope modellieren.
Viele Grüße,
Christian

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 21.07.2011, 21:07:00

danke für deinen Vorschlag! Könnte man sich dabei nicht vielleicht an der JS-Variante orientieren und dem EventDispatcher das Event plus dem jeweils auszuführenden Call am Target-Objekt mitgeben? Innerhalb eines Requests sollte das sicher möglich sein, Probleme sehe ich - und das ist gleichzeitig auch der Nachteil - bei Session-übergreifenden Events... :roll:
Du meinst dass man statt eines EventHandlers ein Objekt und einen Methodennamen übergibt, welcher aufgerufen wird?
Was mir noch ein wenig fehlt ist der Gültigkeitsbereich von Events. Diese würde ich mit Request- und Session-Scope modellieren.
Inwiefern Gültigkeitsbereich?

Megger
Beiträge: 1233
Registriert: 04.11.2008, 10:57:37

Re: EventHandler

Beitrag von Megger » 25.07.2011, 18:25:20

Paar kleine Fragen:
  • Warum ist bei EventDispatcher->trigger die Info Variable an letzter Stelle? Ich würde es sinniger finden, diese an die zweite Stelle zu holen, weil man übergibt einem Event doch häufiger Informationen, wobei Context und Language ruhig vom APFObject "übernommen" werden können
  • Wenn ich dass richtig sehe, dann habe ich keine Möglichkeit einen Handler zu registrieren, der auf alle Events reagiert? (Halte ich für sinnvoll um Events zu loggen, abzuspeichern, einen Soap Request auszuführen usw.)
Wirklich getestet habe ich das bisher allerdings noch nicht, aber was ich mir im Quellcode angeschaut habe finde ich sehr gut
danke für deinen Vorschlag! Könnte man sich dabei nicht vielleicht an der JS-Variante orientieren und dem EventDispatcher das Event plus dem jeweils auszuführenden Call am Target-Objekt mitgeben? Innerhalb eines Requests sollte das sicher möglich sein, Probleme sehe ich - und das ist gleichzeitig auch der Nachteil - bei Session-übergreifenden Events... :roll:
Das würde mich auch interessieren, wie genau das gemeint ist
Was mir noch ein wenig fehlt ist der Gültigkeitsbereich von Events. Diese würde ich mit Request- und Session-Scope modellieren.
Das verstehe ich auch nicht :D
Tutorial: Browsergame mit dem APF (Die ersten Parts handeln von Installation und Inbetriebnahme des APFs, deswegen sicherlich auch für alle Nicht-Browsergame-Programmierer interessant)

APF-Version
  • Entwicklung: 2.0
  • Produktiv: 1.15

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 25.07.2011, 18:30:37

Warum ist bei EventDispatcher->trigger die Info Variable an letzter Stelle? Ich würde es sinniger finden, diese an die zweite Stelle zu holen, weil man übergibt einem Event doch häufiger Informationen, wobei Context und Language ruhig vom APFObject "übernommen" werden können
Gutes Argument, da hab ich wohl nicht genauer drüber nachgedacht, das tauschen wir natürlich an 2. Stelle.
Wenn ich dass richtig sehe, dann habe ich keine Möglichkeit einen Handler zu registrieren, der auf alle Events reagiert? (Halte ich für sinnvoll um Events zu loggen, abzuspeichern, einen Soap Request auszuführen usw.)
Macht Sinn.
Sollen wir dazu einfach die Reihenfolge der Parameter von addHandler() tauschen, sodass der Handler an erster, und der Eventname an 2. Stelle steht, wobei letzterer optional wird?

Megger
Beiträge: 1233
Registriert: 04.11.2008, 10:57:37

Re: EventHandler

Beitrag von Megger » 26.07.2011, 10:46:15

Jep, dass sollte dann so funktionieren! Werde das ganze vielleicht mal im Laufe des Tages testen! Dann kann ich auch gleich mal schauen ob ich es schaffe damit meine 2 Systeme zu synchronisieren. Mal schauen ob das alles so klappt wie ich es mir vorstelle
Tutorial: Browsergame mit dem APF (Die ersten Parts handeln von Installation und Inbetriebnahme des APFs, deswegen sicherlich auch für alle Nicht-Browsergame-Programmierer interessant)

APF-Version
  • Entwicklung: 2.0
  • Produktiv: 1.15

Megger
Beiträge: 1233
Registriert: 04.11.2008, 10:57:37

Re: EventHandler

Beitrag von Megger » 26.07.2011, 14:49:44

Beim EventObject fehlen Getter und Setter für Info

Und wie schon per Skype besprochen:
Um EventHandler zu registrieren bzw. anzusprechen muss die addHandler und die triggerEvent Methode überarbeitet werden
Tutorial: Browsergame mit dem APF (Die ersten Parts handeln von Installation und Inbetriebnahme des APFs, deswegen sicherlich auch für alle Nicht-Browsergame-Programmierer interessant)

APF-Version
  • Entwicklung: 2.0
  • Produktiv: 1.15

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 26.07.2011, 15:54:56

Ok hier die 2 neuen Dateien, ich konnte es noch nicht testen, machst du das dann?

Code: Alles auswählen

<?php

/**
 * Description of Event
 *
 * @author Ralf Schubert 
 */
class Event {
    protected $Name;
    protected $Info;
    protected $Context;
    protected $Language = 'de';
    protected $Cancelled = false;
    
    public function __construct($Name, $Context, $Language = 'de', $Info = null) {
        $this->Name = $Name;
        $this->Info = $Info;
        $this->Context = $Context;
        $this->Language = $Language;
    }
    
    public function getContext() {
        return $this->Context;
    }

    public function setContext($Context) {
        $this->Context = $Context;
        return $this;
    }

    public function getLanguage() {
        return $this->Language;
    }

    public function setLanguage($Language) {
        $this->Language = $Language;
        return $this;
    }

    public function getName() {
        return $this->Name;
    }

    public function setName($Name) {
        $this->Name = $Name;
        return $this;
    }
    
    public function getInfo() {
        return $this->Info;
    }

    public function setInfo($Info) {
        $this->Info = $Info;
        return $this;
    }

    public function isCancelled() {
        return $this->Cancelled;
    }
    
    public function cancel() {
        $this->Cancelled = true;
        return $this;
    }
    


}

?>

Code: Alles auswählen

<?php

/**
 *
 * @author Ralf Schubert
 * @version 0.1,  21.07.2011<br />
 */
class EventDispatcher extends APFObject {
    protected $Handlers = array();
    protected $Events = array();
    
    public function addHandler(EventHandler $Handler, $EventName = 'all', $CatchPreviousEvents = true){
        if(!isset($this->Handlers[$EventName])){
            $this->Handlers[$EventName] = array();
        }
        $this->Handlers[$EventName][] = $Handler;
        
        // handle events which were fired before attaching this eventhandler
        if($CatchPreviousEvents){
            if($EventName === 'all'){
                $this->notifyHandlerRecursive($this->Events, $Handler);
            }
            elseif(isset($this->Events[$EventName])){
                foreach($this->Events[$EventName] as $Event){
                    $Handler->handle($Event);
                }
            }
        }
        
        return $this;
    }
    
    protected function notifyHandlerRecursive(array $Events, EventHandler $Handler){
        foreach($Events as $Event){
            if(is_array($Event)){
                $this->notifyHandlerRecursive($Event, $Handler);
            }
            else {
                $Handler->handle($Event);
            }
        }
    }
    
    public function &trigger($EventName, $Info = null, $Context = null, $Language = null){
        if($Context === null){
            $Context = $this->getContext();
        }
        if($Language === null){
            $Language = $this->getLanguage();
        }
        $Event = new Event($EventName, $Context, $Language, $Info);
        
        return $this->triggerEvent($Event);
    }
    
    public function &triggerEvent(Event &$Event){
        $Name = $Event->getName();
        
        // send event to all relevant eventhandlers
        if(isset($this->Handlers[$Name])){
            foreach($this->Handlers[$Name] as $Handler){
                $Handler->handle($Event);
                if($Event->isCancelled()){
                    break;
                }
            }
        }
        
        // send event to all handlers which listen to all events
        if(!$Event->isCancelled() && isset($this->Handlers['all'])){
            foreach($this->Handlers['all'] as $Handler){
                $Handler->handle($Event);
                if($Event->isCancelled()){
                    break;
                }
            }
        }
        
        // save event for handlers which get attached later
        if(!$Event->isCancelled()){
            if(!isset($this->Events[$Name])){
                $this->Events[$Name] = array();
            }
            $this->Events[$Name] = $Event;
        }
        
        return $Event;
    }
    
}

?>

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 05.08.2011, 11:41:18

danke für deinen Vorschlag! Könnte man sich dabei nicht vielleicht an der JS-Variante orientieren und dem EventDispatcher das Event plus dem jeweils auszuführenden Call am Target-Objekt mitgeben? Innerhalb eines Requests sollte das sicher möglich sein, Probleme sehe ich - und das ist gleichzeitig auch der Nachteil - bei Session-übergreifenden Events... :roll:
Du meinst dass man statt eines EventHandlers ein Objekt und einen Methodennamen übergibt, welcher aufgerufen wird?
Die Frage gilt immernoch ;)


Desweiteren fehlt mir noch eine Möglichkeit, Diese Eventhandler statisch zu registrieren. Mir schwebt da eine Configdatei vor, in welcher man Namespace und Name, sowie Funktionsname eines Objektes eintragen kann, zusammen mit dem entsprechenden Eventname, und wenn das Event gefeuert wird, wird das Objekt importiert, geladen und aufgerufen. Das könnte in vielen Anwendungen Performance sparen, bei Eventhandlern die nur in Spezialfällen gebraucht werden, da somit das Objekt nicht vorher erstellt und hinzugefügt werden muss.

Was haltet ihr von der Idee? (zusätzlich zu der bereits bestehenden sollte man das machen)

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 05.08.2011, 16:27:29

In Bezug auf meinen Vorherigen Beitrag:

Das 1. hab ich jetzt mal Umgesetzt, da ich den Eventhandler schon einsetze jetzt, und das mit der Handler definition doch etwas umständlich war. Neuer Code:

EventDispatcher.php

Code: Alles auswählen

<?php
import('core::event', 'Event');

/**
 *
 * @author Ralf Schubert
 * @version 0.1,  21.07.2011<br />
 */
class EventDispatcher extends APFObject {
    protected $Handlers = array();
    protected $Events = array();
    
    public function addHandler($Handler, $FunctionName, $EventName = 'all', $CatchPreviousEvents = true){
        if(!isset($this->Handlers[$EventName])){
            $this->Handlers[$EventName] = array();
        }
        $this->Handlers[$EventName][] = array($Handler, $FunctionName);
        
        // handle events which were fired before attaching this eventhandler
        if($CatchPreviousEvents){
            if($EventName === 'all'){
                $this->notifyHandlerRecursive($this->Events, $Handler, $FunctionName);
            }
            elseif(isset($this->Events[$EventName])){
                foreach($this->Events[$EventName] as $Event){
                    $Handler->$FunctionName($Event);
                }
            }
        }
        
        return $this;
    }
    
    protected function notifyHandlerRecursive(array $Events, $Handler, $FunctionName){
        foreach($Events as $Event){
            if(is_array($Event)){
                $this->notifyHandlerRecursive($Event, $Handler, $FunctionName);
            }
            else {
                $Handler->$FunctionName($Event);
            }
        }
    }
    
    public function &trigger($EventName, $Info = null, $Context = null, $Language = null){
        if($Context === null){
            $Context = $this->getContext();
        }
        if($Language === null){
            $Language = $this->getLanguage();
        }
        $Event = new Event($EventName, $Context, $Language, $Info);
        
        return $this->triggerEvent($Event);
    }
    
    public function &triggerEvent(Event &$Event){
        $Name = $Event->getName();
        
        // send event to all relevant eventhandlers
        if(isset($this->Handlers[$Name])){
            foreach($this->Handlers[$Name] as $HandlerData){
                $HandlerData[0]->$HandlerData[1]($Event);
                if($Event->isCancelled()){
                    break;
                }
            }
        }
        
        // send event to all handlers which listen to all events
        if(!$Event->isCancelled() && isset($this->Handlers['all'])){
            foreach($this->Handlers['all'] as $HandlerData){
                $HandlerData[0]->$HandlerData[1]($Event);
                if($Event->isCancelled()){
                    break;
                }
            }
        }
        
        // save event for handlers which get attached later
        if(!$Event->isCancelled()){
            if(!isset($this->Events[$Name])){
                $this->Events[$Name] = array();
            }
            $this->Events[$Name] = $Event;
        }
        
        return $Event;
    }
    
}

?>
Am Event Objekt hat sich nichts geändert, das EventHandler interface ist weggefallen.
Das funktioniert so, habs getestet.

Bleibt diese Frage übrig:
Desweiteren fehlt mir noch eine Möglichkeit, Diese Eventhandler statisch zu registrieren. Mir schwebt da eine Configdatei vor, in welcher man Namespace und Name, sowie Funktionsname eines Objektes eintragen kann, zusammen mit dem entsprechenden Eventname, und wenn das Event gefeuert wird, wird das Objekt importiert, geladen und aufgerufen. Das könnte in vielen Anwendungen Performance sparen, bei Eventhandlern die nur in Spezialfällen gebraucht werden, da somit das Objekt nicht vorher erstellt und hinzugefügt werden muss.

Was haltet ihr von der Idee? (zusätzlich zu der bereits bestehenden sollte man das machen)

Benutzeravatar
dr.e.
Administrator
Beiträge: 4527
Registriert: 04.11.2007, 16:13:53

Re: EventHandler

Beitrag von dr.e. » 05.08.2011, 19:22:12

Hallo Ralf,

was die Idee mit der statischen Registrierung angeht, so bin ich etwas unschlüssig. Einerseits ist es eine schöne Möglichkeit um das Event-binding auszulagern, auf er anderen Seite sollte es im Code nur dann zu einem binding kommen, wenn es auch erwünscht ist. Spring im JAVA-Bereich geht den ersten Weg und definiert so ein wiring ausschließlich im XML (=Konfiguration). Das funktioniert dann jedoch nur, wenn du mit Aspekten für die Bean-Calls agierst oder nur diejenigen Calls definierst, die die Bean-Factory auch "kennt". Im PHP-Bereich wirst du da etwas auf die Nase fallen, da eine Methode nur dann registriert werden kann, wenn du das Objekt auch in der Hand hälst bzw. brauchst du eine Instanz des Handlers, dessen Erzeugungs-Art du nicht kennen kannst und willst. Insofern sehe ich da schwarz, solange es sich nicht um einen DIService handelt, dessen Erzeugung klar gekapselt ist.

Die Implementierung habe ich mir noch nicht vollständig angesehen, der Gültigkeits-Bereich von Events scheint jedoch noch nicht unterstützt.
Viele Grüße,
Christian

Benutzeravatar
Screeze
Beiträge: 1920
Registriert: 05.08.2009, 09:49:04
Kontaktdaten:

Re: EventHandler

Beitrag von Screeze » 05.08.2011, 19:37:43

was die Idee mit der statischen Registrierung angeht, so bin ich etwas unschlüssig. Einerseits ist es eine schöne Möglichkeit um das Event-binding auszulagern, auf er anderen Seite sollte es im Code nur dann zu einem binding kommen, wenn es auch erwünscht ist. Spring im JAVA-Bereich geht den ersten Weg und definiert so ein wiring ausschließlich im XML (=Konfiguration). Das funktioniert dann jedoch nur, wenn du mit Aspekten für die Bean-Calls agierst oder nur diejenigen Calls definierst, die die Bean-Factory auch "kennt". Im PHP-Bereich wirst du da etwas auf die Nase fallen, da eine Methode nur dann registriert werden kann, wenn du das Objekt auch in der Hand hälst bzw. brauchst du eine Instanz des Handlers, dessen Erzeugungs-Art du nicht kennen kannst und willst. Insofern sehe ich da schwarz, solange es sich nicht um einen DIService handelt, dessen Erzeugung klar gekapselt ist.
Folgender Anwendungsfall als Beispiel, wie ich mir das Vorstelle:
Ich habe heute für mein CMS eine automatische Generierung der sitemap.xml für Suchmaschinen erstellt. Mein CMS triggert ein spezielles Event, sobald eine neue Seite angelegt wird, oder die Konfiguration einer Seite geändert wurde. Dieses Event möchte ich nutzen, um immer bei Änderung die Sitemap neu zu generieren.

Das ganze habe ich als extra Plugin umgesetzt. Zwar habe ich die Möglichkeit, dass ein Plugin einen Executer definiert, also eine Funktion des Plugins, die bei jedem Seitenaufruf aufgerufen wird, jedoch ist die in diesem Fall unpraktisch. Ich müsste mit der aktuellen Implementierung einen Executer definieren, der bei jedem Seitenaufruf sich selbst (also das Objekt) und einen Methodennamen davon als Eventhandler registriert.

Da aber nur in einem einzigen Sonderfall dieses Event getriggert wird, würde ich so Ressourcen verschwenden, da ich bei JEDEM Request das Plugin laden und eine Instanz davon erzeugen müsste.

Könnte ich stattdessen in einer Konfiguration ein Eventname, Namespace des Objekts, Objektname und Funktionsname definieren, würde das Objekt nur dann geladen werden, wenn es wirklich gebraucht wird, da es eigentlich keine Funktion erfüllen muss bei jedem Aufruf, und daher keinen Executer benötigt.
Das Eintragen in die Konfiguration kann das Plugin selbst übernehmen, dafür werden Installer und Uninstaller Funktionen unterstützt.
Insofern sehe ich da schwarz, solange es sich nicht um einen DIService handelt, dessen Erzeugung klar gekapselt ist.
Jein. Man könnte in der Konfiguration vielleicht noch hinterlegen WIE das Objekt erzeugt werden soll (Direkt, DIService, getAndInitServiceObject), damit wäre alles abgedeckt.
Die Implementierung habe ich mir noch nicht vollständig angesehen, der Gültigkeits-Bereich von Events scheint jedoch noch nicht unterstützt.
Korrekt, und das stört mich ebenfalls, aber ich habe hier Probleme mit der Umsetzung und hoffe auf Ideen von euch....

Gesperrt

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast