Merge branch '2.8'

* 2.8:
  [Yaml] deprecated non-escaped \ in double-quoted strings when parsing
  removed wrong dep
  [EventDispatcher] added EventDispatcher::getListenerPriority()
This commit is contained in:
Fabien Potencier 2015-10-12 12:22:36 +02:00
commit 133bd0a274
14 changed files with 166 additions and 120 deletions

View File

@ -269,27 +269,21 @@ class JsonDescriptor extends Descriptor
{
$data = array();
$registeredListeners = $eventDispatcher->getListeners($event, true);
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
krsort($registeredListeners);
foreach ($registeredListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$listener = $this->getCallableData($listener);
$listener['priority'] = $priority;
$data[] = $listener;
}
foreach ($registeredListeners as $listener) {
$l = $this->getCallableData($listener);
$l['priority'] = $eventDispatcher->getListenerPriority($event, $listener);
$data[] = $l;
}
} else {
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
krsort($eventListeners);
foreach ($eventListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$listener = $this->getCallableData($listener);
$listener['priority'] = $priority;
$data[$eventListened][] = $listener;
}
foreach ($eventListeners as $eventListener) {
$l = $this->getCallableData($eventListener);
$l['priority'] = $eventDispatcher->getListenerPriority($eventListened, $eventListener);
$data[$eventListened][] = $l;
}
}
}

View File

@ -253,30 +253,23 @@ class MarkdownDescriptor extends Descriptor
$this->write(sprintf('# %s', $title)."\n");
$registeredListeners = $eventDispatcher->getListeners($event, true);
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
krsort($registeredListeners);
$order = 1;
foreach ($registeredListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$this->write("\n".sprintf('## Listener %d', $order++)."\n");
$this->describeCallable($listener);
$this->write(sprintf('- Priority: `%d`', $priority)."\n");
}
foreach ($registeredListeners as $order => $listener) {
$this->write("\n".sprintf('## Listener %d', $order + 1)."\n");
$this->describeCallable($listener);
$this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($event, $listener))."\n");
}
} else {
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
$this->write("\n".sprintf('## %s', $eventListened)."\n");
krsort($eventListeners);
$order = 1;
foreach ($eventListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$this->write("\n".sprintf('### Listener %d', $order++)."\n");
$this->describeCallable($listener);
$this->write(sprintf('- Priority: `%d`', $priority)."\n");
}
foreach ($eventListeners as $order => $eventListener) {
$this->write("\n".sprintf('### Listener %d', $order + 1)."\n");
$this->describeCallable($eventListener);
$this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($eventListened, $eventListener))."\n");
}
}
}

View File

