feature #24289 [FrameworkBundle][HttpKernel] Reset profiler (derrabus)

This PR was merged into the 3.4 branch.

Discussion
----------

[FrameworkBundle][HttpKernel] Reset profiler

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #18244
| License       | MIT
| Doc PR        | N/A

This PR adds the ability to reset the profiler between requests. Furthermore, the profiler service has been tagged with the new `kernel.reset` tag from #24155. For this, I had to readd the ability to define multiple reset methods for a service.

Note: This PR requires twigphp/Twig#2560.

Commits
-------

8c39bf7845 Reset profiler.
This commit is contained in:
Fabien Potencier 2017-10-05 05:04:23 -07:00
commit 86684f1c07
41 changed files with 426 additions and 37 deletions

View File

@ -63,6 +63,12 @@ Debug
* Support for stacked errors in the `ErrorHandler` is deprecated and will be removed in Symfony 4.0.
EventDispatcher
---------------
* Implementing `TraceableEventDispatcherInterface` without the `reset()` method
is deprecated and will be unsupported in 4.0.
Filesystem
----------
@ -270,6 +276,10 @@ HttpKernel
* The `Symfony\Component\HttpKernel\Config\EnvParametersResource` class has been deprecated and will be removed in 4.0.
* Implementing `DataCollectorInterface` without a `reset()` method has been deprecated and will be unsupported in 4.0.
* Implementing `DebugLoggerInterface` without a `clear()` method has been deprecated and will be unsupported in 4.0.
* The `ChainCacheClearer::add()` method has been deprecated and will be removed in 4.0,
inject the list of clearers as a constructor argument instead.
@ -320,11 +330,11 @@ SecurityBundle
* Deprecated the HTTP digest authentication: `HttpDigestFactory` will be removed in 4.0.
Use another authentication system like `http_basic` instead.
* Deprecated setting the `switch_user.stateless` option to false when the firewall is `stateless`.
Setting it to false will have no effect in 4.0.
* Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider.
* Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider.
Using the first configured provider is deprecated since 3.4 and will throw an exception on 4.0.
Explicitly configure the provider to use on your firewalls.

View File

@ -192,6 +192,8 @@ EventDispatcher
* The `ContainerAwareEventDispatcher` class has been removed.
Use `EventDispatcher` with closure factories instead.
* The `reset()` method has been added to `TraceableEventDispatcherInterface`.
ExpressionLanguage
------------------
@ -611,6 +613,10 @@ HttpKernel
* The `Symfony\Component\HttpKernel\Config\EnvParametersResource` class has been removed.
* The `reset()` method has been added to `Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface`.
* The `clear()` method has been added to `Symfony\Component\HttpKernel\Log\DebugLoggerInterface`.
* The `ChainCacheClearer::add()` method has been removed,
inject the list of clearers as a constructor argument instead.
@ -693,10 +699,10 @@ SecurityBundle
* Removed the HTTP digest authentication system. The `HttpDigestFactory` class
has been removed. Use another authentication system like `http_basic` instead.
* The `switch_user.stateless` option is now always true if the firewall is stateless.
* Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider.
* Not configuring explicitly the provider on a firewall is ambiguous when there is more than one registered provider.
The first configured provider is not used anymore and an exception is thrown instead.
Explicitly configure the provider to use on your firewalls.

View File

@ -20,7 +20,7 @@
"ext-xml": "*",
"doctrine/common": "~2.4",
"fig/link-util": "^1.0",
"twig/twig": "~1.34|~2.4",
"twig/twig": "^1.35|^2.4.4",
"psr/cache": "~1.0",
"psr/container": "^1.0",
"psr/link": "^1.0",

View File

@ -28,6 +28,10 @@ class DoctrineDataCollector extends DataCollector
private $registry;
private $connections;
private $managers;
/**
* @var DebugStack[]
*/
private $loggers = array();
public function __construct(ManagerRegistry $registry)
@ -65,6 +69,16 @@ class DoctrineDataCollector extends DataCollector
);
}
public function reset()
{
$this->data = array();
foreach ($this->loggers as $logger) {
$logger->queries = array();
$logger->currentQuery = 0;
}
}
public function getManagers()
{
return $this->data['managers'];

View File

@ -101,6 +101,20 @@ class DoctrineDataCollectorTest extends TestCase
$this->assertTrue($collectedQueries['default'][1]['explainable']);
}
public function testReset()
{
$queries = array(
array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1),
);
$c = $this->createCollector($queries);
$c->collect(new Request(), new Response());
$c->reset();
$c->collect(new Request(), new Response());
$this->assertEquals(array('default' => array()), $c->getQueries());
}
/**
* @dataProvider paramProvider
*/

