# # sig.php is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this software; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301 USA class Signal { private $object; private $expected_args; private $callbacks; private $first_key; private $last_key; private $sorted; /** * Constructor * @param object $object The parent object * @param integer $expected_args The amount of expected args on emit() */ function __construct($object = NULL, $expected_args = 0) { $this->object = $object; $this->expected_args = $expected_args; $this->callbacks = array(); $this->first_key = 0; $this->last_key = 0; } private function connect_real($callback, $args, $after = true) { $key = $after ? ++$this->last_key : --$this->first_key; $this->callbacks[$key] = array($callback, $args); $this->sorted = false; return $key; } private function emit_real($args) { if (count($args) != $this->expected_args) throw new Exception(sprintf("Wrong argument count. Expected: %d, given: %d", $this->expected_args, count($args))); if (!$this->sorted) { ksort($this->callbacks, SORT_NUMERIC); $this->sorted = true; } array_unshift($args, $this->object); foreach ($this->callbacks as $data) { list($callback, $cargs) = $data; call_user_func_array($callback, array_merge($args, $cargs)); } } /** * Connect a handler to the signal (will be run first) * @param string $callback The handler name * @param variant* $args Zero or more additional args * @return integer The handler id */ public function connect() { $args = func_get_args(); $callback = array_shift($args); $this->connect_real($callback, $args, true); } /** * Connect a handler to the signal (will be run last) * @param string $callback The handler name * @param variant* $args Zero or more additional args * @return integer The handler id */ public function connect_after() { $args = func_get_args(); $callback = array_shift($args); $this->connect_real($callback, $args, false); } /** * Emit the signal. * This will cause any connected handler to be run * @param variant* $args Zero or more additional args to be passed to * the handlers */ public function emit() { $args = func_get_args(); $this->emit_real($args); } /** * Clear the handler list */ public function clear() { $this->callbacks = array(); } /** * Disconnect a handler to the signal * @param integer The id of the handler to disconnect */ public function disconnect($id) { unset($this->callbacks[$id]); } } /** * This class handles the registering and access of signals. It's better, * but not required, to use it as a parent class for your own classes. * * Note: be careful when overwriting __get, __set or __isset. */ abstract class SignalOwner { private $signals = array(); /** * Define a new signal for the class. * The new signal can be accessed with $object->$name. * @param string $name The signal name * @param integer $expected_args The amount of expected args on emit() */ protected function set_signal($name, $expected_args = 0) { if (!preg_match('/[a-z_][a-z0-9_]/i', $name)) throw new Exception("Bad name for signal: $name"); $this->signals[$name] = new Signal($this, $expected_args); } /** * Does the current class have a signal of that name ? * @param string $name The signal name */ protected function has_signal($name) { return isset($this->signals[$name]); } public function __get($name) { if (isset($this->signals[$name])) return $this->signals[$name]; } public function __set($name, $value) { if (isset($this->signals[$name])) throw new Exception("Can't override signals"); else $this->$name = $value; # restore the default PHP behaviour } public function __isset($name) { return isset($this->signals[$name]); } } # ex:ts=2:et: ?>