@ -341,15 +341,14 @@ class TextDescriptor extends Descriptor
$options['output']->title($title);
$registeredListeners = $eventDispatcher->getListeners($event, true);
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
$this->renderEventListenerTable($registeredListeners, $options['output']);
$this->renderEventListenerTable($eventDispatcher, $event, $registeredListeners, $options['output']);
} else {
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
$options['output']->section(sprintf('"%s" event', $eventListened));
$this->renderEventListenerTable($eventListeners, $options['output']);
$this->renderEventListenerTable($eventDispatcher, $eventListened, $eventListeners, $options['output']);
}
}
}
@ -365,17 +364,14 @@ class TextDescriptor extends Descriptor
/**
* @param array $array
*/
private function renderEventListenerTable(array $eventListeners, SymfonyStyle $renderer)
private function renderEventListenerTable(EventDispatcherInterface $eventDispatcher, $event, array $eventListeners, SymfonyStyle $renderer)
{
$tableHeaders = array('Order', 'Callable', 'Priority');
$tableRows = array();
krsort($eventListeners);
$order = 1;
foreach ($eventListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$tableRows[] = array(sprintf('#%d', $order++), $this->formatCallable($listener), $priority);
}
foreach ($eventListeners as $order => $listener) {
$tableRows[] = array(sprintf('#%d', $order + 1), $this->formatCallable($listener), $eventDispatcher->getListenerPriority($event, $listener));
}
$renderer->table($tableHeaders, $tableRows);

View File

@ -429,9 +429,9 @@ class XmlDescriptor extends Descriptor
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher'));
$registeredListeners = $eventDispatcher->getListeners($event, true);
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
$this->appendEventListenerDocument($eventDispatcherXML, $registeredListeners);
$this->appendEventListenerDocument($eventDispatcher, $event, $eventDispatcherXML, $registeredListeners);
} else {
ksort($registeredListeners);
@ -439,7 +439,7 @@ class XmlDescriptor extends Descriptor
$eventDispatcherXML->appendChild($eventXML = $dom->createElement('event'));
$eventXML->setAttribute('name', $eventListened);
$this->appendEventListenerDocument($eventXML, $eventListeners);
$this->appendEventListenerDocument($eventDispatcher, $eventListened, $eventXML, $eventListeners);
}
}
@ -450,16 +450,13 @@ class XmlDescriptor extends Descriptor
* @param \DOMElement $element
* @param array $eventListeners
*/
private function appendEventListenerDocument(\DOMElement $element, array $eventListeners)
private function appendEventListenerDocument(EventDispatcherInterface $eventDispatcher, $event, \DOMElement $element, array $eventListeners)
{
krsort($eventListeners);
foreach ($eventListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$callableXML = $this->getCallableDocument($listener);
$callableXML->childNodes->item(0)->setAttribute('priority', $priority);
foreach ($eventListeners as $listener) {
$callableXML = $this->getCallableDocument($listener);
$callableXML->childNodes->item(0)->setAttribute('priority', $eventDispatcher->getListenerPriority($event, $listener));
$element->appendChild($element->ownerDocument->importNode($callableXML->childNodes->item(0), true));
}
$element->appendChild($element->ownerDocument->importNode($callableXML->childNodes->item(0), true));
}
}

View File