View File

@ -45,6 +45,16 @@ class Logger extends BaseLogger implements DebugLoggerInterface
return 0;
}
/**
* {@inheritdoc}
*/
public function clear()
{
if (($logger = $this->getDebugLogger()) && method_exists($logger, 'clear')) {
$logger->clear();
}
}
/**
* Returns a DebugLoggerInterface instance if one is registered with this logger.
*

View File

@ -55,4 +55,13 @@ class DebugProcessor implements DebugLoggerInterface
{
return $this->errorCount;
}
/**
* {@inheritdoc}
*/
public function clear()
{
$this->records = array();
$this->errorCount = 0;
}
}

View File

@ -128,4 +128,17 @@ class LoggerTest extends TestCase
$this->assertEquals('test', $record['message']);
$this->assertEquals(Logger::INFO, $record['priority']);
}
public function testClear()
{
$handler = new TestHandler();
$logger = new Logger('test', array($handler));
$logger->pushProcessor(new DebugProcessor());
$logger->addInfo('test');
$logger->clear();
$this->assertEmpty($logger->getLogs());
$this->assertSame(0, $logger->countErrors());
}
}

View File

@ -44,6 +44,16 @@ class TwigDataCollector extends DataCollector implements LateDataCollectorInterf
{
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->profile->reset();
$this->computed = null;
$this->data = array();
}
/**
* {@inheritdoc}
*/

View File

