Skip to content
This repository

Event based programming #44

Open
Marlinc opened this Issue July 09, 2012 · 6 comments

5 participants

Marlin Cremers Nick Rawe Jelmer Schreuder Dave Hulbert Phil Sturgeon
Marlin Cremers

Why not create a standard for event based programming in PHP?

So if you have a application with loop you can use the standard that describes events.

So you have a event like this:

<?php

abstract class Event
{

    public $nameLevel = 2;

    public function getName()
    {
        return end(explode('\\', get_class($this), $this->nameLevel + 1));
    }

}

And a event-handler like this:

<?php

class EventHandler
{

    static protected $callbacks = array();

    protected $events           = array();

    public static function raise(Event $event)
    {
        foreach (self::$callbacks[$event->getName()] as $callback) {
            call_user_func($callback, $event);
        }
    }

    public static function addCallback(event, $callback)
    {
        self::$callbacks[$event->getName()][] = $callback;
    }

}

So you can add a callback to a event with:

<?php

function onClick(Event $event)
{
    printf('Raised event %s', $event->getName) . PHP_EOL:
}

EventHandler::addCallback(new Clicked(), 'onClick');

And you're able to raise the event with:

<?php

EventHandler::raise(new Clicked());

I'm sorry if this doesn't look the way you wan't it but this is just my idea. I'm open to suggestions and would like to get some feedback. And possibly event a standard for event based applications.

Also how do you think about event-based data like a 'nickname' for a user when he joins a IRC channel. Should this information be saved in a extended event class. In the raise method. Or something else?

Nick Rawe

Hi @Marlinc :)

While I think that a standard for Signal Slots/Events would be a good, there are a couple of things I wanted to ask about your example:

  1. I'm fairly sure the add|raise(new Clicked()) method wouldn't work as the objects would have unique references, so you would be forced to have slots based on object types... Not necessarily the best way to go IMHO as that limits the flexibility of any implementation. Is there a reason for this I may have missed?
  2. Is the purpose of the Event object just to contain meta-data about an event?
  3. Does the EventHandler really need to be static?

An alternate take on the above could be:

<?php

interface IEventHandler
{
    public function addEvent($name);
    public function addEventCallback($name, $callback);

    /**
     * param array $meta Any parameters to be passed using call_user_func_array to the listeners
     */
    public function raise($name, array $meta = array());
}

?>

This in practice could then be used like:

<?php

$eh = new \EventHandler();

// Ensure the event is registered so that errors are not thrown in trying to hook a callback that does not exist
$eh->addEvent("exception"); 

$eh->addEventCallback("exception", 'OnException');
$eh->addEventCallback("exception", function (\Exception $e) { 
    // Close a resource related to the process i.e. log file, DB Connection
});

set_exception_handler(function (\Exception $e) use ($eh) {
    // ... Do some logging

    // ... Call any listeners
    $eh->raise("exception", array($e));
});

throw new \ErrorException("Something went wrong");

function OnException(\Exception $e) {
    echo $e;    
}

?>

Just my two-pennys! Good idea @marlinc :)

Marlin Cremers

Hey @nrawe, thank you for reacting this fast!

About your questions:
1. I was thinking about using objects so you could create for eample a 'Error' event. And you have a Notice, Warning and Fatal event extend this Error event. So you could listen on the Error event and get all the Notice, Warning and Fatal events too. (I know this isn't a practicle example but just to give a idea about the class extend).
2. I thought that it would be pretty nice to have the meta-data in the event specific class itself. Because this also allow's the use of a IDE's autocomplete functionallity.
3. It doesn't nessesarly have to be a static class but I thought it would be a easy way to access it across the intire application.

I also like your implementation especially the idea of creating a event for a Exception.
On the other hand I like objects for events more because like I said (and you asked) they can store meta-data about the specific event. And that way for example also support many IDE's auto-completing.

O and by the way I'm sorry if my English isn't that great :)

Nick Rawe

Hey @Marlinc

Not a problem :) You could still use objects to pass on meta data about an event, a fairly trivial example of which would be:

<?php
interface IClicked
{
    public function getTargetName();
    public function setTargetName();
}

$eh->addEvent("my_button_clicked");
$eh->addEventCallback("my_button_clicked", function (IClicked $event) { echo "Hello, {$event->getTargetName())}"; });

// ... Do some work

$click = new Clicked(); // Implements IClicked
$click->setTargetName("Mercury");
$eh->raise("my_button_clicked", array($click)); // echo "Hello, Mercury"

?>

The auto-completes will pick up that the $event is an IClick object, so you should get the methods getTargetName() and setTargetName() come up still to work off of. It would be pretty flexible as you could also have interface bound event handlers as the listeners:

<?php

interface IMyEventListener
{
    public function HookClickEvent(IClick $event);
}

$el = new MyEventListener();
$eh->addEventCallback("my_button_clicked", array($el, "HookClickEvent"));

?>

That should allow for big/small domain models to use the interface quite flexibly.

Not a worry, if you hadn't of said I wouldn't have known :)

Jelmer Schreuder

Doesn't PHP already offer this from SPL with the SplSubject and SplObserver interfaces?

Dave Hulbert

A removeEvent() method would be useful too.

Something this could provide over SqlSubject / SqlObserver is a priority / order flag.

Phil Sturgeon
Collaborator

Other than the last suggestion we've not had anything happen here for almost a year. You guys got anything more to throw in?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.