commit
e2444ec9c5
@ -0,0 +1,89 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
namespace OC\Hooks; |
||||
|
||||
abstract class BasicEmitter implements Emitter { |
||||
|
||||
/** |
||||
* @var (callable[])[] $listeners |
||||
*/ |
||||
private $listeners = array(); |
||||
|
||||
/** |
||||
* @param string $scope |
||||
* @param string $method |
||||
* @param callable $callback |
||||
*/ |
||||
public function listen($scope, $method, $callback) { |
||||
$eventName = $scope . '::' . $method; |
||||
if (!isset($this->listeners[$eventName])) { |
||||
$this->listeners[$eventName] = array(); |
||||
} |
||||
if (array_search($callback, $this->listeners[$eventName]) === false) { |
||||
$this->listeners[$eventName][] = $callback; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param string $scope optional |
||||
* @param string $method optional |
||||
* @param callable $callback optional |
||||
*/ |
||||
public function removeListener($scope = null, $method = null, $callback = null) { |
||||
$names = array(); |
||||
$allNames = array_keys($this->listeners); |
||||
if ($scope and $method) { |
||||
$name = $scope . '::' . $method; |
||||
if (isset($this->listeners[$name])) { |
||||
$names[] = $name; |
||||
} |
||||
} elseif ($scope) { |
||||
foreach ($allNames as $name) { |
||||
$parts = explode('::', $name, 2); |
||||
if ($parts[0] == $scope) { |
||||
$names[] = $name; |
||||
} |
||||
} |
||||
} elseif ($method) { |
||||
foreach ($allNames as $name) { |
||||
$parts = explode('::', $name, 2); |
||||
if ($parts[1] == $method) { |
||||
$names[] = $name; |
||||
} |
||||
} |
||||
} else { |
||||
$names = $allNames; |
||||
} |
||||
|
||||
foreach ($names as $name) { |
||||
if ($callback) { |
||||
$index = array_search($callback, $this->listeners[$name]); |
||||
if ($index !== false) { |
||||
unset($this->listeners[$name][$index]); |
||||
} |
||||
} else { |
||||
$this->listeners[$name] = array(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param string $scope |
||||
* @param string $method |
||||
* @param array $arguments optional |
||||
*/ |
||||
protected function emit($scope, $method, $arguments = array()) { |
||||
$eventName = $scope . '::' . $method; |
||||
if (isset($this->listeners[$eventName])) { |
||||
foreach ($this->listeners[$eventName] as $callback) { |
||||
call_user_func_array($callback, $arguments); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,32 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
namespace OC\Hooks; |
||||
|
||||
/** |
||||
* Class Emitter |
||||
* |
||||
* interface for all classes that are able to emit events |
||||
* |
||||
* @package OC\Hooks |
||||
*/ |
||||
interface Emitter { |
||||
/** |
||||
* @param string $scope |
||||
* @param string $method |
||||
* @param callable $callback |
||||
*/ |
||||
public function listen($scope, $method, $callback); |
||||
|
||||
/** |
||||
* @param string $scope optional |
||||
* @param string $method optional |
||||
* @param callable $callback optional |
||||
*/ |
||||
public function removeListener($scope = null, $method = null, $callback = null); |
||||
} |
@ -0,0 +1,16 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
namespace OC\Hooks; |
||||
|
||||
abstract class LegacyEmitter extends BasicEmitter { |
||||
protected function emit($scope, $method, $arguments = array()) { |
||||
\OC_Hook::emit($scope, $method, $arguments); |
||||
parent::emit($scope, $method, $arguments); |
||||
} |
||||
} |
@ -0,0 +1,261 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
namespace Test\Hooks; |
||||
|
||||
/** |
||||
* Class DummyEmitter |
||||
* |
||||
* class to make BasicEmitter::emit publicly available |
||||
* |
||||
* @package Test\Hooks |
||||
*/ |
||||
class DummyEmitter extends \OC\Hooks\BasicEmitter { |
||||
public function emitEvent($scope, $method, $arguments = array()) { |
||||
$this->emit($scope, $method, $arguments); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Class EmittedException |
||||
* |
||||
* a dummy exception so we can check if an event is emitted |
||||
* |
||||
* @package Test\Hooks |
||||
*/ |
||||
class EmittedException extends \Exception { |
||||
} |
||||
|
||||
class BasicEmitter extends \PHPUnit_Framework_TestCase { |
||||
/** |
||||
* @var \OC\Hooks\Emitter $emitter |
||||
*/ |
||||
protected $emitter; |
||||
|
||||
public function setUp() { |
||||
$this->emitter = new DummyEmitter(); |
||||
} |
||||
|
||||
public function nonStaticCallBack() { |
||||
throw new EmittedException; |
||||
} |
||||
|
||||
public static function staticCallBack() { |
||||
throw new EmittedException; |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testAnonymousFunction() { |
||||
$this->emitter->listen('Test', 'test', function () { |
||||
throw new EmittedException; |
||||
}); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testStaticCallback() { |
||||
$this->emitter->listen('Test', 'test', array('\Test\Hooks\BasicEmitter', 'staticCallBack')); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testNonStaticCallback() { |
||||
$this->emitter->listen('Test', 'test', array($this, 'nonStaticCallBack')); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
public function testOnlyCallOnce() { |
||||
$count = 0; |
||||
$listener = function () use (&$count) { |
||||
$count++; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->assertEquals(1, $count, 'Listener called an invalid number of times (' . $count . ') expected 1'); |
||||
} |
||||
|
||||
public function testDifferentMethods() { |
||||
$count = 0; |
||||
$listener = function () use (&$count) { |
||||
$count++; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Test', 'foo', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->emitter->emitEvent('Test', 'foo'); |
||||
$this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2'); |
||||
} |
||||
|
||||
public function testDifferentScopes() { |
||||
$count = 0; |
||||
$listener = function () use (&$count) { |
||||
$count++; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Bar', 'test', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->emitter->emitEvent('Bar', 'test'); |
||||
$this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2'); |
||||
} |
||||
|
||||
public function testDifferentCallbacks() { |
||||
$count = 0; |
||||
$listener1 = function () use (&$count) { |
||||
$count++; |
||||
}; |
||||
$listener2 = function () use (&$count) { |
||||
$count++; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener1); |
||||
$this->emitter->listen('Test', 'test', $listener2); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2'); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testArguments() { |
||||
$this->emitter->listen('Test', 'test', function ($foo, $bar) { |
||||
if ($foo == 'foo' and $bar == 'bar') { |
||||
throw new EmittedException; |
||||
} |
||||
}); |
||||
$this->emitter->emitEvent('Test', 'test', array('foo', 'bar')); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testNamedArguments() { |
||||
$this->emitter->listen('Test', 'test', function ($foo, $bar) { |
||||
if ($foo == 'foo' and $bar == 'bar') { |
||||
throw new EmittedException; |
||||
} |
||||
}); |
||||
$this->emitter->emitEvent('Test', 'test', array('foo' => 'foo', 'bar' => 'bar')); |
||||
} |
||||
|
||||
public function testRemoveAllSpecified() { |
||||
$listener = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->removeListener('Test', 'test', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
public function testRemoveWildcardListener() { |
||||
$listener1 = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$listener2 = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener1); |
||||
$this->emitter->listen('Test', 'test', $listener2); |
||||
$this->emitter->removeListener('Test', 'test'); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
public function testRemoveWildcardMethod() { |
||||
$listener = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Test', 'foo', $listener); |
||||
$this->emitter->removeListener('Test', null, $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->emitter->emitEvent('Test', 'foo'); |
||||
} |
||||
|
||||
public function testRemoveWildcardScope() { |
||||
$listener = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Bar', 'test', $listener); |
||||
$this->emitter->removeListener(null, 'test', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->emitter->emitEvent('Bar', 'test'); |
||||
} |
||||
|
||||
public function testRemoveWildcardScopeAndMethod() { |
||||
$listener = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Test', 'foo', $listener); |
||||
$this->emitter->listen('Bar', 'foo', $listener); |
||||
$this->emitter->removeListener(null, null, $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->emitter->emitEvent('Test', 'foo'); |
||||
$this->emitter->emitEvent('Bar', 'foo'); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testRemoveKeepOtherCallback() { |
||||
$listener1 = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$listener2 = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener1); |
||||
$this->emitter->listen('Test', 'test', $listener2); |
||||
$this->emitter->removeListener('Test', 'test', $listener1); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testRemoveKeepOtherMethod() { |
||||
$listener = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Test', 'foo', $listener); |
||||
$this->emitter->removeListener('Test', 'foo', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testRemoveKeepOtherScope() { |
||||
$listener = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->listen('Bar', 'test', $listener); |
||||
$this->emitter->removeListener('Bar', 'test', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException \Test\Hooks\EmittedException |
||||
*/ |
||||
public function testRemoveNonExistingName() { |
||||
$listener = function () { |
||||
throw new EmittedException; |
||||
}; |
||||
$this->emitter->listen('Test', 'test', $listener); |
||||
$this->emitter->removeListener('Bar', 'test', $listener); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
} |
||||
} |
@ -0,0 +1,55 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
namespace Test\Hooks; |
||||
|
||||
/** |
||||
* Class DummyLegacyEmitter |
||||
* |
||||
* class to make LegacyEmitter::emit publicly available |
||||
* |
||||
* @package Test\Hooks |
||||
*/ |
||||
class DummyLegacyEmitter extends \OC\Hooks\LegacyEmitter { |
||||
public function emitEvent($scope, $method, $arguments = array()) { |
||||
$this->emit($scope, $method, $arguments); |
||||
} |
||||
} |
||||
|
||||
class LegacyEmitter extends BasicEmitter { |
||||
|
||||
//we can't use exceptions here since OC_Hooks catches all exceptions |
||||
private static $emitted = false; |
||||
|
||||
public function setUp() { |
||||
$this->emitter = new DummyLegacyEmitter(); |
||||
self::$emitted = false; |
||||
\OC_Hook::clear('Test','test'); |
||||
} |
||||
|
||||
public static function staticLegacyCallBack() { |
||||
self::$emitted = true; |
||||
} |
||||
|
||||
public static function staticLegacyArgumentsCallBack($arguments) { |
||||
if ($arguments['foo'] == 'foo' and $arguments['bar'] == 'bar') |
||||
self::$emitted = true; |
||||
} |
||||
|
||||
public function testLegacyHook() { |
||||
\OC_Hook::connect('Test', 'test', '\Test\Hooks\LegacyEmitter', 'staticLegacyCallBack'); |
||||
$this->emitter->emitEvent('Test', 'test'); |
||||
$this->assertEquals(true, self::$emitted); |
||||
} |
||||
|
||||
public function testLegacyArguments() { |
||||
\OC_Hook::connect('Test', 'test', '\Test\Hooks\LegacyEmitter', 'staticLegacyArgumentsCallBack'); |
||||
$this->emitter->emitEvent('Test', 'test', array('foo' => 'foo', 'bar' => 'bar')); |
||||
$this->assertEquals(true, self::$emitted); |
||||
} |
||||
} |
Loading…
Reference in new issue