@ -17,7 +17,7 @@
],
"require": {
"php": "^5.5.9|>=7.0.8",
"twig/twig": "~1.34|~2.4"
"twig/twig": "^1.35|^2.4.4"
},
"require-dev": {
"fig/link-util": "^1.0",

View File

@ -609,9 +609,9 @@ class FrameworkExtension extends Extension
}
}
if (!$config['collect']) {
$container->getDefinition('profiler')->addMethodCall('disable', array());
}
$container->getDefinition('profiler')
->addArgument($config['collect'])
->addTag('kernel.reset', array('method' => 'reset'));
}
/**

View File

@ -203,6 +203,14 @@ class SecurityDataCollector extends DataCollector implements LateDataCollectorIn
}
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = array();
}
public function lateCollect()
{
$this->data = $this->cloneVar($this->data);

View File

@ -53,6 +53,15 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter
$this->data['total']['statistics'] = $this->calculateTotalStatistics();
}
public function reset()
{
$this->data = array();
foreach ($this->instances as $instance) {
// Calling getCalls() will clear the calls.
$instance->getCalls();
}
}
public function lateCollect()
{
$this->data = $this->cloneVar($this->data);

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
3.4.0
-----
* Implementing `TraceableEventDispatcherInterface` without the `reset()` method has been deprecated.
3.3.0
-----

View File

@ -212,6 +212,11 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
return $notCalled;
}
public function reset()
{
$this->called = array();
}
/**
* Proxies all method calls to the original event dispatcher.
*

View File

@ -15,6 +15,8 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @method reset() Resets the trace.
*/
interface TraceableEventDispatcherInterface extends EventDispatcherInterface
{

View File

@ -124,6 +124,21 @@ class TraceableEventDispatcherTest extends TestCase
$this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
}
public function testClearCalledListeners()
{
$tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
$tdispatcher->addListener('foo', function () {}, 5);
$tdispatcher->dispatch('foo');
$tdispatcher->reset();
$listeners = $tdispatcher->getNotCalledListeners();
$this->assertArrayHasKey('stub', $listeners['foo.closure']);
unset($listeners['foo.closure']['stub']);
$this->assertEquals(array(), $tdispatcher->getCalledListeners());
$this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners);
}
public function testGetCalledListenersNested()
{
$tdispatcher = null;

View File

@ -72,12 +72,9 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
public function __construct(FormDataExtractorInterface $dataExtractor)
{
$this->dataExtractor = $dataExtractor;
$this->data = array(
'forms' => array(),
'forms_by_hash' => array(),
'nb_errors' => 0,
);
$this->hasVarDumper = class_exists(ClassStub::class);
$this->reset();
}
/**
@ -87,6 +84,15 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
{
}
public function reset()
{
$this->data = array(
'forms' => array(),
'forms_by_hash' => array(),
'nb_errors' => 0,
);
}
/**
* {@inheritdoc}
*/

View File

@ -695,6 +695,36 @@ class FormDataCollectorTest extends TestCase
$this->assertFalse(isset($child21Data['has_children_error']), 'The leaf data does not contains "has_children_error" property.');
}
public function testReset()
{
$form = $this->createForm('my_form');
$this->dataExtractor->expects($this->any())
->method('extractConfiguration')
->will($this->returnValue(array()));
$this->dataExtractor->expects($this->any())
->method('extractDefaultData')
->will($this->returnValue(array()));
$this->dataExtractor->expects($this->any())
->method('extractSubmittedData')
->with($form)
->will($this->returnValue(array('errors' => array('baz'))));
$this->dataCollector->buildPreliminaryFormTree($form);
$this->dataCollector->collectSubmittedData($form);
$this->dataCollector->reset();
$this->assertSame(
array(
'forms' => array(),
'forms_by_hash' => array(),
'nb_errors' => 0,
),
$this->dataCollector->getData()
);
}
private function createForm($name)
{
$builder = new FormBuilder($name, null, $this->dispatcher, $this->factory);

View File

@ -14,7 +14,9 @@ CHANGELOG
* deprecated the `ChainCacheClearer::add()` method
* deprecated the `CacheaWarmerAggregate::add()` and `setWarmers()` methods
* made `CacheWarmerAggregate` and `ChainCacheClearer` classes final
* added the possibility to reset the profiler to its initial state
* deprecated data collectors without a `reset()` method
* deprecated implementing `DebugLoggerInterface` without a `clear()` method
3.3.0
-----

View File

@ -26,6 +26,11 @@ class AjaxDataCollector extends DataCollector
// all collecting is done client side
}
public function reset()
{
// all collecting is done client side
}
public function getName()
{
return 'ajax';

View File

@ -95,6 +95,14 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte
}
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = array();
}
public function lateCollect()
{
$this->data = $this->cloneVar($this->data);

View File

@ -18,6 +18,8 @@ use Symfony\Component\HttpFoundation\Response;
* DataCollectorInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @method reset() Resets this data collector to its initial state.
*/
interface DataCollectorInterface
{

View File

@ -27,6 +27,9 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter
public function __construct(EventDispatcherInterface $dispatcher = null)
{
if ($dispatcher instanceof TraceableEventDispatcherInterface && !method_exists($dispatcher, 'reset')) {
@trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since version 3.4 and will be unsupported in 4.0 for class "%s".', TraceableEventDispatcherInterface::class, \get_class($dispatcher)), E_USER_DEPRECATED);
}
$this->dispatcher = $dispatcher;
}
@ -41,6 +44,19 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter
);
}
public function reset()
{
$this->data = array();
if ($this->dispatcher instanceof TraceableEventDispatcherInterface) {
if (!method_exists($this->dispatcher, 'reset')) {
return; // @deprecated
}
$this->dispatcher->reset();
}
}
public function lateCollect()
{
if ($this->dispatcher instanceof TraceableEventDispatcherInterface) {

View File

@ -34,6 +34,14 @@ class ExceptionDataCollector extends DataCollector
}
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = array();
}
/**
* Checks if the exception is not null.
*

View File

@ -29,6 +29,10 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
public function __construct($logger = null, $containerPathPrefix = null)
{
if (null !== $logger && $logger instanceof DebugLoggerInterface) {
if (!method_exists($logger, 'clear')) {
@trigger_error(sprintf('Implementing "%s" without the "clear()" method is deprecated since version 3.4 and will be unsupported in 4.0 for class "%s".', DebugLoggerInterface::class, \get_class($logger)), E_USER_DEPRECATED);
}
$this->logger = $logger;
}
@ -43,6 +47,17 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
// everything is done as late as possible
}
/**
* {@inheritdoc}
*/
public function reset()
{
if ($this->logger && method_exists($this->logger, 'clear')) {
$this->logger->clear();
}
$this->data = array();
}
/**
* {@inheritdoc}
*/

View File

@ -23,10 +23,7 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte
{
public function __construct()
{
$this->data = array(
'memory' => 0,
'memory_limit' => $this->convertToBytes(ini_get('memory_limit')),
);
$this->reset();
}
/**
@ -37,6 +34,17 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte
$this->updateMemoryUsage();
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = array(
'memory' => 0,
'memory_limit' => $this->convertToBytes(ini_get('memory_limit')),
);
}
/**
* {@inheritdoc}
*/

View File

@ -156,6 +156,12 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
$this->data = $this->cloneVar($this->data);
}
public function reset()
{
$this->data = array();
$this->controllers = new \SplObjectStorage();
}
public function getMethod()
{
return $this->data['method'];

View File

@ -23,17 +23,14 @@ use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
*/
class RouterDataCollector extends DataCollector
{
/**
* @var \SplObjectStorage
*/
protected $controllers;
public function __construct()
{
$this->controllers = new \SplObjectStorage();
$this->data = array(
'redirect' => false,
'url' => null,
'route' => null,
);
$this->reset();
}
/**
@ -53,6 +50,17 @@ class RouterDataCollector extends DataCollector
unset($this->controllers[$request]);
}
public function reset()
{
$this->controllers = new \SplObjectStorage();
$this->data = array(
'redirect' => false,
'url' => null,
'route' => null,
);
}
protected function guessRoute(Request $request, $controller)
{
return 'n/a';

View File

@ -49,6 +49,14 @@ class TimeDataCollector extends DataCollector implements LateDataCollectorInterf
);
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = array();
}
/**
* {@inheritdoc}
*/

View File

@ -15,6 +15,8 @@ namespace Symfony\Component\HttpKernel\Log;
* DebugLoggerInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @method clear() Removes all log records.
*/
interface DebugLoggerInterface
{

View File

@ -40,6 +40,11 @@ class Profiler
*/
private $logger;
/**
* @var bool
*/
private $initiallyEnabled = true;
/**
* @var bool
*/
@ -48,11 +53,13 @@ class Profiler
/**
* @param ProfilerStorageInterface $storage A ProfilerStorageInterface instance
* @param LoggerInterface $logger A LoggerInterface instance
* @param bool $enable The initial enabled state
*/
public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null)
public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null, $enable = true)
{
$this->storage = $storage;
$this->logger = $logger;
$this->initiallyEnabled = $this->enabled = (bool) $enable;
}
/**
@ -188,6 +195,18 @@ class Profiler
return $profile;
}
public function reset()
{
foreach ($this->collectors as $collector) {
if (!method_exists($collector, 'reset')) {
continue;
}
$collector->reset();
}
$this->enabled = $this->initiallyEnabled;
}
/**
* Gets the Collectors associated with this profiler.
*
@ -218,6 +237,10 @@ class Profiler
*/
public function add(DataCollectorInterface $collector)
{
if (!method_exists($collector, 'reset')) {
@trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since version 3.4 and will be unsupported in 4.0 for class "%s".', DataCollectorInterface::class, \get_class($collector)), E_USER_DEPRECATED);
}
$this->collectors[$collector->getName()] = $collector;
}

View File

@ -37,4 +37,23 @@ class ExceptionDataCollectorTest extends TestCase
$this->assertSame('exception', $c->getName());
$this->assertSame($trace, $c->getTrace());
}
public function testCollectWithoutException()
{
$c = new ExceptionDataCollector();
$c->collect(new Request(), new Response());
$this->assertFalse($c->hasException());
}
public function testReset()
{
$c = new ExceptionDataCollector();
$c->collect(new Request(), new Response(), new \Exception());
$c->reset();
$c->collect(new Request(), new Response());
$this->assertFalse($c->hasException());
}
}

View File

