Gå til innhold

Litt bistand med event dispatcher wildcards


Anbefalte innlegg

Jeg trenger litt bistand til å finne en ryddig måte å løse dette på, da jeg ikke er fornøyd med mine egne forsøk.

 

Jeg har et ferdigbygd system for event dispatching, men jeg ønsker å utbedre dette til å akseptere wildcards. Systemet er inspirert av Symfony sin løsning, og likner derfor en del. Det innebærer at en event trigges med følgende linje (eller noe slikt, skriver fra minne nå):

$eventDispatcher->dispatch('controllers.mycontroller.eventname', $event);

og en listeners defineres slik:

$eventDispatcher->addListener(array($listener, 'methodToTrigger'), 'controllers.mycontroller.eventname');

Dette fører til at alle listeners som lytter etter eventen med navn 'controllers.mycontroller.eventname' trigger. So far so good.

 

Det jeg derimot nå ønsker er som sagt å støtte wildcards, så jeg vil at en listener skal kunne lytte etter, f.eks:

*.mycontroller.eventname
controllers.*
eventname

Alle eksemplene over burde trigge på 'controllers.mycontroller.eventname'.

 

Jeg har lekt meg litt på papiret med hvordan dette kan gjøres, men jeg føler ikke at jeg finner noen ryddig løsning, og at dette burde være noe det finnes en eller annen standard metode for.

 

Edit: Merk at jeg kun ønsker å endre en enkel metode i dispatcher systmet mitt, jeg trenger kun en eller annen funksjon som returnerer true/false dersom det er et treff.

Endret av Lanes
Lenke til kommentar
Videoannonse
Annonse

Litt overraskende at det ikke har kommet noen svar her. Det pleier å være såpass appetitt etter litt aktivitet på dette forumet at de kommer raskt.

Jeg fikk nå likevel satt meg ned og tenkt ut en litt annen løsning enn hva jeg først så for meg, så det er mulig at dette vil fungere. Merk at jeg ikke har testet koden enda da jeg skrev det på iPaden under under frokost i dag tidlig.

Dette føles derimot fremdeles litt mer tungvint enn hva det burde være.

    public function dispatch($sEventIdentifier, Event $event)
    {
        $aEventPathElements = explode(".", $sEventIdentifier);
        $sEventName = array_pop($aEventPathElements);
        $iEventPathElements = count($aEventPathElements);
       
        foreach ($this->aListeners as $sListenerFullPath => $callback) {
            $aListenerPathElements = explode(".", $sListenerFullPath);
            $sListenerEventName = array_pop($aListenerPathElements);
            $iListenerPathElements = count($aListenerPathElements);
       
            // Dersom det kun er satt eventname i listener sjekker vi mot denne
            if ($iListenerPathElements === 0) {
                if ($sListenerEventTarget == $sEventName) {
                    call_user_func($callback, $event, $this);
                    continue;
                }
            }
           
            // Beveg oss nedover og sjekk hvert element
            $bMatch = false;
            for ($i; $i == $iEventPathElements;$i++) {
                if ($aEventPathElements[$i] == $aListenerPathElements[$i] 
                   || $aListenerPathElements[$i] == "*")
                {
                    $bMatch = true;
                }
                ($bMatch) ? continue : break;
            }
            // Kommer vi hit er $bMatch true i alle elementene, og vi har
            // en match i alle ledd og kan kalle listener objektet
            call_user_func($callback, $event, $this);  
        }    
    }

Koden over må fordeles i egne methods, men flyten er den samme.

Endret av Lanes
Lenke til kommentar
  • 2 uker senere...

Det ble virkelig en monolog dette her :) Koden over var bare rot, så jeg tenkte å i det minste poste hva jeg endte opp med. Føler fremdeles at dette kan gjøres mye enklere med noen smarte regex løsninger...

 

Gidder ikke å skrive om koden til en mer lettforståelig del, så dette blir min implementasjon direkte i min applikasjon. Hovedsaken er at den tar to stringer i formatet namespace.subnamespace.class og sjekker de opp mot hverandre for å se om de er like, og da selvfølgelig med støtte for wildcards.

private function extractDataFromEventIdentifier($sEventIdentifier)
{
    $aEventPathElements = explode(".", $sEventIdentifier);
    $sEventName = end($aEventPathElements);
    $sFirstElement = reset($aEventPathElements);
    return array(
       'sEventName' => $sEventName,
        'aEventPathElements' => $aEventPathElements,
        'iEventPathElements' => count($aEventPathElements),
        'sFirstElement'		=> $sFirstElement
    );
}

private function isMatch($aEventData, $aListenerData)
{
    if($aListenerData['iEventPathElements'] === 1 
       && $aListenerData['sEventName'] == $aEventData['sEventName'])
    {
   	return true;
    }
    	
    if ($aListenerData['sFirstElement'] == '*') {
    	$aEventData['aEventPathElements'] = array_reverse($aEventData['aEventPathElements']);
    	$aListenerData['aEventPathElements'] = array_reverse($aListenerData['aEventPathElements']);
    }
    return $this->checkForMatch($aEventData, $aListenerData);
}

private function checkForMatch(array $aEventData, array $aListenerData)
{
    foreach($aEventData['aEventPathElements'] as $key => $sElementToMatch) {
        $sElement = array_slice($aListenerData['aEventPathElements'], $key, 1);

    	if(isset($sElement[0])) {
    		$sElement = $sElement[0];
    	} else {
    		continue;
    	}
    		 
    	if($sElement == '*') {
    		$bMatch = true;
    		continue;
    	} else if ($sElementToMatch == $sElement) {
    		$bMatch = true;
    		continue;
    	} else {
    		return false;
    	}
    }
    return $bMatch;    	
}
Endret av Lanes
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...