@ 2014-06-02T10:52:03Z <?php
error_reporting(-1);
class Slot {
/**
* @var callable
*/
public $listener;
/**
* @var boolean
*/
public $once;
/** @var array */
public $params = array();
/**
* @var Signal
*/
private $signal;
function __construct(Signal $signal, $listener, $once) {
$this->listener = $listener;
$this->once = $once;
$this->signal = $signal;
}
public function execute(array $values) {
$resolver = $this->signal->getListenerResolver();
if ($resolver) {
$callback = $resolver->resolve($this->listener);
} else {
$callback = $this->listener;
}
$args = array_merge($values, $this->params);
return call_user_func_array($callback, $args);
}
public function getListener() {
return $this->listener;
}
public function getOnce() {
return $this->once;
}
}
class SlotList {
/**
* @var Signal
*/
private $signal;
/**
* @var Slot[]
*/
private $slots = array();
function __construct(Signal $signal) {
$this->signal = $signal;
}
public function add(Slot $slot) {
return $this->slots[] = $slot;
}
public function count() {
return count($this->slots);
}
public function execute(array $values) {
$returnResultMatcher = $this->signal->getReturnResultMatcher();
foreach ($this->slots as $key => $slot) {
if ($slot->once) {
unset($this->slots[$key]);
}
$result = $slot->execute($values);
if ($returnResultMatcher && $returnResultMatcher->match($result)) {
return $result;
}
}
}
/**
* @param callable $listener
* @return Slot
*/
public function find($listener) {
foreach ($this->slots as $slot) {
if ($slot->listener === $listener) {
return $slot;
}
}
}
/**
* @param callable $listener
* @return Slot
*/
public function remove($listener) {
foreach ($this->slots as $key => $slot) {
if ($slot->listener === $listener) {
unset($this->slots[$key]);
return $slot;
}
}
}
}
interface ListenerResolverInterface {
public function resolve($listener);
}
interface ListenerResolverAwareInterface {
public function getListenerResolver();
public function setListenerResolver(ListenerResolverInterface $listenerResolver);
}
class SimpleListenerResolver implements ListenerResolverInterface {
public function resolve($listener) {
if (is_array($listener)) {
list($class, $method) = $listener;
} else if (is_string($listener) && strpos($listener, '::')) {
list($class, $method) = explode('::', $listener);
}
if (isset($class) && !is_object($class)) {
return array(new $class, $method);
}
return $listener;
}
}
interface ReturnResultMatcherInterface {
public function match($result);
}
interface ReturnResultMatcherAwareInterface {
public function getReturnResultMatcher();
public function setReturnResultMatcher(ReturnResultMatcherInterface $returnResultMatcher);
}
class NonNullReturnResultMatcher implements ReturnResultMatcherInterface {
public function match($result) {
return $result !== null;
}
}
interface OnceSignalInterface {
public function addOnce($listener);
public function dispatch();
public function getNumListeners();
public function remove($listener);
}
interface SignalInterface {
public function add($listener);
}
abstract class AbstractOnceSignal implements OnceSignalInterface {
protected $slots;
public function addOnce($listener) {
return $this->register($listener, true);
}
public function dispatch() {
if (count($this->slots) < 1) {
return;
}
$values = func_get_args();
return $this->execute($values);
}
public function getNumListeners() {
return count($this->slots);
}
abstract protected function register($listener, $once);
abstract protected function execute(array $values);
}
class OnceSignal extends AbstractOnceSignal implements ListenerResolverAwareInterface, ReturnResultMatcherAwareInterface {
/** @var ListenerResolverInterface */
protected $listenerResolver;
/** @var ReturnResultMatcherInterface */
protected $returnResultMatcher;
/** @var mixed */
private $owner;
/**
* @var array
*/
private $valueTypes;
function __construct(array $valueTypes = array(), $owner = null) {
$this->valueTypes = $valueTypes;
$this->owner = $owner;
$this->slots = new SlotList($this);
}
public function addOnce($listener) {
return $this->register($listener, true);
}
public function getNumListeners() {
return $this->slots->count();
}
public function getListenerResolver() {
return $this->listenerResolver;
}
public function setListenerResolver(ListenerResolverInterface $listenerResolver) {
$this->listenerResolver = $listenerResolver;
}
public function getReturnResultMatcher() {
return $this->returnResultMatcher;
}
public function setReturnResultMatcher(ReturnResultMatcherInterface $returnResultMatcher) {
$this->returnResultMatcher = $returnResultMatcher;
}
protected function execute(array $values) {
// validate value types
if (isset($this->owner)) {
$values[] = $this->owner;
}
if (empty($this->listenerResolver)) {
$this->setListenerResolver(new SimpleListenerResolver());
}
return $this->slots->execute($values);
}
protected function register($listener, $once) {
$existing = $this->slots->find($listener);
if ($existing) {
if ($existing->once !== $once) {
throw new InvalidArgumentException('Once not once!');
}
return $existing;
}
return $this->slots->add(new Slot($this, $listener, $once));
}
public function remove($listener) {
return $this->slots->remove($listener);
}
}
class Signal extends OnceSignal implements SignalInterface {
public function add($listener) {
return $this->register($listener, false);
}
}
class MinimalSignal extends AbstractOnceSignal implements SignalInterface {
public function __construct() {
$this->slots = array();
}
public function add($listener) {
$this->register($listener, false);
}
protected function execute(array $values) {
foreach ($this->slots as $key => $slot) {
if ($slot[1]) {
unset($this->slots[$key]);
}
call_user_func_array($slot[0], $values);
}
}
protected function register($listener, $once) {
foreach ($this->slots as $slot) {
if ($slot[0] === $listener) {
if ($slot[1] !== $once) {
throw new InvalidArgumentException('Once not once!');
}
return;
}
}
$this->slots[] = array($listener, $once);
}
public function remove($listener) {
foreach ($this->slots as $key => $slot) {
if ($slot[0] === $listener) {
unset($this->slots[$key]);
return;
}
}
}
}
echo "<pre>";
class MyListener {
public function onSignal(MySignalOwner $owner) {
die('<pre>' . print_r($owner, 1) . __FILE__ . ' : ' . __LINE__ . "\n");
}
}
class MySignalOwner {
/**
* @var Signal
*/
private $signal;
/**
* @return Signal
*/
public function getSignal() {
return $this->signal ? : $this->signal = new Signal(array(), $this);
}
}
$listener = 'MyListener::onSignal';
$listener = array(new MyListener, 'onSignal');
$owner = new MySignalOwner();
$owner->getSignal()->addOnce($listener);
$result = $owner->getSignal()->dispatch();
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Output for 8.0.10 - 8.0.30 , 8.1.0 - 8.1.28 , 8.2.0 - 8.2.18 , 8.3.0 - 8.3.6 <pre>
Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, SlotList given in /in/1oXUL:198
Stack trace:
#0 /in/1oXUL(381): AbstractOnceSignal->dispatch()
#1 {main}
thrown in /in/1oXUL on line 198
Process exited with code 255 . Output for 8.0.0 - 8.0.9 <pre>
Fatal error: Uncaught TypeError: count(): Argument #1 ($var) must be of type Countable|array, SlotList given in /in/1oXUL:198
Stack trace:
#0 /in/1oXUL(381): AbstractOnceSignal->dispatch()
#1 {main}
thrown in /in/1oXUL on line 198
Process exited with code 255 . Output for 7.2.0 - 7.2.33 , 7.3.0 - 7.3.33 , 7.4.0 - 7.4.33 <pre>
Warning: count(): Parameter must be an array or an object that implements Countable in /in/1oXUL on line 198
<pre>MySignalOwner Object
(
[signal:MySignalOwner:private] => Signal Object
(
[listenerResolver:protected] => SimpleListenerResolver Object
(
)
[returnResultMatcher:protected] =>
[owner:OnceSignal:private] => MySignalOwner Object
*RECURSION*
[valueTypes:OnceSignal:private] => Array
(
)
[slots:protected] => SlotList Object
(
[signal:SlotList:private] => Signal Object
*RECURSION*
[slots:SlotList:private] => Array
(
)
)
)
)
/in/1oXUL : 355
Output for 5.3.0 - 5.3.29 , 5.4.0 - 5.4.45 , 5.5.0 - 5.5.38 , 5.6.0 - 5.6.28 , 7.0.0 - 7.0.20 , 7.1.0 - 7.1.25 <pre><pre>MySignalOwner Object
(
[signal:MySignalOwner:private] => Signal Object
(
[listenerResolver:protected] => SimpleListenerResolver Object
(
)
[returnResultMatcher:protected] =>
[owner:OnceSignal:private] => MySignalOwner Object
*RECURSION*
[valueTypes:OnceSignal:private] => Array
(
)
[slots:protected] => SlotList Object
(
[signal:SlotList:private] => Signal Object
*RECURSION*
[slots:SlotList:private] => Array
(
)
)
)
)
/in/1oXUL : 355
preferences:dark mode live preview
219.08 ms | 404 KiB | 317 Q