From 294868e5c0b37582c343521a601359421fc89247 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 20 Sep 2016 10:42:22 +0200 Subject: [PATCH] [Form][EventDispatcher] Fix VarDumper usage related to perf regression --- .../Debug/TraceableEventDispatcher.php | 97 ++--------- .../EventDispatcher/Debug/WrappedListener.php | 45 +++++ .../Debug/TraceableEventDispatcherTest.php | 6 +- .../DataCollector/FormDataCollector.php | 119 +++++++++++++ .../DataCollector/FormDataExtractor.php | 91 ++-------- .../DataCollector/FormDataExtractorTest.php | 157 ++++++++---------- .../DataCollector/DataCollector.php | 1 + 7 files changed, 272 insertions(+), 244 deletions(-) diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index fc76b62cac91..e74ac05f83ba 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -15,8 +15,6 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\Stopwatch\Stopwatch; -use Symfony\Component\VarDumper\Caster\ClassStub; -use Symfony\Component\VarDumper\Cloner\VarCloner; use Psr\Log\LoggerInterface; /** @@ -34,7 +32,6 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface private $called; private $dispatcher; private $wrappedListeners; - private $cloner; /** * Constructor. @@ -50,9 +47,6 @@ public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $sto $this->logger = $logger; $this->called = array(); $this->wrappedListeners = array(); - if (class_exists(ClassStub::class)) { - $this->cloner = new VarCloner(); - } } /** @@ -159,8 +153,7 @@ public function getCalledListeners() $called = array(); foreach ($this->called as $eventName => $listeners) { foreach ($listeners as $listener) { - $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); - $called[$eventName.'.'.$info['pretty']] = $info; + $called[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName); } } @@ -198,8 +191,10 @@ public function getNotCalledListeners() } if (!$called) { - $info = $this->getListenerInfo($listener, $eventName); - $notCalled[$eventName.'.'.$info['pretty']] = $info; + if (!$listener instanceof WrappedListener) { + $listener = new WrappedListener($listener, null, $this->stopwatch, $this); + } + $notCalled[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName); } } } @@ -245,12 +240,11 @@ protected function postDispatch($eventName, Event $event) private function preProcess($eventName) { foreach ($this->dispatcher->getListeners($eventName) as $listener) { - $info = $this->getListenerInfo($listener, $eventName); - $name = isset($info['class']) ? $info['class'] : $info['type']; - $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this); + $priority = $this->getListenerPriority($eventName, $listener); + $wrappedListener = new WrappedListener($listener, null, $this->stopwatch, $this); $this->wrappedListeners[$eventName][] = $wrappedListener; $this->dispatcher->removeListener($eventName, $listener); - $this->dispatcher->addListener($eventName, $wrappedListener, $info['priority']); + $this->dispatcher->addListener($eventName, $wrappedListener, $priority); } } @@ -267,10 +261,13 @@ private function postProcess($eventName) $this->dispatcher->removeListener($eventName, $listener); $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority); - $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName); + if (null !== $this->logger) { + $context = array('event' => $eventName, 'listener' => $listener->getPretty()); + } + if ($listener->wasCalled()) { if (null !== $this->logger) { - $this->logger->debug('Notified event "{event}" to listener "{listener}".', array('event' => $eventName, 'listener' => $info['pretty'])); + $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context); } if (!isset($this->called[$eventName])) { @@ -281,12 +278,12 @@ private function postProcess($eventName) } if (null !== $this->logger && $skipped) { - $this->logger->debug('Listener "{listener}" was not called for event "{event}".', array('listener' => $info['pretty'], 'event' => $eventName)); + $this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context); } if ($listener->stoppedPropagation()) { if (null !== $this->logger) { - $this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', array('listener' => $info['pretty'], 'event' => $eventName)); + $this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context); } $skipped = true; @@ -294,70 +291,6 @@ private function postProcess($eventName) } } - /** - * Returns information about the listener. - * - * @param object $listener The listener - * @param string $eventName The event name - * - * @return array Information about the listener - */ - private function getListenerInfo($listener, $eventName) - { - $info = array( - 'event' => $eventName, - 'priority' => $this->getListenerPriority($eventName, $listener), - ); - if ($listener instanceof \Closure) { - $info += array( - 'type' => 'Closure', - 'pretty' => 'closure', - ); - } elseif (is_string($listener)) { - try { - $r = new \ReflectionFunction($listener); - $file = $r->getFileName(); - $line = $r->getStartLine(); - } catch (\ReflectionException $e) { - $file = null; - $line = null; - } - $info += array( - 'type' => 'Function', - 'function' => $listener, - 'file' => $file, - 'line' => $line, - 'pretty' => $listener, - ); - } elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) { - if (!is_array($listener)) { - $listener = array($listener, '__invoke'); - } - $class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; - try { - $r = new \ReflectionMethod($class, $listener[1]); - $file = $r->getFileName(); - $line = $r->getStartLine(); - } catch (\ReflectionException $e) { - $file = null; - $line = null; - } - $info += array( - 'type' => 'Method', - 'class' => $class, - 'method' => $listener[1], - 'file' => $file, - 'line' => $line, - 'pretty' => $class.'::'.$listener[1], - ); - } - if (null !== $this->cloner) { - $info['data'] = $this->cloner->cloneVar(array(new ClassStub($info['pretty'].'()', $listener)))->seek(0); - } - - return $info; - } - private function sortListenersByPriority($a, $b) { if (is_int($a['priority']) && !is_int($b['priority'])) { diff --git a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php index e16627d6ad91..45208a19b244 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php +++ b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php @@ -14,6 +14,8 @@ use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Cloner\VarCloner; /** * @author Fabien Potencier @@ -26,6 +28,10 @@ class WrappedListener private $stoppedPropagation; private $stopwatch; private $dispatcher; + private $pretty; + private $data; + + private static $cloner; public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) { @@ -35,6 +41,26 @@ public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatc $this->dispatcher = $dispatcher; $this->called = false; $this->stoppedPropagation = false; + + if (is_array($listener)) { + $this->name = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + $this->pretty = $this->name.'::'.$listener[1]; + } elseif ($listener instanceof \Closure) { + $this->pretty = $this->name = 'closure'; + } elseif (is_string($listener)) { + $this->pretty = $this->name = $listener; + } else { + $this->name = get_class($listener); + $this->pretty = $this->name.'::__invoke'; + } + + if (null !== $name) { + $this->name = $name; + } + + if (null === self::$cloner) { + self::$cloner = class_exists(ClassStub::class) ? new VarCloner() : false; + } } public function getWrappedListener() @@ -52,6 +78,25 @@ public function stoppedPropagation() return $this->stoppedPropagation; } + public function getPretty() + { + return $this->pretty; + } + + public function getInfo($eventName) + { + if (null === $this->data) { + $this->data = false !== self::$cloner ? self::$cloner->cloneVar(array(new ClassStub($this->pretty.'()', $this->listener)))->seek(0) : $this->pretty; + } + + return array( + 'event' => $eventName, + 'priority' => null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null, + 'pretty' => $this->pretty, + 'data' => $this->data, + ); + } + public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) { $this->called = true; diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php index 67543a254c3e..eed3ec466ed0 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -96,15 +96,17 @@ public function testGetCalledListeners() $tdispatcher->addListener('foo', $listener = function () {}); $listeners = $tdispatcher->getNotCalledListeners(); + $this->assertArrayHasKey('data', $listeners['foo.closure']); unset($listeners['foo.closure']['data']); $this->assertEquals(array(), $tdispatcher->getCalledListeners()); - $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $listeners); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 0)), $listeners); $tdispatcher->dispatch('foo'); $listeners = $tdispatcher->getCalledListeners(); + $this->assertArrayHasKey('data', $listeners['foo.closure']); unset($listeners['foo.closure']['data']); - $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => null)), $listeners); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => null)), $listeners); $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); } diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index ec817c4e2c0a..886153bab38c 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -16,6 +16,12 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; +use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Cloner\VarCloner; /** * Data collector for {@link FormInterface} instances. @@ -63,6 +69,17 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf */ private $formsByView; + /** + * @var ValueExporter + */ + private $valueExporter; + + /** + * @var ClonerInterface + */ + private $cloner; + private $clonerCache = array(); + public function __construct(FormDataExtractorInterface $dataExtractor) { $this->dataExtractor = $dataExtractor; @@ -221,6 +238,108 @@ public function getData() return $this->data; } + public function serialize() + { + $cloneVar = array($this, 'cloneVar'); + + foreach ($this->data['forms_by_hash'] as &$form) { + if ($form['type_class'] instanceof Data) { + continue; + } + $form['view_vars'] = array_map($cloneVar, $form['view_vars']); + $form['type_class'] = $cloneVar($form['type_class'], true); + $form['synchronized'] = $cloneVar($form['synchronized']); + $form['passed_options'] = array_map($cloneVar, $form['passed_options']); + $form['resolved_options'] = array_map($cloneVar, $form['resolved_options']); + $form['default_data'] = array_map($cloneVar, $form['default_data']); + $form['submitted_data'] = array_map($cloneVar, $form['submitted_data']); + + if (!empty($form['errors'])) { + foreach ($form['errors'] as $i => $error) { + if (!empty($error['trace'])) { + $form['errors'][$i]['trace'] = array_map($cloneVar, $error['trace']); + } + } + } + } + + return serialize($this->data); + } + + /** + * {@inheritdoc} + */ + protected function cloneVar($var, $isClass = false) + { + if (null === $this->cloner) { + if (class_exists(ClassStub::class)) { + $this->cloner = new VarCloner(); + $this->cloner->addCasters(array( + '*' => function ($v, array $a, Stub $s, $isNested) { + if ($isNested && !$v instanceof \DateTimeInterface) { + $s->cut = -1; + $a = array(); + } + + return $a; + }, + \Exception::class => function (\Exception $e, array $a, Stub $s) { + if (isset($a[$k = "\0Exception\0previous"])) { + unset($a[$k]); + ++$s->cut; + } + + return $a; + }, + FormInterface::class => function (FormInterface $f, array $a) { + return array( + Caster::PREFIX_VIRTUAL.'name' => $f->getName(), + Caster::PREFIX_VIRTUAL.'type_class' => new ClassStub(get_class($f->getConfig()->getType()->getInnerType())), + ); + }, + ConstraintViolationInterface::class => function (ConstraintViolationInterface $v, array $a) { + return array( + Caster::PREFIX_VIRTUAL.'root' => $v->getRoot(), + Caster::PREFIX_VIRTUAL.'path' => $v->getPropertyPath(), + Caster::PREFIX_VIRTUAL.'value' => $v->getInvalidValue(), + ); + }, + )); + } else { + @trigger_error(sprintf('Using the %s() method without the VarDumper component is deprecated since version 3.2 and won\'t be supported in 4.0. Install symfony/var-dumper version 3.2 or above.', __METHOD__), E_USER_DEPRECATED); + $this->cloner = false; + } + } + if (false === $this->cloner) { + if (null === $this->valueExporter) { + $this->valueExporter = new ValueExporter(); + } + + return $this->valueExporter->exportValue($var); + } + if (null === $var) { + $type = $hash = 'null'; + } elseif (array() === $var) { + $type = $hash = 'array'; + } elseif ('object' === $type = gettype($var)) { + $hash = spl_object_hash($var); + } elseif ('double' === $type) { + $hash = (string) $var; + } elseif ('integer' === $type || 'string' === $type) { + $hash = $var; + } else { + $type = null; + } + if (null !== $type && null !== $cache = &$this->clonerCache[$type][$hash]) { + return $cache; + } + if ($isClass) { + return $cache = $this->cloner->cloneVar(array(new ClassStub($var)))->seek(0); + } + + return $cache = $this->cloner->cloneVar($var); + } + private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output, array &$outputByHash) { $hash = spl_object_hash($form); diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php index 06f690bf4c20..a09b87fa1c33 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php @@ -15,12 +15,6 @@ use Symfony\Component\Form\FormView; use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; use Symfony\Component\Validator\ConstraintViolationInterface; -use Symfony\Component\VarDumper\Caster\Caster; -use Symfony\Component\VarDumper\Caster\ClassStub; -use Symfony\Component\VarDumper\Caster\StubCaster; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Cloner\Stub; -use Symfony\Component\VarDumper\Cloner\VarCloner; /** * Default implementation of {@link FormDataExtractorInterface}. @@ -52,18 +46,18 @@ public function extractConfiguration(FormInterface $form) $data = array( 'id' => $this->buildId($form), 'name' => $form->getName(), - 'type_class' => $this->cloneVar(new ClassStub(get_class($form->getConfig()->getType()->getInnerType()))), - 'synchronized' => $this->cloneVar($form->isSynchronized()), + 'type_class' => get_class($form->getConfig()->getType()->getInnerType()), + 'synchronized' => $form->isSynchronized(), 'passed_options' => array(), 'resolved_options' => array(), ); foreach ($form->getConfig()->getAttribute('data_collector/passed_options', array()) as $option => $value) { - $data['passed_options'][$option] = $this->cloneVar($value); + $data['passed_options'][$option] = $value; } foreach ($form->getConfig()->getOptions() as $option => $value) { - $data['resolved_options'][$option] = $this->cloneVar($value); + $data['resolved_options'][$option] = $value; } ksort($data['passed_options']); @@ -79,17 +73,17 @@ public function extractDefaultData(FormInterface $form) { $data = array( 'default_data' => array( - 'norm' => $this->cloneVar($form->getNormData()), + 'norm' => $form->getNormData(), ), 'submitted_data' => array(), ); if ($form->getData() !== $form->getNormData()) { - $data['default_data']['model'] = $this->cloneVar($form->getData()); + $data['default_data']['model'] = $form->getData(); } if ($form->getViewData() !== $form->getNormData()) { - $data['default_data']['view'] = $this->cloneVar($form->getViewData()); + $data['default_data']['view'] = $form->getViewData(); } return $data; @@ -102,17 +96,17 @@ public function extractSubmittedData(FormInterface $form) { $data = array( 'submitted_data' => array( - 'norm' => $this->cloneVar($form->getNormData()), + 'norm' => $form->getNormData(), ), 'errors' => array(), ); if ($form->getViewData() !== $form->getNormData()) { - $data['submitted_data']['view'] = $this->cloneVar($form->getViewData()); + $data['submitted_data']['view'] = $form->getViewData(); } if ($form->getData() !== $form->getNormData()) { - $data['submitted_data']['model'] = $this->cloneVar($form->getData()); + $data['submitted_data']['model'] = $form->getData(); } foreach ($form->getErrors() as $error) { @@ -146,13 +140,10 @@ public function extractSubmittedData(FormInterface $form) break; } - if ($errorData['trace']) { - $errorData['trace'] = $this->cloneVar($errorData['trace']); - } $data['errors'][] = $errorData; } - $data['synchronized'] = $this->cloneVar($form->isSynchronized()); + $data['synchronized'] = $form->isSynchronized(); return $data; } @@ -162,20 +153,14 @@ public function extractSubmittedData(FormInterface $form) */ public function extractViewVariables(FormView $view) { - $data = array(); - - // Set the ID in case no FormInterface object was collected for this - // view - if (!isset($data['id'])) { - $data['id'] = isset($view->vars['id']) ? $view->vars['id'] : null; - } - - if (!isset($data['name'])) { - $data['name'] = isset($view->vars['name']) ? $view->vars['name'] : null; - } + $data = array( + 'id' => isset($view->vars['id']) ? $view->vars['id'] : null, + 'name' => isset($view->vars['name']) ? $view->vars['name'] : null, + 'view_vars' => array(), + ); foreach ($view->vars as $varName => $value) { - $data['view_vars'][$varName] = $this->cloneVar($value); + $data['view_vars'][$varName] = $value; } ksort($data['view_vars']); @@ -200,46 +185,4 @@ private function buildId(FormInterface $form) return $id; } - - /** - * Converts the variable into a serializable Data instance. - * - * @param mixed $var - * - * @return Data - */ - private function cloneVar($var) - { - if (null === $this->cloner) { - $this->cloner = new VarCloner(); - $this->cloner->addCasters(array( - Stub::class => function (Stub $v, array $a, Stub $s, $isNested) { - return $isNested ? $a : StubCaster::castStub($v, $a, $s, true); - }, - \Exception::class => function (\Exception $e, array $a, Stub $s) { - if (isset($a[$k = "\0Exception\0previous"])) { - unset($a[$k]); - ++$s->cut; - } - - return $a; - }, - FormInterface::class => function (FormInterface $f, array $a) { - return array( - Caster::PREFIX_VIRTUAL.'name' => $f->getName(), - Caster::PREFIX_VIRTUAL.'type_class' => new ClassStub(get_class($f->getConfig()->getType()->getInnerType())), - ); - }, - ConstraintViolationInterface::class => function (ConstraintViolationInterface $v, array $a) { - return array( - Caster::PREFIX_VIRTUAL.'root' => $v->getRoot(), - Caster::PREFIX_VIRTUAL.'path' => $v->getPropertyPath(), - Caster::PREFIX_VIRTUAL.'value' => $v->getInvalidValue(), - ); - }, - )); - } - - return $this->cloner->cloneVar($var); - } } diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php index 6e1c43717e39..c45ca112781f 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -19,8 +19,6 @@ use Symfony\Component\Form\FormView; use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; use Symfony\Component\Validator\ConstraintViolation; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** @@ -66,11 +64,11 @@ public function testExtractConfiguration() $this->assertSame(array( 'id' => 'name', 'name' => 'name', - 'type_class' => '"stdClass"', - 'synchronized' => 'true', + 'type_class' => 'stdClass', + 'synchronized' => true, 'passed_options' => array(), 'resolved_options' => array(), - ), $this->inlineData($this->dataExtractor->extractConfiguration($form))); + ), $this->dataExtractor->extractConfiguration($form)); } public function testExtractConfigurationSortsPassedOptions() @@ -96,15 +94,15 @@ public function testExtractConfigurationSortsPassedOptions() $this->assertSame(array( 'id' => 'name', 'name' => 'name', - 'type_class' => '"stdClass"', - 'synchronized' => 'true', + 'type_class' => 'stdClass', + 'synchronized' => true, 'passed_options' => array( - 'a' => '"bar"', - 'b' => '"foo"', - 'c' => '"baz"', + 'a' => 'bar', + 'b' => 'foo', + 'c' => 'baz', ), 'resolved_options' => array(), - ), $this->inlineData($this->dataExtractor->extractConfiguration($form))); + ), $this->dataExtractor->extractConfiguration($form)); } public function testExtractConfigurationSortsResolvedOptions() @@ -127,15 +125,15 @@ public function testExtractConfigurationSortsResolvedOptions() $this->assertSame(array( 'id' => 'name', 'name' => 'name', - 'type_class' => '"stdClass"', - 'synchronized' => 'true', + 'type_class' => 'stdClass', + 'synchronized' => true, 'passed_options' => array(), 'resolved_options' => array( - 'a' => '"bar"', - 'b' => '"foo"', - 'c' => '"baz"', + 'a' => 'bar', + 'b' => 'foo', + 'c' => 'baz', ), - ), $this->inlineData($this->dataExtractor->extractConfiguration($form))); + ), $this->dataExtractor->extractConfiguration($form)); } public function testExtractConfigurationBuildsIdRecursively() @@ -163,11 +161,11 @@ public function testExtractConfigurationBuildsIdRecursively() $this->assertSame(array( 'id' => 'grandParent_parent_name', 'name' => 'name', - 'type_class' => '"stdClass"', - 'synchronized' => 'true', + 'type_class' => 'stdClass', + 'synchronized' => true, 'passed_options' => array(), 'resolved_options' => array(), - ), $this->inlineData($this->dataExtractor->extractConfiguration($form))); + ), $this->dataExtractor->extractConfiguration($form)); } public function testExtractDefaultData() @@ -178,10 +176,10 @@ public function testExtractDefaultData() $this->assertSame(array( 'default_data' => array( - 'norm' => '"Foobar"', + 'norm' => 'Foobar', ), 'submitted_data' => array(), - ), $this->inlineData($this->dataExtractor->extractDefaultData($form))); + ), $this->dataExtractor->extractDefaultData($form)); } public function testExtractDefaultDataStoresModelDataIfDifferent() @@ -196,11 +194,11 @@ public function testExtractDefaultDataStoresModelDataIfDifferent() $this->assertSame(array( 'default_data' => array( - 'norm' => '"Bar"', - 'model' => '"Foo"', + 'norm' => 'Bar', + 'model' => 'Foo', ), 'submitted_data' => array(), - ), $this->inlineData($this->dataExtractor->extractDefaultData($form))); + ), $this->dataExtractor->extractDefaultData($form)); } public function testExtractDefaultDataStoresViewDataIfDifferent() @@ -215,11 +213,11 @@ public function testExtractDefaultDataStoresViewDataIfDifferent() $this->assertSame(array( 'default_data' => array( - 'norm' => '"Foo"', - 'view' => '"Bar"', + 'norm' => 'Foo', + 'view' => 'Bar', ), 'submitted_data' => array(), - ), $this->inlineData($this->dataExtractor->extractDefaultData($form))); + ), $this->dataExtractor->extractDefaultData($form)); } public function testExtractSubmittedData() @@ -230,11 +228,11 @@ public function testExtractSubmittedData() $this->assertSame(array( 'submitted_data' => array( - 'norm' => '"Foobar"', + 'norm' => 'Foobar', ), 'errors' => array(), - 'synchronized' => 'true', - ), $this->inlineData($this->dataExtractor->extractSubmittedData($form))); + 'synchronized' => true, + ), $this->dataExtractor->extractSubmittedData($form)); } public function testExtractSubmittedDataStoresModelDataIfDifferent() @@ -250,12 +248,12 @@ public function testExtractSubmittedDataStoresModelDataIfDifferent() $this->assertSame(array( 'submitted_data' => array( - 'norm' => '"Bar"', - 'model' => '"Foo"', + 'norm' => 'Bar', + 'model' => 'Foo', ), 'errors' => array(), - 'synchronized' => 'true', - ), $this->inlineData($this->dataExtractor->extractSubmittedData($form))); + 'synchronized' => true, + ), $this->dataExtractor->extractSubmittedData($form)); } public function testExtractSubmittedDataStoresViewDataIfDifferent() @@ -271,12 +269,12 @@ public function testExtractSubmittedDataStoresViewDataIfDifferent() $this->assertSame(array( 'submitted_data' => array( - 'norm' => '"Foo"', - 'view' => '"Bar"', + 'norm' => 'Foo', + 'view' => 'Bar', ), 'errors' => array(), - 'synchronized' => 'true', - ), $this->inlineData($this->dataExtractor->extractSubmittedData($form))); + 'synchronized' => true, + ), $this->dataExtractor->extractSubmittedData($form)); } public function testExtractSubmittedDataStoresErrors() @@ -288,13 +286,13 @@ public function testExtractSubmittedDataStoresErrors() $this->assertSame(array( 'submitted_data' => array( - 'norm' => '"Foobar"', + 'norm' => 'Foobar', ), 'errors' => array( array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array()), ), - 'synchronized' => 'true', - ), $this->inlineData($this->dataExtractor->extractSubmittedData($form))); + 'synchronized' => true, + ), $this->dataExtractor->extractSubmittedData($form)); } public function testExtractSubmittedDataStoresErrorOrigin() @@ -309,13 +307,13 @@ public function testExtractSubmittedDataStoresErrorOrigin() $this->assertSame(array( 'submitted_data' => array( - 'norm' => '"Foobar"', + 'norm' => 'Foobar', ), 'errors' => array( array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array()), ), - 'synchronized' => 'true', - ), $this->inlineData($this->dataExtractor->extractSubmittedData($form))); + 'synchronized' => true, + ), $this->dataExtractor->extractSubmittedData($form)); } public function testExtractSubmittedDataStoresErrorCause() @@ -332,29 +330,34 @@ public function testExtractSubmittedDataStoresErrorCause() $this->assertDumpMatchesFormat(<< array:1 [ - "norm" => ""Foobar"" + "norm" => "Foobar" ] "errors" => array:1 [ 0 => array:3 [ "message" => "Invalid!" "origin" => "$origin" - "trace" => """ - array:2 [\\n - 0 => Symfony\Component\Validator\ConstraintViolation {\\n - root: "Root"\\n - path: "property.path"\\n - value: "Invalid!"\\n - }\\n - 1 => Exception {%A}\\n - ] - """ + "trace" => array:2 [ + 0 => Symfony\Component\Validator\ConstraintViolation { + -message: "Foo" + -messageTemplate: "Foo" + -parameters: [] + -plural: null + -root: "Root" + -propertyPath: "property.path" + -invalidValue: "Invalid!" + -constraint: null + -code: null + -cause: Exception {%A} + } + 1 => Exception {#1} + ] ] ] - "synchronized" => "true" + "synchronized" => true ] EODUMP , - $this->inlineData($this->dataExtractor->extractSubmittedData($form)) + $this->dataExtractor->extractSubmittedData($form) ); } @@ -373,12 +376,12 @@ function () { $this->assertSame(array( 'submitted_data' => array( - 'norm' => '"Foobar"', - 'model' => 'null', + 'norm' => 'Foobar', + 'model' => null, ), 'errors' => array(), - 'synchronized' => 'false', - ), $this->inlineData($this->dataExtractor->extractSubmittedData($form))); + 'synchronized' => false, + ), $this->dataExtractor->extractSubmittedData($form)); } public function testExtractViewVariables() @@ -397,31 +400,13 @@ public function testExtractViewVariables() 'id' => 'foo_bar', 'name' => 'bar', 'view_vars' => array( - 'a' => '"bar"', - 'b' => '"foo"', - 'c' => '"baz"', - 'id' => '"foo_bar"', - 'name' => '"bar"', + 'a' => 'bar', + 'b' => 'foo', + 'c' => 'baz', + 'id' => 'foo_bar', + 'name' => 'bar', ), - ), $this->inlineData($this->dataExtractor->extractViewVariables($view))); - } - - private function inlineData(array $extraction) - { - $dumper = new CliDumper(); - $inlined = array(); - - foreach ($extraction as $k => $v) { - if (is_array($v)) { - $inlined[$k] = $this->inlineData($v); - } elseif ($v instanceof Data) { - $inlined[$k] = rtrim($dumper->dump($v->withRefHandles(false), true)); - } else { - $inlined[$k] = $v; - } - } - - return $inlined; + ), $this->dataExtractor->extractViewVariables($view)); } /** diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 83f87d273038..904baeedbe2f 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -67,6 +67,7 @@ protected function cloneVar($var) if (null === $this->cloner) { if (class_exists(ClassStub::class)) { $this->cloner = new VarCloner(); + $this->cloner->setMaxItems(250); $this->cloner->addCasters(array( Stub::class => function (Stub $v, array $a, Stub $s, $isNested) { return $isNested ? $a : StubCaster::castStub($v, $a, $s, true);