21 Commits
Author SHA1 Message Date
Jeremy Mikola cc3257181c Handle preg_quote changes for PHP 7.3 (#14)
* Add PHP 7.3 to Travis

* Prepare preg_replace() replacements by PHP version

PHP 7.3+ requires the pattern to expect preg_quote() to escape `#` characters in the event pattern.
2019-02-12 11:00:06 -05:00
Jeremy Mikola 544fca2b55 Merge pull request #11 2018-04-12 09:45:06 -04:00
Jeremy Mikola f32bd8592f Disable xdebug for Travis CI builds 2018-04-12 09:37:27 -04:00
Jeremy Mikola 392eff7dc6 Add Composer directories to Travis CI cache 2018-04-12 09:34:50 -04:00
Jeremy Mikola bcb29ff342 Add PHP 7.1 and 7.2 test environments 2018-04-12 09:34:50 -04:00
Jeremy Mikola 8733d7382b Add PHPUnit dev dependency and remove HHVM test env 2018-04-12 09:34:50 -04:00
Casey McLaughlin fe0da3a28b Fixed travis.yml for PHP5.3 and PHP7 2018-04-11 14:56:53 -04:00
Casey McLaughlin 5ed48b24cb Added support for Symfony event-dispatcher v4 2018-04-11 14:08:00 -04:00
Jeremy Mikola 65d13be38c Removed unused environment variable 2016-09-22 19:31:47 -04:00
Jeremy Mikola 19dc115e94 Alias dev-master as 1.1.x-dev 2016-09-14 15:37:00 -04:00
Jeremy Mikola 8d53f365b2 Merge pull request #7 2016-09-14 15:36:37 -04:00
Jeremy Mikola f8099a63f8 Add more platforms to Travis build matrix
This ensures we test against old and current Symfony versions
2016-09-09 15:42:24 -04:00
Jeremy Mikola fd3a84d57d Remove minimum-stability from composer.json
This only applies to root projects, and isn't necessary for testing.
2016-09-09 15:36:56 -04:00
Jeremy Mikola 8fcbe9f726 Remove extra whitespace in doc block 2016-09-09 15:31:27 -04:00
Jeremy Mikola 5472039e48 Throw exceptions for getListenerPriority() incompatibilities
For the time being, do not support wildcard patterns with getListenerPriority().

An exception will be thrown if getListenerPriority() is not implemented on the inner dispatcher. The method was added in https://github.com/symfony/symfony/pull/16198 for Symfony 2.8. It was added to the interface in Symfony 3.0, which is relevant for our test cases.
2016-09-09 15:27:43 -04:00
SpacePossum 2d68309b14 Use assertCount() and getMockBuilder() in tests
getMock() was deprecated in PHPUnit 5.4.0.
2016-09-09 15:09:13 -04:00
SpacePossum 83a285207b Use FQCN for InvalidArgumentException in doc block 2016-09-09 15:08:35 -04:00
SpacePossum 0300cbb1a5 Compatibility with Symfony 3.0 2016-09-09 14:49:16 -04:00
Jeremy Mikola 392eb3b559 Add installation notes to readme 2016-09-09 14:37:29 -04:00
Jeremy Mikola a688303be2 Simplify header syntax in readme 2016-09-09 14:36:46 -04:00
Jeremy Mikola 6af1e73df4 Remove "Symfony2" references in readme 2016-09-09 14:36:20 -04:00
10 changed files with 168 additions and 36 deletions
+25 -2
View File
@@ -1,7 +1,30 @@
language: php
dist: trusty
sudo: false
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
before_script: composer install --dev
cache:
directories:
- ${HOME}/.composer/cache/files
matrix:
fast_finish: true
include:
- php: 5.3
dist: precise
env: COMPOSER_FLAGS="--prefer-lowest"
before_script:
- phpenv config-rm xdebug.ini
- composer update --no-interaction --no-progress --no-suggest ${COMPOSER_FLAGS}
script:
- vendor/bin/phpunit -v
+24 -9
View File
@@ -2,21 +2,36 @@
[![Build Status](https://travis-ci.org/jmikola/WildcardEventDispatcher.png?branch=master)](https://travis-ci.org/jmikola/WildcardEventDispatcher)
This library implements an event dispatcher, based on [Symfony2's interface][],
This library implements an event dispatcher, based on [Symfony's interface][],
with wildcard syntax inspired by AMQP topic exchanges. Listeners may be bound to
a wildcard pattern and be notified if a dispatched event's name matches that
pattern. Literal event name matching is still supported.
If you are interested in using this library in a Symfony2 project, you may also
If you are interested in using this library in a Symfony project, you may also
want to take a look at the corresponding [bundle][].
[Symfony2's interface]: https://github.com/symfony/EventDispatcher
[Symfony's interface]: https://github.com/symfony/EventDispatcher
[bundle]: https://github.com/jmikola/JmikolaWildcardEventDispatcherBundle
## Usage ##
## Installation
The library is published as a [package][] and is installable via [Composer][]:
```
$ composer require jmikola/wildcard-event-dispatcher=~1.0
```
[package]: https://packagist.org/packages/jmikola/wildcard-event-dispatcher
[Composer]: http://getcomposer.org/
### Compatibility
This library requires Symfony 2.3 or above.
## Usage
WildcardEventDispatcher implements EventDispatcherInterface and may be used as
you would Symfony2's standard EventDispatcher:
you would Symfony's standard EventDispatcher:
```php
<?php
@@ -50,9 +65,9 @@ $dispatcher = new WildcardEventDispatcher(new EventDispatcher());
[composes]: http://en.wikipedia.org/wiki/Object_composition
## Wildcard Syntax ##
## Wildcard Syntax
### Single-word Wildcard ###
### Single-word Wildcard
Consider the scenario where the same listener is defined for multiple events,
all of which share a common prefix:
@@ -90,7 +105,7 @@ and followed by another word. The matching of `core` alone may not make sense,
but this is implemented in order to be consistent with AMQP. A trailing `*`
after a non-empty sequence may match the preceding sequence sans `.*`.
### Multi-word Wildcard ###
### Multi-word Wildcard
Suppose there was a `core` event in your application named `core.foo.bar`. The
aforementioned `core.*` pattern would not catch this event. You could use:
@@ -130,7 +145,7 @@ $dispatcher = new WildcardEventDispatcher();
$dispatcher->addListener('#', $allListener);
```
### Additional Wildcard Documentation ###
### Additional Wildcard Documentation
When in doubt, the unit tests for `ListenerPattern` are a good resource for
inferring how wildcards will be interpreted. This library aims to mimic the
+9 -2
View File
@@ -8,12 +8,19 @@
"authors": [
{ "name": "Jeremy Mikola", "email": "jmikola@gmail.com" }
],
"minimum-stability": "dev",
"require": {
"php": ">=5.3.2",
"symfony/event-dispatcher": "~2.0"
"symfony/event-dispatcher": "^2.3 || ^3.0 || ^4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36 || ^6.4"
},
"autoload": {
"psr-0": { "Jmikola": "src/" }
},
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
}
}
@@ -15,7 +15,7 @@ class LazyListenerPattern extends ListenerPattern
* @param string $eventPattern
* @param callback $listenerProvider
* @param integer $priority
* @throws InvalidArgumentException if the listener provider is not a callback
* @throws \InvalidArgumentException if the listener provider is not a callback
*/
public function __construct($eventPattern, $listenerProvider, $priority = 0)
{
@@ -12,18 +12,7 @@ class ListenerPattern
protected $priority;
protected $regex;
private static $replacements = array(
// Trailing single-wildcard with separator prefix
'/\\\\\.\\\\\*$/' => '(?:\.\w+)?',
// Single-wildcard with separator prefix
'/\\\\\.\\\\\*/' => '(?:\.\w+)',
// Single-wildcard without separator prefix
'/(?<!\\\\\.)\\\\\*/' => '(?:\w+)',
// Multi-wildcard with separator prefix
'/\\\\\.#/' => '(?:\.\w+)*',
// Multi-wildcard without separator prefix
'/(?<!\\\\\.)#/' => '(?:|\w+(?:\.\w+)*)',
);
private static $replacements;
/**
* Constructor.
@@ -110,10 +99,52 @@ class ListenerPattern
*/
private function createRegex($eventPattern)
{
$replacements = self::getReplacements();
return sprintf('/^%s$/', preg_replace(
array_keys(self::$replacements),
array_values(self::$replacements),
array_keys($replacements),
array_values($replacements),
preg_quote($eventPattern, '/')
));
}
/**
* Returns preg_replace() replacements for preparing the event pattern.
*
* @return array
*/
private static function getReplacements()
{
if (null !== self::$replacements) {
return self::$replacements;
}
self::$replacements = array(
// Trailing single-wildcard with separator prefix
'/\\\\\.\\\\\*$/' => '(?:\.\w+)?',
// Single-wildcard with separator prefix
'/\\\\\.\\\\\*/' => '(?:\.\w+)',
// Single-wildcard without separator prefix
'/(?<!\\\\\.)\\\\\*/' => '(?:\w+)',
);
// preg_quote() escapes `#` in PHP 7.3+
if (PHP_VERSION_ID >= 70300) {
self::$replacements += array(
// Multi-wildcard with separator prefix
'/\\\\\.\\\\\#/' => '(?:\.\w+)*',
// Multi-wildcard without separator prefix
'/(?<!\\\\\.)\\\\\#/' => '(?:|\w+(?:\.\w+)*)',
);
} else {
self::$replacements += array(
// Multi-wildcard with separator prefix
'/\\\\\.#/' => '(?:\.\w+)*',
// Multi-wildcard without separator prefix
'/(?<!\\\\\.)#/' => '(?:|\w+(?:\.\w+)*)',
);
}
return self::$replacements;
}
}
@@ -16,7 +16,7 @@ class WildcardEventDispatcher implements EventDispatcherInterface
/**
* Constructor.
*
* If an EventDispatcherInterface is not provided , a new EventDispatcher
* If an EventDispatcherInterface is not provided, a new EventDispatcher
* will be composed.
*
* @param EventDispatcherInterface $dispatcher
@@ -195,4 +195,22 @@ class WildcardEventDispatcher implements EventDispatcherInterface
}
}
}
/**
* @see EventDispatcherInterface::getListenerPriority()
* @throws \InvalidArgumentException if $eventName contains a wildcard pattern
* @throws \BadMethodCallException if this method is not implemented on the composed EventDispatcher
*/
public function getListenerPriority($eventName, $listener)
{
if ($this->hasWildcards($eventName)) {
throw new \InvalidArgumentException('Wildcard patterns are not supported');
}
if ( ! method_exists($this->dispatcher, 'getListenerPriority')) {
throw new \BadMethodCallException('getListenerPriority() is not implemented');
}
return $this->dispatcher->getListenerPriority($eventName, $listener);
}
}
@@ -3,8 +3,9 @@
namespace Jmikola\Tests\WildcardEventDispatcher;
use Jmikola\WildcardEventDispatcher\LazyListenerPattern;
use PHPUnit\Framework\TestCase;
class LazyListenerPatternTest extends \PHPUnit_Framework_TestCase
class LazyListenerPatternTest extends TestCase
{
/**
* @expectedException InvalidArgumentException
@@ -3,8 +3,9 @@
namespace Jmikola\Tests\WildcardEventDispatcher;
use Jmikola\WildcardEventDispatcher\ListenerPattern;
use PHPUnit\Framework\TestCase;
class ListenerPatternTest extends \PHPUnit_Framework_TestCase
class ListenerPatternTest extends TestCase
{
/**
* @dataProvider providePatternsAndMatches
@@ -86,6 +87,6 @@ class ListenerPatternTest extends \PHPUnit_Framework_TestCase
private function getMockEventDispatcher()
{
return $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
return $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
}
}
@@ -4,8 +4,9 @@ namespace Jmikola\Tests\WildcardEventDispatcher;
use Jmikola\WildcardEventDispatcher\WildcardEventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use PHPUnit\Framework\TestCase;
class WildcardEventDispatcherFunctionalTest extends \PHPUnit_Framework_TestCase
class WildcardEventDispatcherFunctionalTest extends TestCase
{
const coreRequest = 'core.request';
const coreException = 'core.exception';
@@ -127,7 +128,7 @@ class WildcardEventDispatcherFunctionalTest extends \PHPUnit_Framework_TestCase
private function assertNumberListenersAdded($expected, $eventName = null)
{
return isset($eventName)
? $this->assertEquals($expected, count($this->dispatcher->getListeners($eventName)))
? $this->assertCount($expected, $this->dispatcher->getListeners($eventName))
: $this->assertEquals($expected, array_sum(array_map('count', $this->dispatcher->getListeners())));
}
}
@@ -4,8 +4,9 @@ namespace Jmikola\Tests\WildcardEventDispatcher;
use Jmikola\WildcardEventDispatcher\WildcardEventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use PHPUnit\Framework\TestCase;
class EventDispatcherTest extends \PHPUnit_Framework_TestCase
class EventDispatcherTest extends TestCase
{
private $dispatcher;
private $innerDispatcher;
@@ -146,9 +147,43 @@ class EventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->dispatcher->removeSubscriber($subscriber);
}
public function testGetListenerPriorityInvokesMethodOnInnerDispather()
{
if ( ! method_exists('Symfony\Component\EventDispatcher\EventDispatcherInterface', 'getListenerPriority')) {
$this->markTestSkipped('getListenerPriority() does not exist on EventDispatcherInterface');
}
$this->innerDispatcher->expects($this->once())
->method('getListenerPriority')
->with('core.request', 'callback')
->will($this->returnValue(1));
$this->assertSame(1, $this->dispatcher->getListenerPriority('core.request', 'callback'));
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetListenerPriorityRequiresEventNameWithoutWildcards()
{
$this->dispatcher->getListenerPriority('core.*', 'callback');
}
/**
* @expectedException BadMethodCallException
*/
public function testGetListenerPriorityRequiresMethodOnInnerDispather()
{
if (method_exists('Symfony\Component\EventDispatcher\EventDispatcherInterface', 'getListenerPriority')) {
$this->markTestSkipped('getListenerPriority() exists on EventDispatcherInterface');
}
$this->dispatcher->getListenerPriority('core.request', 'callback');
}
private function getMockEventDispatcher()
{
return $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
return $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
}
}