Skip to content

Commit

Permalink
Added globalDispatchFirst option
Browse files Browse the repository at this point in the history
  • Loading branch information
enumag authored and fprochazka committed Oct 30, 2015
1 parent 9b7aec1 commit fc82cd1
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 6 deletions.
33 changes: 33 additions & 0 deletions docs/en/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,39 @@ FatListener::orderSuccess
SlimListener::slowOrderSuccess
```


Dispatch Order
--------------

When using Nette Events properties you can bind a normal callback and a listener from Doctrine Events to the same event. In this case the callbacks are invoked first by default. In some cases, for example when you use a redirect in the callback, you might want to reverse the order to call global listeners first.

You can change the default behaviour for all events in your config.neon.

```yml
events:
globalDispatchFirst: on
```

You can also change the behaviour for one event only using annotation.

```php
class OrderProcess extends Nette\Object
{
/**
* This event will always dispatch the global listeners first.
* @globalDispatchFirst
*/
public $onStartup = array();

/**
* This event will always dispatch the callbacks first even if you changed the default behaviour in config.neon.
* @globalDispatchFirst false
*/
public $onSuccess = array();
}
```


Debugging
---------

Expand Down
17 changes: 15 additions & 2 deletions src/Kdyby/Events/DI/EventsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@ class EventsExtension extends Nette\DI\CompilerExtension
'optimize' => TRUE,
'debugger' => '%debugMode%',
'exceptionHandler' => NULL,
'globalDispatchFirst' => FALSE,
);

/**
* @var array
*/
private $loadedConfig;

/**
* @var array
*/
Expand Down Expand Up @@ -107,14 +113,16 @@ public function loadConfiguration()
->setClass('Symfony\Component\EventDispatcher\EventDispatcherInterface')
->setFactory('Kdyby\Events\SymfonyDispatcher');
}

$this->loadedConfig = $config;
}



public function beforeCompile()
{
$builder = $this->getContainerBuilder();
$config = $this->getConfig($this->defaults);
$config = $this->loadedConfig;

$manager = $builder->getDefinition($this->prefix('manager'));
foreach (array_keys($builder->findByTag(self::SUBSCRIBER_TAG)) as $serviceName) {
Expand All @@ -128,6 +136,7 @@ public function beforeCompile()

Nette\Utils\Validators::assertField($config, 'autowire', 'bool');
if ($config['autowire']) {
Nette\Utils\Validators::assertField($config, 'globalDispatchFirst', 'bool');
$this->autowireEvents($builder);
}

Expand Down Expand Up @@ -322,7 +331,11 @@ protected function bindEventProperties(Nette\DI\ServiceDefinition $def, Nette\Re
$def->addSetup('$' . $name, array(
new Nette\DI\Statement($this->prefix('@manager') . '::createEvent', array(
array($class->getName(), $name),
new Code\PhpLiteral('$service->' . $name)
new Code\PhpLiteral('$service->' . $name),
NULL,
$property->hasAnnotation('globalDispatchFirst')
? (bool) $property->getAnnotation('globalDispatchFirst')
: $this->loadedConfig['globalDispatchFirst'],
))
));
}
Expand Down
18 changes: 17 additions & 1 deletion src/Kdyby/Events/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@
class Event implements \ArrayAccess, \IteratorAggregate, \Countable
{

/**
* Changes the order of listeners being invoked,
* The default is that the closures and listeners registered directly are first,
* but this property can change that, so the global is first.
*
* @var bool
*/
public $globalDispatchFirst = FALSE;

/**
* @var callable[]
*/
Expand Down Expand Up @@ -170,7 +179,7 @@ public function getListeners()
$name = $this->getName();
$evm = $this->eventManager;
$argsClass = $this->argsClass;
$listeners[] = function () use ($name, $evm, $argsClass) {
$globalDispatch = function () use ($name, $evm, $argsClass) {
if ($argsClass === NULL) {
$args = new EventArgsList(func_get_args());

Expand All @@ -181,6 +190,13 @@ public function getListeners()
$evm->dispatchEvent($name, $args);
};

if ($this->globalDispatchFirst) {
array_unshift($listeners, $globalDispatch);

} else {
$listeners[] = $globalDispatch;
}

return $listeners;
}

Expand Down
4 changes: 3 additions & 1 deletion src/Kdyby/Events/EventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,13 @@ public function removeEventSubscriber(EventSubscriber $subscriber)
* @param string|array $name
* @param array $defaults
* @param string $argsClass
* @param bool $globalDispatchFirst
* @return Event
*/
public function createEvent($name, $defaults = array(), $argsClass = NULL)
public function createEvent($name, $defaults = array(), $argsClass = NULL, $globalDispatchFirst = FALSE)
{
$event = new Event($name, $defaults, $argsClass);
$event->globalDispatchFirst = $globalDispatchFirst;
$event->injectEventManager($this);

if ($this->panel) {
Expand Down
4 changes: 2 additions & 2 deletions src/Kdyby/Events/NamespacedEventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ public function setExceptionHandler(IExceptionHandler $exceptionHandler)
/**
* {@inheritDoc}
*/
public function createEvent($name, $defaults = array(), $argsClass = NULL)
public function createEvent($name, $defaults = array(), $argsClass = NULL, $globalDispatchFirst = FALSE)
{
return $this->evm->createEvent($this->namespace . $name, $defaults, $argsClass);
return $this->evm->createEvent($this->namespace . $name, $defaults, $argsClass, $globalDispatchFirst);
}

}
50 changes: 50 additions & 0 deletions tests/KdybyTests/Events/Event.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,56 @@ class EventTest extends Tester\TestCase
Assert::same(4, $args->int);
}



public function testDispatchOrderGlobalFirst()
{
$listener = new EventListenerMock();
$evm = new Kdyby\Events\EventManager();
$evm->addEventSubscriber($listener);

$event = new Event('onFoo');
$event->injectEventManager($evm);
$event->globalDispatchFirst = TRUE;

$event[] = function () use ($listener) {
$listener->calls[] = __METHOD__;
};

$args = new EventArgsMock();
$event->dispatch($args);

Assert::same(array(
array('KdybyTests\Events\EventListenerMock::onFoo', array($args)),
'KdybyTests\Events\EventTest::KdybyTests\Events\{closure}',
), $listener->calls);
}



public function testDispatchOrderGlobalLast()
{
$listener = new EventListenerMock();
$evm = new Kdyby\Events\EventManager();
$evm->addEventSubscriber($listener);

$event = new Event('onFoo');
$event->injectEventManager($evm);
$event->globalDispatchFirst = FALSE;

$event[] = function () use ($listener) {
$listener->calls[] = __METHOD__;
};

$args = new EventArgsMock();
$event->dispatch($args);

Assert::same(array(
'KdybyTests\Events\EventTest::KdybyTests\Events\{closure}',
array('KdybyTests\Events\EventListenerMock::onFoo', array($args)),
), $listener->calls);
}

}

\run(new EventTest());
27 changes: 27 additions & 0 deletions tests/KdybyTests/Events/Extension.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,34 @@ class ExtensionTest extends Tester\TestCase
$foo3 = $fooFactory->create();
Assert::type('Kdyby\Events\Event', $foo3->onBar);
Assert::notSame($foo, $foo3);
}



public function testGlobalDispatchFirst()
{
$container = $this->createContainer('globalDispatchFirst');
$manager = $container->getService('events.manager');
/** @var Kdyby\Events\EventManager $manager */

$mock = $container->getService('dispatchOrderMock');
Assert::true($mock->onGlobalDispatchFirst->globalDispatchFirst);
Assert::false($mock->onGlobalDispatchLast->globalDispatchFirst);
Assert::true($mock->onGlobalDispatchDefault->globalDispatchFirst);
}



public function testGlobalDispatchLast()
{
$container = $this->createContainer('globalDispatchLast');
$manager = $container->getService('events.manager');
/** @var Kdyby\Events\EventManager $manager */

$mock = $container->getService('dispatchOrderMock');
Assert::true($mock->onGlobalDispatchFirst->globalDispatchFirst);
Assert::false($mock->onGlobalDispatchLast->globalDispatchFirst);
Assert::false($mock->onGlobalDispatchDefault->globalDispatchFirst);
}

}
Expand Down
5 changes: 5 additions & 0 deletions tests/KdybyTests/Events/config/globalDispatchFirst.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
events:
globalDispatchFirst: yes

services:
dispatchOrderMock: KdybyTests\Events\DispatchOrderMock
2 changes: 2 additions & 0 deletions tests/KdybyTests/Events/config/globalDispatchLast.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
services:
dispatchOrderMock: KdybyTests\Events\DispatchOrderMock
24 changes: 24 additions & 0 deletions tests/KdybyTests/Events/mocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,27 @@ public function onCreate()
$this->eventCalls[] = func_get_args();
}
}



class DispatchOrderMock extends Nette\Object
{

/**
* @globalDispatchFirst
* @var array|callable[]|Event
*/
public $onGlobalDispatchFirst = array();

/**
* @globalDispatchFirst false
* @var array|callable[]|Event
*/
public $onGlobalDispatchLast = array();

/**
* @var array|callable[]|Event
*/
public $onGlobalDispatchDefault = array();

}

0 comments on commit fc82cd1

Please sign in to comment.