<?php

$hooks = array();

/**
 * Registered a hook on a specific event
 *
 * @param $event string The event name
 * @param $callable callable The callable to run on event
 * @param $param mixed Paremeter that will be pass to the callable
 *                         Use an array if you have multiple parameters to pass
 *
 * @return void
 */
function register_hook($event, $callable, $param=NULL) {
  global $hooks;
  if (!array_key_exists($event, $hooks))
    $hooks[$event] = array();
  $hooks[$event][] = array (
    'callable' => $callable,
    'param'    => $param,
  );
}

/**
 * Run triggered actions on specific event
 *
 * @param $event string Event name
 *
 * @return boolean True if all triggered actions succefully runned, false otherwise
 */
function trigger_hook($event_name, $event_data=null) {
  global $hooks;
  $return = true;

  if (isset($hooks[$event_name]) && is_array($hooks[$event_name])) {
    if ($event_name == 'all')
      $event = new Event($event_data['event_name'], $event_data['event_data']);
    else
      $event = new Event($event_name, $event_data);
    foreach ($hooks[$event_name] as $e) {
      if (is_callable($e['callable'])) {
        try {
          call_user_func_array($e['callable'],array($event, &$e['param']));
        }
        catch(Exception $e) {
          logException(
            $e, "An exception occured running hook ".format_callable($e['callable']).
            " on event $event_name");
          $return = false;
        }
      }
      else {
        logging(
          'ERROR',
          "The hook ".format_callable($e['callable'])." on event $event_name is not callable.");
        $return = false;
      }
    }
  }
  else
    logging('DEBUG', "No hook registered for event $event_name.");

  // Handle 'all' event
  if ($event_name != 'all') {
    trigger_hook (
      'all',
      array (
        'event_name' => $event_name,
        'event_data' => $event_data,
      )
    );
  }

  return $return;
}

class Event implements JsonSerializable {
  private $name;
  private $data;

  function __construct($name, $data) {
    $this -> name = $name;
    $this -> data = $data;
  }

  function __get($key) {
    if ($key == 'name')
      return $this -> name;
    elseif ($key == 'data')
      return $this -> data;
    elseif (is_array($this -> data) && array_key_exists($key, $this -> data))
      return $this -> data[$key];
    return null;
  }

  public function jsonSerialize() {
    return array (
      'name' => $this -> name,
      'data' => $this -> data,
    );
  }
}