leilukin-tumbleblog/includes/class/Trigger.php

263 lines
7.9 KiB
PHP

<?php
/**
* Class: Trigger
* Controls and keeps track of all of the Triggers and events.
*/
class Trigger {
# Array: $priorities
# Custom prioritized callbacks.
public $priorities = array();
# Array: $called
# Keeps track of called Triggers.
private $called = array();
# Array: $exists
# Caches trigger existence states.
private $exists = array();
/**
* Function: __construct
* Add predefined filters to implement Unicode emoji and Markdown support.
*/
private function __construct() {
$config = Config::current();
if ($config->enable_emoji)
$this->priorities["markup_text"][] = array(
"priority" => 10,
"function" => "emote"
);
if ($config->enable_markdown)
$this->priorities["markup_text"][] = array(
"priority" => 5,
"function" => "markdown"
);
}
/**
* Function: cmp
* Sorts actions by priority when used with usort.
*/
private function cmp($a, $b): int {
if (empty($a) or empty($b))
return 0;
if ($a["priority"] == $b["priority"])
return 0;
return ($a["priority"] < $b["priority"]) ? -1 : 1 ;
}
/**
* Function: decide
* Decides what to do with a call return value.
*/
private function decide($return, $val): mixed {
if ($return === false)
return $val;
if (is_string($return) and is_string($val))
return $return.$val;
return oneof($val, $return);
}
/**
* Function: call
* Calls a trigger action.
*
* Parameters:
* $name - The name of the trigger, or an array of triggers to call.
*
* Returns:
* A concatenated string if all calls return a string, or;
* @false@ if none of the triggers exist, or;
* the most substantial returned value decided by oneof().
*
* Notes:
* Any additional arguments are passed on to the trigger responders.
*/
public function call($name): mixed {
$return = false;
if (is_array($name)) {
foreach ($name as $call) {
$args = func_get_args();
$args[0] = $call;
$val = call_user_func_array(
array($this, "call"),
$args
);
if ($val !== false)
$return = $this->decide($return, $val);
}
return $return;
}
if (!$this->exists($name))
return $return;
$arguments = func_get_args();
array_shift($arguments);
$this->called[$name] = array();
if (
isset($this->priorities[$name]) and
usort($this->priorities[$name], array($this, "cmp"))
)
foreach ($this->priorities[$name] as $action) {
$function = $action["function"];
if (is_array($function)) {
$object = $function[0];
if (is_object($object) and !empty($object->cancelled))
continue;
}
$val = call_user_func_array($function, $arguments);
$return = $this->decide($return, $val);
$this->called[$name][] = $function;
}
foreach (Modules::$instances as $module)
if (
is_callable(array($module, $name)) and
!in_array(array($module, $name), $this->called[$name])
) {
if (!empty($module->cancelled))
continue;
$val = call_user_func_array(
array($module, $name),
$arguments
);
$return = $this->decide($return, $val);
}
return $return;
}
/**
* Function: filter
* Modify a variable by filtering it through a stack of trigger actions.
*
* Parameters:
* &$target - The variable to filter.
* $name - The name of the trigger.
*
* Returns:
* $target, filtered through any/all actions for the trigger $name.
*
* Notes:
* Any additional arguments are passed on to the trigger responders.
*/
public function filter(&$target, $name): mixed {
if (is_array($name)) {
foreach ($name as $filter) {
$args = func_get_args();
$args[0] =& $target;
$args[1] = $filter;
$target = call_user_func_array(
array($this, "filter"),
$args
);
}
return $target;
}
if (!$this->exists($name))
return $target;
$arguments = func_get_args();
array_shift($arguments);
array_shift($arguments);
$this->called[$name] = array();
if (isset($this->priorities[$name]) and
usort($this->priorities[$name], array($this, "cmp"))
)
foreach ($this->priorities[$name] as $action) {
$function = $action["function"];
if (is_array($function)) {
$object = $function[0];
if (is_object($object) and !empty($object->cancelled))
continue;
}
$val = call_user_func_array(
$function,
array_merge(array(&$target), $arguments)
);
$this->called[$name][] = $function;
$target = fallback($val, $target);
}
foreach (Modules::$instances as $module)
if (
is_callable(array($module, $name)) and
!in_array(array($module, $name), $this->called[$name])
) {
if (!empty($module->cancelled))
continue;
$val = call_user_func_array(
array($module, $name),
array_merge(array(&$target), $arguments)
);
$target = fallback($val, $target);
}
return $target;
}
/**
* Function: exists
* Checks if there are any actions for a given $trigger.
*
* Parameters:
* $trigger - The trigger name.
*
* Returns:
* @true@ or @false@
*/
public function exists($name): bool {
if (isset($this->exists[$name]))
return $this->exists[$name];
foreach (Modules::$instances as $module) {
if (is_callable(array($module, $name)))
return $this->exists[$name] = true;
}
if (isset($this->priorities[$name]))
return $this->exists[$name] = true;
return $this->exists[$name] = false;
}
/**
* Function: current
* Returns a singleton reference to the current class.
*/
public static function & current(): self {
static $instance = null;
$instance = (empty($instance)) ? new self() : $instance ;
return $instance;
}
}