PHPでイベントドリブンしてみる

Pearのパッケージを眺めてたら、Event_Dispatcherというのを発見。
そそられたものの、サンプル見てみると自分が適当に使う分には少し手続きが煩雑な気がするなーと思ったので簡単なのを自分で書いてみることにした。

ソース

<?php
// event_dispatcher.php

class Event{
    const ALL_EVENTS = 'allEvents';
    const SOME_EVENT = 'someEvent';
}

class EventListener{
    public $event_type;
    public $func;
    public static $listeners = array();
    
    function __construct( $e, $f){
        $this->event_type = $e;
        $this->func = $f;
    }
}

function addEventListener( $event_type, $func){
    $listeners = &EventListener::$listeners;
    
    $listeners[] = new EventListener( $event_type , $func);
}

function removeEventListener( $event_type, $func){
    $listeners = &EventListener::$listeners;
    
    foreach( $listeners as $key => &$listener){
        if( $listener->func === $func || $event_type === $listener->event_type){
            unset( $listeners[$key]);
        }
    }
}

function dispatchEvent( $event_type, $info = array()){
    $listeners = &EventListener::$listeners;
    
    foreach( $listeners as &$listener){
        if( $listener->event_type === Event::ALL_EVENTS ||$event_type === $listener->event_type){
            call_user_func( $listener->func, $info);
        }
    }
}

サンプル

<?php
// sample.php

require 'event_dispatcher.php';

addEventListener( Event::ALL_EVENTS, 'onAllEvents');
addEventListener( Event::SOME_EVENT, 'onSomeEvent');

echo "dispatch ALL_EVENTS\n";
dispatchEvent( Event::ALL_EVENTS);
echo "\n";
echo "dispatch SOME_EVENT\n";
dispatchEvent( Event::SOME_EVENT);
echo "\n";
echo "dispatch SOME_EVENT\n";
dispatchEvent( Event::SOME_EVENT);

//---

function onAllEvents( $info){
    static $i = 0;
    $i++;
    
    echo "これは{$i}回目のEvent::ALL_EVENTSです\n";
}

function onSomeEvent( $info){
    echo "これは一回限定のEvent::SOME_EVENTです\n";
    
    removeEventListener( EVENT::SOME_EVENT, 'onSomeEvent');
}

上記サンプルの実行結果

dispatch ALL_EVENTS
これは1回目のEvent::ALL_EVENTSです

dispatch SOME_EVENT
これは2回目のEvent::ALL_EVENTSです
これは一回限定のEvent::SOME_EVENTです

dispatch SOME_EVENT
これは3回目のEvent::ALL_EVENTSです