@ -19,7 +19,10 @@ class LoggerDataCollectorTest extends TestCase
{
public function testCollectWithUnexpectedFormat()
{
$logger = $this->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface')->getMock();
$logger = $this
->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface')
->setMethods(array('countErrors', 'getLogs', 'clear'))
->getMock();
$logger->expects($this->once())->method('countErrors')->will($this->returnValue('foo'));
$logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue(array()));
@ -43,7 +46,10 @@ class LoggerDataCollectorTest extends TestCase
*/
public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount, $expectedScreamCount, $expectedPriorities = null)
{
$logger = $this->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface')->getMock();
$logger = $this
->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface')
->setMethods(array('countErrors', 'getLogs', 'clear'))
->getMock();
$logger->expects($this->once())->method('countErrors')->will($this->returnValue($nb));
$logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue($logs));
@ -70,6 +76,18 @@ class LoggerDataCollectorTest extends TestCase
}
}
public function testReset()
{
$logger = $this
->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface')
->setMethods(array('countErrors', 'getLogs', 'clear'))
->getMock();
$logger->expects($this->once())->method('clear');
$c = new LoggerDataCollector($logger);
$c->reset();
}
public function getCollectTestData()
{
yield 'simple log' => array(

View File

@ -29,6 +29,11 @@ class CloneVarDataCollector extends DataCollector
$this->data = $this->cloneVar($this->varToClone);
}
public function reset()
{
$this->data = array();
}
public function getData()
{
return $this->data;

View File

@ -25,4 +25,8 @@ class TestEventDispatcher extends EventDispatcher implements TraceableEventDispa
{
return array('bar');
}
public function reset()
{
}
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\HttpKernel\Tests\Profiler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage;
use Symfony\Component\HttpKernel\Profiler\Profiler;
@ -40,6 +41,19 @@ class ProfilerTest extends TestCase
$this->assertSame('bar', $profile->getCollector('request')->getRequestQuery()->all()['foo']->getValue());
}
public function testReset()
{
$collector = $this->getMockBuilder(DataCollectorInterface::class)
->setMethods(['collect', 'getName', 'reset'])
->getMock();
$collector->expects($this->any())->method('getName')->willReturn('mock');
$collector->expects($this->once())->method('reset');
$profiler = new Profiler($this->storage);
$profiler->add($collector);
$profiler->reset();
}
public function testFindWorksWithDates()
{
$profiler = new Profiler($this->storage);

View File

@ -58,6 +58,14 @@ class TranslationDataCollector extends DataCollector implements LateDataCollecto
{
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = array();
}
/**
* @return array
*/

View File

@ -19,6 +19,7 @@ use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Symfony\Component\Validator\Validator\TraceableValidator;
use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Caster\ClassStub;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Stub;
/**
@ -31,10 +32,7 @@ class ValidatorDataCollector extends DataCollector implements LateDataCollectorI
public function __construct(TraceableValidator $validator)
{
$this->validator = $validator;
$this->data = array(
'calls' => array(),
'violations_count' => 0,
);
$this->reset();
}
/**
@ -45,6 +43,15 @@ class ValidatorDataCollector extends DataCollector implements LateDataCollectorI
// Everything is collected once, on kernel terminate.
}
public function reset()
{
$this->validator->reset();
$this->data = array(
'calls' => $this->cloneVar(array()),
'violations_count' => 0,
);
}
/**
* {@inheritdoc}
*/
@ -52,16 +59,22 @@ class ValidatorDataCollector extends DataCollector implements LateDataCollectorI
{
$collected = $this->validator->getCollectedData();
$this->data['calls'] = $this->cloneVar($collected);
$this->data['violations_count'] += array_reduce($collected, function ($previous, $item) {
return $previous += count($item['violations']);
$this->data['violations_count'] = array_reduce($collected, function ($previous, $item) {
return $previous + count($item['violations']);
}, 0);
}
/**
* @return Data
*/
public function getCalls()
{
return $this->data['calls'];
}
/**
* @return int
*/
public function getViolationsCount()
{
return $this->data['violations_count'];

View File

@ -50,6 +50,33 @@ class ValidatorDataCollectorTest extends TestCase
$this->assertCount(2, $call['violations']);
}
public function testReset()
{
$originalValidator = $this->createMock(ValidatorInterface::class);
$validator = new TraceableValidator($originalValidator);
$collector = new ValidatorDataCollector($validator);
$violations = new ConstraintViolationList(array(
$this->createMock(ConstraintViolation::class),
$this->createMock(ConstraintViolation::class),
));
$originalValidator->method('validate')->willReturn($violations);
$validator->validate(new \stdClass());
$collector->lateCollect();
$collector->reset();
$this->assertCount(0, $collector->getCalls());
$this->assertSame(0, $collector->getViolationsCount());
$collector->lateCollect();
$this->assertCount(0, $collector->getCalls());
$this->assertSame(0, $collector->getViolationsCount());
}
protected function createMock($classname)
{
return $this->getMockBuilder($classname)->disableOriginalConstructor()->getMock();

View File

@ -11,7 +11,6 @@
namespace Symfony\Component\Validator\Validator;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
@ -30,13 +29,18 @@ class TraceableValidator implements ValidatorInterface
}
/**
* @return ConstraintViolationList[]
* @return array
*/
public function getCollectedData()
{
return $this->collectedData;
}
public function reset()
{
$this->collectedData = array();
}
/**
* {@inheritdoc}
*/