5 changed files with 453 additions and 0 deletions
-
89lib/hooks/basicemitter.php
-
32lib/hooks/emitter.php
-
16lib/hooks/legacyemitter.php
-
261tests/lib/hooks/basicemitter.php
-
55tests/lib/hooks/legacyemitter.php
@ -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); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue