From 879bad2191e27801ac1fac2ac11bb491eda50f65 Mon Sep 17 00:00:00 2001 From: oanhnn Date: Thu, 21 Jan 2016 18:26:00 +0700 Subject: [PATCH] Updated --- .gitattributes | 1 + .gitignore | 22 +++- .php_cs | 31 ++++++ .travis.yml | 23 ++++ README.md | 10 +- composer.json | 4 +- phpunit.xml | 9 +- src/Dispatcher.php | 29 +++-- src/DispatcherInterface.php | 19 ++-- src/Event.php | 17 ++- tests/DispatcherTest.php | 204 +++++++++++++++++++++++++++++++++- tests/EventTest.php | 24 +++- tests/Stub/EventListeners.php | 31 ++++++ tests/bootstrap.php | 10 +- 14 files changed, 398 insertions(+), 36 deletions(-) create mode 100644 .php_cs create mode 100644 .travis.yml create mode 100644 tests/Stub/EventListeners.php diff --git a/.gitattributes b/.gitattributes index 047dcb5..68037cf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -40,5 +40,6 @@ /.gitattributes export-ignore /.gitignore export-ignore /.travis.yml export-ignore +/.php_cs export-ignore /phpunit.xml export-ignore /CHANGELOG.md export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index 351baa5..af308f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,22 @@ +# User specific & automatically generated files # +################################################# /composer.lock -/vendor \ No newline at end of file +/vendor +/build/logs + +# IDE and editor specific files # +################################# +/nbproject +.idea + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db +*.mo diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..e66bea1 --- /dev/null +++ b/.php_cs @@ -0,0 +1,31 @@ +level('psr2') + ->fixers(array( + 'header_comment', + 'short_array_syntax', + 'ordered_use', + 'php_unit_construct', + 'php_unit_strict', + 'strict', + 'strict_param', + )) + ->finder( + Symfony\CS\Finder\DefaultFinder::create() + ->exclude('tmp') + ->in(__DIR__) + ) +; \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d394db8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +sudo: false + +before_install: + - composer self-update + +install: + - composer install + - composer require satooshi/php-coveralls '~1.0' + +script: + - php vendor/bin/phpunit --coverage-clover build/logs/clover.xml + +after_script: + - php vendor/bin/coveralls -v diff --git a/README.md b/README.md index d709188..7a17a28 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ -lemonphp/event +Package lemonphp/event === +[![Build Status](https://travis-ci.org/lemonphp/event.svg?branch=master)](https://travis-ci.org/lemonphp/event) +[![Coverage Status](https://coveralls.io/repos/github/lemonphp/event/badge.svg?branch=master)](https://coveralls.io/github/lemonphp/event?branch=master) + A simple event dispatcher Usage --- + ``` -use Lemonphp\Event\Event; -use Lemonphp\Event\Dispatcher; +use Lemon\Event\Event; +use Lemon\Event\Dispatcher; $dispatcher = new Dispatcher(); diff --git a/composer.json b/composer.json index aaa5ff3..e11a8e6 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "lemonphp/event", - "version": "1.0.0", + "type": "library", "description": "Simple event dispatcher", "keywords": ["event", "event-dispatcher"], "license": "MIT", @@ -24,6 +24,6 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "~4.5" + "phpunit/phpunit": "~4.8" } } diff --git a/phpunit.xml b/phpunit.xml index 049286c..44a282b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -19,15 +19,18 @@ - ./tests + tests - ./vendor/ - bin/ + vendor + bin + + src + diff --git a/src/Dispatcher.php b/src/Dispatcher.php index 6e3e660..9e2ec9b 100644 --- a/src/Dispatcher.php +++ b/src/Dispatcher.php @@ -1,5 +1,14 @@ getListeners($eventName)) { - $this->doDispatch($listeners, $eventName, $event); + if ($listeners = $this->getListeners($event->getEventName())) { + $this->doDispatch($listeners, $event); } return $event; @@ -48,6 +57,11 @@ public function off($eventName, $listener = null) return; } + if (is_null($listener)) { + unset($this->listeners[$eventName], $this->sorted[$eventName]); + return; + } + foreach ($this->listeners[$eventName] as $priority => $listeners) { if (false !== ($key = array_search($listener, $listeners, true))) { unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); @@ -119,13 +133,12 @@ public function getListenerPriority($eventName, $listener) * for each listener. * * @param callable[] $listeners The event listeners. - * @param string $eventName The name of the event to dispatch. * @param Event $event The event object to pass to the event handlers/listeners. */ - protected function doDispatch($listeners, $eventName, Event $event) + protected function doDispatch($listeners, Event $event) { foreach ($listeners as $listener) { - call_user_func($listener, $event, $eventName, $this); + call_user_func($listener, $event); if ($event->isPropagationStopped()) { break; } diff --git a/src/DispatcherInterface.php b/src/DispatcherInterface.php index 3539dc8..850b690 100644 --- a/src/DispatcherInterface.php +++ b/src/DispatcherInterface.php @@ -1,5 +1,14 @@ name = (string) $name; + $this->eventName = (string) $name; } /** @@ -48,8 +57,8 @@ public function stopPropagation() /** * @return string */ - public function getName() + public function getEventName() { - return $this->name; + return $this->eventName; } } diff --git a/tests/DispatcherTest.php b/tests/DispatcherTest.php index 89fbfd3..b7101a4 100644 --- a/tests/DispatcherTest.php +++ b/tests/DispatcherTest.php @@ -1,17 +1,37 @@ dispatcher = new Dispatcher(); + $this->listeners = new EventListeners(); } /** @@ -31,10 +52,185 @@ protected function tearDown() } /** - * + * Test initinal state of dispatcher */ public function testInitialState() { - $this->markTestIncomplete(); + $this->assertSame([], $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::PRE_FOO)); + $this->assertFalse($this->dispatcher->hasListeners(self::POST_FOO)); + } + + public function testOn() + { + $this->dispatcher->on(self::PRE_FOO, [$this->listeners, 'preFoo']); + $this->dispatcher->on(self::POST_FOO, [$this->listeners, 'postFoo']); + + $this->assertTrue($this->dispatcher->hasListeners(self::PRE_FOO)); + $this->assertTrue($this->dispatcher->hasListeners(self::POST_FOO)); + $this->assertFalse($this->dispatcher->hasListeners(self::PRE_BAR)); + $this->assertCount(1, $this->dispatcher->getListeners(self::PRE_FOO)); + $this->assertCount(1, $this->dispatcher->getListeners(self::POST_FOO)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testOnWithPriority() + { + $this->dispatcher->on(self::PRE_FOO, [$this->listeners, 'preFoo'], 10); + + $this->assertTrue($this->dispatcher->hasListeners(self::PRE_FOO)); + $this->assertCount(1, $this->dispatcher->getListeners(self::PRE_FOO)); + $this->assertSame(10, $this->dispatcher->getListenerPriority(self::PRE_FOO, [$this->listeners, 'preFoo'])); + } + + public function testOff() + { + $listener1 = new EventListeners(); + $listener2 = new EventListeners(); + + $this->dispatcher->on(self::PRE_FOO, [$listener1, 'preFoo']); + $this->dispatcher->on(self::PRE_FOO, [$listener2, 'preFoo']); + $this->dispatcher->on(self::PRE_FOO, [$this->listeners, 'preFoo']); + $this->assertCount(3, $this->dispatcher->getListeners(self::PRE_FOO)); + + $this->dispatcher->off(self::PRE_FOO, [$this->listeners, 'preFoo']); + $this->assertCount(2, $this->dispatcher->getListeners(self::PRE_FOO)); + $this->assertNull($this->dispatcher->getListenerPriority(self::PRE_FOO, [$this->listeners, 'preFoo'])); + + $this->dispatcher->off('noevent'); + $this->assertCount(2, $this->dispatcher->getListeners(self::PRE_FOO)); + + $this->dispatcher->off(self::PRE_FOO); + $this->assertFalse($this->dispatcher->hasListeners(self::PRE_FOO)); + } + + public function testGetListenersSortByPriority() + { + $listener1 = new EventListeners(); + $listener2 = new EventListeners(); + $listener3 = new EventListeners(); + $this->dispatcher->on(self::PRE_FOO, [$listener1, 'preFoo'], -10); + $this->dispatcher->on(self::PRE_FOO, [$listener2, 'preFoo'], 10); + $this->dispatcher->on(self::PRE_FOO, [$listener3, 'preFoo']); + + $expected = [ + [$listener2, 'preFoo'], + [$listener3, 'preFoo'], + [$listener1, 'preFoo'], + ]; + + $this->assertSame($expected, $this->dispatcher->getListeners(self::PRE_FOO)); + } + + public function testGetAllListenersSortByPriority() + { + $listener1 = new EventListeners(); + $listener2 = new EventListeners(); + $listener3 = new EventListeners(); + $this->dispatcher->on(self::PRE_FOO, [$listener1, 'preFoo'], -10); + $this->dispatcher->on(self::PRE_FOO, [$listener2, 'preFoo']); + $this->dispatcher->on(self::PRE_FOO, [$listener3, 'preFoo'], 10); + $this->dispatcher->on(self::POST_FOO, [$listener1, 'postFoo'], -10); + $this->dispatcher->on(self::POST_FOO, [$listener2, 'postFoo']); + $this->dispatcher->on(self::POST_FOO, [$listener3, 'postFoo'], 10); + + $expected = [ + 'pre.foo' => [[$listener3, 'preFoo'], [$listener2, 'preFoo'], [$listener1, 'preFoo']], + 'post.foo' => [[$listener3, 'postFoo'], [$listener2, 'postFoo'], [$listener1, 'postFoo']], + ]; + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testGetListenerPriority() + { + $listener1 = new EventListeners(); + $listener2 = new EventListeners(); + $this->dispatcher->on(self::PRE_FOO, [$listener1, 'preFoo'], -10); + $this->dispatcher->on(self::PRE_FOO, [$listener2, 'preFoo']); + + $this->assertSame(-10, $this->dispatcher->getListenerPriority(self::PRE_FOO, [$listener1, 'preFoo'])); + $this->assertSame(0, $this->dispatcher->getListenerPriority(self::PRE_FOO, [$listener2, 'preFoo'])); + $this->assertNull($this->dispatcher->getListenerPriority(self::PRE_BAR, [$listener1, 'preFoo'])); + $this->assertNull($this->dispatcher->getListenerPriority(self::PRE_FOO, function() {})); + } + + public function testTrigger() + { + $this->dispatcher->on(self::PRE_FOO, [$this->listeners, 'preFoo']); + $this->dispatcher->on(self::POST_FOO, [$this->listeners, 'postFoo']); + $this->dispatcher->trigger(self::PRE_FOO); + $event = new Event('test.event'); + + $this->assertTrue($this->listeners->preFooInvoked); + $this->assertFalse($this->listeners->postFooInvoked); + $this->assertInstanceOf('\Lemon\Event\Event', $this->dispatcher->trigger('noevent')); + $this->assertInstanceOf('\Lemon\Event\Event', $this->dispatcher->trigger(self::PRE_FOO)); + $this->assertSame($event, $this->dispatcher->trigger($event)); + } + + public function testTriggerForClosure() + { + $invoked = '|'; + $listener = function (Event $e) use (&$invoked) { + $invoked .= ($e->getEventName() . '|'); + }; + + $this->dispatcher->on(self::PRE_FOO, $listener); + $this->dispatcher->on(self::POST_FOO, $listener); + $this->dispatcher->trigger(self::PRE_FOO); + + $this->assertEquals('|pre.foo|', $invoked); + } + + public function testTriggerOrderByPriority() + { + $invoked = []; + $listener1 = function() use (&$invoked) { + $invoked[] = 1; + }; + $listener2 = function() use (&$invoked) { + $invoked[] = 2; + }; + $listener3 = function() use (&$invoked) { + $invoked[] = 3; + }; + + $this->dispatcher->on(self::PRE_FOO, $listener1, -10); + $this->dispatcher->on(self::PRE_FOO, $listener2); + $this->dispatcher->on(self::PRE_FOO, $listener3, 10); + $this->dispatcher->trigger(self::PRE_FOO); + + $this->assertEquals([3, 2, 1], $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new EventListeners(); + + $this->dispatcher->on(self::POST_FOO, [$this->listeners, 'postFoo'], 10); + $this->dispatcher->on(self::POST_FOO, [$otherListener, 'postFoo']); + $this->dispatcher->trigger(self::POST_FOO); + + $this->assertTrue($this->listeners->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testListenerArgument() + { + $args = []; + $listener = function() use (&$args) { + $args = func_get_args(); + }; + $event = new Event(self::PRE_FOO); + + $this->dispatcher->on(self::PRE_FOO, $listener); + $this->dispatcher->trigger(self::PRE_FOO); + + $this->assertCount(1, $args); + $this->assertInstanceOf('\Lemon\Event\Event', $args[0]); + + $this->dispatcher->trigger($event); + $this->assertSame($event, $args[0]); } } diff --git a/tests/EventTest.php b/tests/EventTest.php index ce6a5c7..6c59767 100644 --- a/tests/EventTest.php +++ b/tests/EventTest.php @@ -1,6 +1,15 @@ event = null; } + /** + * Test Event::isPropagationStopped() method + */ public function testIsPropagationStopped() { $this->assertFalse($this->event->isPropagationStopped()); } + /** + * Test Event::stopPropagation() method + */ public function testStopPropagationAndIsPropagationStopped() { $this->event->stopPropagation(); $this->assertTrue($this->event->isPropagationStopped()); } - public function testGetName() + /** + * Test Event::getEventName() method + */ + public function testGetEventName() { - $this->assertSame('event.test', $this->event->getName()); + $this->assertSame('event.test', $this->event->getEventName()); } } diff --git a/tests/Stub/EventListeners.php b/tests/Stub/EventListeners.php new file mode 100644 index 0000000..4ec5356 --- /dev/null +++ b/tests/Stub/EventListeners.php @@ -0,0 +1,31 @@ +preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + $e->stopPropagation(); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 2da5a96..8e58657 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,6 +1,14 @@