Event-driven architecture and hooks in PHP
Asked Answered
A

4

15

I am planning on working on a game that has a PHP back-end to communicate with the data repository. I was thinking about it and concluded that the best design paradigm to follow for our game would be event driven. I am looking to have an achievement system (similar to the badges system of this website) and basically I would like to be able to hook these "achievement checks" into a number of different events that occur in the game. ie:

When a user does action X hook Y is fired and all attached functions are called to check against an achievement requirement.

In structuring the architecture like this I will allow for new achievements to be added easily as all I will have to do is add the checking function to the proper hook and everything else will fall into place.

I'm not sure if this is a great explanation of what I intend to do, but in any case I am looking for the following:

  1. Good reference material on how to code an event-driven application
  2. Code snippet(s) showing how to put a "hook" in a function in PHP
  3. Code snippet(s) showing how to attach a function to the "hook" mentioned in point 2

I have a few ideas as to how to accomplish 2) and 3) but I was hoping that somebody well-versed in the matter could shed some light on best practices.

Thank you in advance!

Act answered 27/7, 2011 at 14:30 Comment(4)
I know that wordpress uses hooks for their plugins. You can have a look herePersimmon
A link to WordPress's hook API isn't really what I'm asking for.Act
Well, you could actually check how wordpress uses hooks. There is good documentation and the source code is clear.Persimmon
Well, the wordpress hook implementation is not really a good example, it has no clear interface, the implementation is buggy and the code is not well documented. What it shows however is that you can assign callbacks to variables incl. arrays, that you can sort these arrays and what you can do wrong on invoking callbacks and on callback de-registration. However that information is not really in an accessible format.Effluence
E
16

Good reference material on how to code an event-driven application

You can either do this with "dumb" callbacks (Demo):

class Hooks
{
    private $hooks;
    public function __construct()
    {
        $this->hooks = array();
    }
    public function add($name, $callback) {
        // callback parameters must be at least syntactically
        // correct when added.
        if (!is_callable($callback, true))
        {
            throw new InvalidArgumentException(sprintf('Invalid callback: %s.', print_r($callback, true)));
        }
        $this->hooks[$name][] = $callback;
    }
    public function getCallbacks($name)
    {
        return isset($this->hooks[$name]) ? $this->hooks[$name] : array();
    }
    public function fire($name)
    {
        foreach($this->getCallbacks($name) as $callback)
        {
            // prevent fatal errors, do your own warning or
            // exception here as you need it.
            if (!is_callable($callback))
                continue;

            call_user_func($callback);
        }
    }
}

$hooks = new Hooks;
$hooks->add('event', function() {echo 'morally disputed.';});
$hooks->add('event', function() {echo 'explicitly called.';});
$hooks->fire('event');

Or implementing a pattern often used in event-driven applications: Observer Pattern.

Code snippet(s) showing how to put a "hook" in a function in PHP

The manual link above (callbacks can be stored into a variable) and some PHP code examples for the Observer Pattern.

Effluence answered 27/7, 2011 at 14:39 Comment(7)
Awesome. This is more or less what I was thinking of regarding hook implementation but wanted to make sure I wasn't missing something. Also, very cool site for the PHP demo.Act
@MoarCodePlz: This implementation lacks of events/hook contexts. You can for example encapsulate events into classes of it's own as well so they can be fired (interface them). This would add another layer however. Another useful function is is_callable in this context.Effluence
@MoarCodePlz: Added some sanitization to the class to show that. You might need some extension to remove callbacks again (de-registration).Effluence
What is a good approach to "stop" next event from running, say if i would output a response or something?Ancylostomiasis
@John: Define for hooks not to open a side-channel like output. Alternatively PHP-Style drop all output by capturing it and turn it into a fatal error. Hooks should return through defined structures, not a side-channel, otherwise you run into common problems of global static state you normally try to prevent.Effluence
How would I inject stuff, like objects in to an event?Ancylostomiasis
@John: Take a look to call_user_func_array() instead of call_user_func() - it allows you to pass an array of zero or more parameters. And there is also func_get_args()Effluence
P
5

For PHP I've regulary integrated the Symfony Event Component: http://components.symfony-project.org/event-dispatcher/.

Here's a short example below, which you can find expanded in Symfony's Recipe section.

<?php

class Foo
{
  protected $dispatcher = null;

    // Inject the dispatcher via the constructor
  public function __construct(sfEventDispatcher $dispatcher)
  {
    $this->dispatcher = $dispatcher;
  }

  public function sendEvent($foo, $bar)
  {
    // Send an event
    $event = new sfEvent($this, 'foo.eventName', array('foo' => $foo, 'bar' => $bar));
    $this->dispatcher->notify($event);
  }
}


class Bar
{
  public function addBarMethodToFoo(sfEvent $event)
  {
    // respond to event here.
  }
}


// Somewhere, wire up the Foo event to the Bar listener
$dispatcher->connect('foo.eventName', array($bar, 'addBarMethodToFoo'));

?>

This is the system we integrated into a shopping cart to create a game-like shopping experience, hooking user actions into game-events. When the user performed specific actions, php fired events causing rewards to be triggered.

Example 1: if the user clicked a specific button 10 times, they received a star.

Example 2: when the user refers a friend and that friend signs up an event is fired rewarding the original referrer with points.

Phare answered 27/7, 2011 at 14:47 Comment(0)
D
1

Check out CodeIgniter as it has hooks built right in.

Simply enable hooks:

$config['enable_hooks'] = TRUE;

And then define your hook:

 $hook['post_controller_constructor'] = array(
                                'class'    => 'Hooks',
                                'function' => 'session_check',
                                'filename' => 'hooks.php',
                                'filepath' => 'hooks',
                                'params'   => array()
                                ); 

Then use it in your class:

<?php

    class Hooks {
        var $CI;

        function Hooks() {
            $this->CI =& get_instance();
        }

        function session_check() {
            if(!$this->CI->session->userdata("logged_in") && $this->CI->uri->uri_string != "/user/login")
                redirect('user/login', 'location');
        }
    }

?> 
Damick answered 27/7, 2011 at 14:34 Comment(7)
While I appreciate the feedback and will definitely have to check out CodeIgniter, I am more looking for the explanation on how to write this sort of stuff from scratch. I am also looking for good reference on developing event-driven applications so that I may develop my own event-driven application with my own user-made hooks.Act
Spend your time coding your game logic, not re-inventing wheels that are tried, true, and tested man. Updated my answer with an example usage.Damick
While I completely agree with the "not reinventing the wheel argument," I also think that if all I need is a very simple event-driven hook system then using an external library that has a number of bells and whistles is a worse approach than writing a single class as hakre has shown. IF I was looking for something more involved and robust then I think using an external library would be a better fit.Act
You're building a game and you don't think you'll need a robust framework with bells and whistles? OK :)Damick
Fair enough. The majority of the framework will be focusing on other things but part of the excitement I have towards doing this is working on my own framework. While it may not work as well as the tried and true frameworks, I hope to get some good experience and insight. I don't often like not knowing how something I'm using works.Act
True dat. I know the feeling. One thing to consider is that community-driven open source frameworks will always be better than a solo effort. Learning how to do something is as simple as looking at the source code and reverse-engineering it. That way you can set your foundation at the current best-practice and go from there.Damick
CodeIgniter is far far away from event-driven design.Anagnos
C
0

A new visitor might find some insight into the answer to the O/P's inquiry by investigating ReactPHP. If you search the phrase "Event-Driven Arch in PHP" today (in 2023), ReactPHP seems to be the common denominator.

Coelom answered 9/9, 2023 at 8:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.