@ -117,7 +117,7 @@ class ContainerAwareEventDispatcher extends EventDispatcher
/**
* {@inheritdoc}
*/
public function getListeners($eventName = null, $withPriorities = false)
public function getListeners($eventName = null)
{
if (null === $eventName) {
foreach ($this->listenerIds as $serviceEventName => $args) {
@ -127,7 +127,17 @@ class ContainerAwareEventDispatcher extends EventDispatcher
$this->lazyLoad($eventName);
}
return parent::getListeners($eventName, $withPriorities);
return parent::getListeners($eventName);
}
/**
* {@inheritdoc}
*/
public function getListenerPriority($eventName, $listener)
{
$this->lazyLoad($eventName);
return parent::getListenerPriority($eventName, $listener);
}
/**

View File

@ -94,9 +94,17 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
/**
* {@inheritdoc}
*/
public function getListeners($eventName = null, $withPriorities = false)
public function getListeners($eventName = null)
{
return $this->dispatcher->getListeners($eventName, $withPriorities);
return $this->dispatcher->getListeners($eventName);
}
/**
* {@inheritdoc}
*/
public function getListenerPriority($eventName, $listener)
{
return $this->dispatcher->getListenerPriority($eventName, $listener);
}
/**
@ -141,8 +149,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
$called = array();
foreach ($this->called as $eventName => $listeners) {
foreach ($listeners as $listener) {
$priority = $this->getListenerPriority($eventName, $listener);
$info = $this->getListenerInfo($listener->getWrappedListener(), $eventName, $priority);
$info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
$called[$eventName.'.'.$info['pretty']] = $info;
}
}
@ -156,7 +163,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
public function getNotCalledListeners()
{
try {
$allListeners = $this->getListeners(null, true);
$allListeners = $this->getListeners();
} catch (\Exception $e) {
if (null !== $this->logger) {
$this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e));
@ -167,24 +174,22 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
}
$notCalled = array();
foreach ($allListeners as $eventName => $priorities) {
foreach ($priorities as $priority => $listeners) {
foreach ($listeners as $listener) {
$called = false;
if (isset($this->called[$eventName])) {
foreach ($this->called[$eventName] as $l) {
if ($l->getWrappedListener() === $listener) {
$called = true;
foreach ($allListeners as $eventName => $listeners) {
foreach ($listeners as $listener) {
$called = false;
if (isset($this->called[$eventName])) {
foreach ($this->called[$eventName] as $l) {
if ($l->getWrappedListener() === $listener) {
$called = true;
break;
}
break;
}
}
}
if (!$called) {
$info = $this->getListenerInfo($listener, $eventName, $priority);
$notCalled[$eventName.'.'.$info['pretty']] = $info;
}
if (!$called) {
$info = $this->getListenerInfo($listener, $eventName);
$notCalled[$eventName.'.'.$info['pretty']] = $info;
}
}
}
@ -286,11 +291,11 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
*
* @return array Information about the listener
*/
private function getListenerInfo($listener, $eventName, $priority = null)
private function getListenerInfo($listener, $eventName)
{
$info = array(
'event' => $eventName,
'priority' => $priority,
'priority' => $this->getListenerPriority($eventName, $listener),
);
if ($listener instanceof \Closure) {
$info += array(
@ -339,28 +344,6 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
return $info;
}
private function getListenerPriority($eventName, $listenerConfig)
{
try {
$allListeners = $this->getListeners(null, true);
} catch (\Exception $e) {
if (null !== $this->logger) {
$this->logger->info('An exception was thrown while getting the listeners.', array('exception' => $e));
}
return;
}
$listenerWrapper = $listenerConfig->getWrappedListener();
foreach ($allListeners[$eventName] as $priority => $listeners) {
foreach ($listeners as $listener) {
if (is_array($listenerWrapper) && $listenerWrapper[0] === $listener[0]) {
return $priority;
}
}
}
}
private function sortListenersByPriority($a, $b)
{
if (is_int($a['priority']) && !is_int($b['priority'])) {

View File

@ -49,16 +49,8 @@ class EventDispatcher implements EventDispatcherInterface
/**
* {@inheritdoc}
*/
public function getListeners($eventName = null, $withPriorities = false)
public function getListeners($eventName = null)
{
if (true === $withPriorities) {
if (null !== $eventName) {
return isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : array();
}
return array_filter($this->listeners);
}
if (null !== $eventName) {
if (!isset($this->listeners[$eventName])) {
return array();
@ -80,6 +72,29 @@ class EventDispatcher implements EventDispatcherInterface
return array_filter($this->sorted);
}
/**
* Gets the listener priority for a specific event.
*
* Returns null if the event or the listener does not exist.
*
* @param string $eventName The name of the event
* @param callable $listener The listener to remove
*
* @return int|null The event listener priority
*/
public function getListenerPriority($eventName, $listener)
{
if (!isset($this->listeners[$eventName])) {
return;
}
foreach ($this->listeners[$eventName] as $priority => $listeners) {
if (false !== ($key = array_search($listener, $listeners, true))) {
return $priority;
}
}
}
/**
* {@inheritdoc}
*/
@ -174,8 +189,6 @@ class EventDispatcher implements EventDispatcherInterface
*/
private function sortListeners($eventName)
{
$this->sorted[$eventName] = array();
krsort($this->listeners[$eventName]);
$this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
}

View File

@ -78,9 +78,17 @@ class ImmutableEventDispatcher implements EventDispatcherInterface
/**
* {@inheritdoc}
*/
public function getListeners($eventName = null, $withPriorities = false)
public function getListeners($eventName = null)
{
return $this->dispatcher->getListeners($eventName, $withPriorities);
return $this->dispatcher->getListeners($eventName);
}
/**
* {@inheritdoc}
*/
public function getListenerPriority($eventName, $listener)
{
return $this->dispatcher->getListenerPriority($eventName, $listener);
}
/**

View File

@ -108,6 +108,20 @@ abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->assertSame($expected, $this->dispatcher->getListeners());
}
public function testGetListenerPriority()
{
$listener1 = new TestEventListener();
$listener2 = new TestEventListener();
$this->dispatcher->addListener('pre.foo', $listener1, -10);
$this->dispatcher->addListener('pre.foo', $listener2);
$this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1));
$this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2));
$this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2));
$this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {}));
}
public function testDispatch()
{
$this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo'));

View File

@ -25,7 +25,7 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
$tdispatcher->addListener('foo', $listener = function () {; });
$tdispatcher->addListener('foo', $listener = function () {});
$listeners = $dispatcher->getListeners('foo');
$this->assertCount(1, $listeners);
$this->assertSame($listener, $listeners[0]);
@ -39,7 +39,7 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
$tdispatcher->addListener('foo', $listener = function () {; });
$tdispatcher->addListener('foo', $listener = function () {});
$this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo'));
}
@ -51,7 +51,7 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($dispatcher->hasListeners('foo'));
$this->assertFalse($tdispatcher->hasListeners('foo'));
$tdispatcher->addListener('foo', $listener = function () {; });
$tdispatcher->addListener('foo', $listener = function () {});
$this->assertTrue($dispatcher->hasListeners('foo'));
$this->assertTrue($tdispatcher->hasListeners('foo'));
}
@ -76,7 +76,7 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
{
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
$tdispatcher->addListener('foo', $listener = function () {; });
$tdispatcher->addListener('foo', $listener = function () {});
$this->assertEquals(array(), $tdispatcher->getCalledListeners());
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $tdispatcher->getNotCalledListeners());
@ -107,8 +107,8 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
$tdispatcher->addListener('foo', $listener1 = function () {; });
$tdispatcher->addListener('foo', $listener2 = function () {; });
$tdispatcher->addListener('foo', $listener1 = function () {});
$tdispatcher->addListener('foo', $listener2 = function () {});
$logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".');
$logger->expects($this->at(1))->method('debug')->with('Notified event "foo" to listener "closure".');
@ -123,7 +123,7 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
$tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
$tdispatcher->addListener('foo', $listener2 = function () {; });
$tdispatcher->addListener('foo', $listener2 = function () {});
$logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".');
$logger->expects($this->at(1))->method('debug')->with('Listener "closure" stopped propagation of the event "foo".');

View File

@ -1,6 +1,12 @@
CHANGELOG
=========
2.8.0
-----
* Deprecated non-escaped \ in double-quoted strings when parsing Yaml
("Foo\Var" is not valid whereas "Foo\\Var" is)
2.1.0
-----

View File

@ -145,3 +145,11 @@ php: |
array(
'double' => "some value\n \"some quoted string\" and 'some single quotes one'"
)
---
test: Backslashes
yaml: |
{ single: 'foo\Var', no-quotes: foo\Var, double: "foo\\Var" }
php: |
array(
'single' => 'foo\Var', 'no-quotes' => 'foo\Var', 'double' => 'foo\Var'
)

View File

@ -70,6 +70,23 @@ class InlineTest extends \PHPUnit_Framework_TestCase
$this->assertSame($value, Inline::parse(Inline::dump($value)));
}
/**
* @group legacy
* throws \Symfony\Component\Yaml\Exception\ParseException in 3.0
*/
public function testParseScalarWithNonEscapedBlackslashShouldThrowException()
{
$this->assertSame('Foo\Var', Inline::parse('"Foo\Var"'));
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
public function testParseScalarWithNonEscapedBlackslashAtTheEndShouldThrowException()
{
Inline::parse('"Foo\\"');
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/

View File

@ -22,7 +22,7 @@ class Unescaper
/**
* Regex fragment that matches an escaped character in a double quoted string.
*/
const REGEX_ESCAPED_CHARACTER = "\\\\([0abt\tnvfre \\\"\\/\\\\N_LP]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})";
const REGEX_ESCAPED_CHARACTER = "\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)";
/**
* Unescapes a single quoted string.
@ -59,10 +59,13 @@ class Unescaper
* @param string $value An escaped character
*
* @return string The unescaped character
*
* @internal This method is public to be usable as callback. It should not
* be used in user code. Should be changed in 3.0.
*/
public function unescapeCharacter($value)
{
switch ($value{1}) {
switch ($value[1]) {
case '0':
return "\x0";
case 'a':
@ -109,6 +112,10 @@ class Unescaper
return self::utf8chr(hexdec(substr($value, 2, 4)));
case 'U':
return self::utf8chr(hexdec(substr($value, 2, 8)));
default:
@trigger_error('Not escaping a backslash in a double-quoted string is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', E_USER_DEPRECATED);
return $value;
}
}