[Profiler][Validator] Add a validator panel in profiler
This commit is contained in:
parent
b223241f64
commit
ac5e884f36
@ -81,6 +81,7 @@ class FrameworkExtension extends Extension
|
|||||||
private $translationConfigEnabled = false;
|
private $translationConfigEnabled = false;
|
||||||
private $sessionConfigEnabled = false;
|
private $sessionConfigEnabled = false;
|
||||||
private $annotationsConfigEnabled = false;
|
private $annotationsConfigEnabled = false;
|
||||||
|
private $validatorConfigEnabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|null
|
* @var string|null
|
||||||
@ -456,6 +457,10 @@ class FrameworkExtension extends Extension
|
|||||||
$loader->load('form_debug.xml');
|
$loader->load('form_debug.xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->validatorConfigEnabled) {
|
||||||
|
$loader->load('validator_debug.xml');
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->translationConfigEnabled) {
|
if ($this->translationConfigEnabled) {
|
||||||
$loader->load('translation_debug.xml');
|
$loader->load('translation_debug.xml');
|
||||||
$container->getDefinition('translator.data_collector')->setDecoratedService('translator');
|
$container->getDefinition('translator.data_collector')->setDecoratedService('translator');
|
||||||
@ -1107,7 +1112,7 @@ class FrameworkExtension extends Extension
|
|||||||
*/
|
*/
|
||||||
private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
||||||
{
|
{
|
||||||
if (!$this->isConfigEnabled($container, $config)) {
|
if (!$this->validatorConfigEnabled = $this->isConfigEnabled($container, $config)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<defaults public="false" />
|
||||||
|
|
||||||
|
<service id="debug.validator" decorates="validator" decoration-priority="255" class="Symfony\Component\Validator\Validator\TraceableValidator">
|
||||||
|
<argument type="service" id="debug.validator.inner" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- DataCollector -->
|
||||||
|
<service id="data_collector.validator" class="Symfony\Component\Validator\DataCollector\ValidatorDataCollector">
|
||||||
|
<argument type="service" id="debug.validator"/>
|
||||||
|
<tag name="data_collector" template="@WebProfiler/Collector/validator.html.twig" id="validator" priority="320" />
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
</container>
|
@ -49,7 +49,7 @@
|
|||||||
"symfony/stopwatch": "~2.8|~3.0|~4.0",
|
"symfony/stopwatch": "~2.8|~3.0|~4.0",
|
||||||
"symfony/translation": "~3.2|~4.0",
|
"symfony/translation": "~3.2|~4.0",
|
||||||
"symfony/templating": "~2.8|~3.0|~4.0",
|
"symfony/templating": "~2.8|~3.0|~4.0",
|
||||||
"symfony/validator": "~3.3|~4.0",
|
"symfony/validator": "~3.4|~4.0",
|
||||||
"symfony/var-dumper": "~3.3|~4.0",
|
"symfony/var-dumper": "~3.3|~4.0",
|
||||||
"symfony/workflow": "~3.3|~4.0",
|
"symfony/workflow": "~3.3|~4.0",
|
||||||
"symfony/yaml": "~3.2|~4.0",
|
"symfony/yaml": "~3.2|~4.0",
|
||||||
@ -69,7 +69,7 @@
|
|||||||
"symfony/property-info": "<3.3",
|
"symfony/property-info": "<3.3",
|
||||||
"symfony/serializer": "<3.3",
|
"symfony/serializer": "<3.3",
|
||||||
"symfony/translation": "<3.2",
|
"symfony/translation": "<3.2",
|
||||||
"symfony/validator": "<3.3",
|
"symfony/validator": "<3.4",
|
||||||
"symfony/workflow": "<3.3"
|
"symfony/workflow": "<3.3"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% block toolbar %}
|
||||||
|
{% if collector.violationsCount > 0 or collector.calls|length %}
|
||||||
|
{% set status_color = collector.violationsCount ? 'red' : '' %}
|
||||||
|
{% set icon %}
|
||||||
|
{{ include('@WebProfiler/Icon/validator.svg') }}
|
||||||
|
<span class="sf-toolbar-value">
|
||||||
|
{{ collector.violationsCount }}
|
||||||
|
</span>
|
||||||
|
{% endset %}
|
||||||
|
|
||||||
|
{% set text %}
|
||||||
|
<div class="sf-toolbar-info-piece">
|
||||||
|
<b>Validator calls</b>
|
||||||
|
<span class="sf-toolbar-status">{{ collector.calls|length }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="sf-toolbar-info-piece">
|
||||||
|
<b>Number of violations</b>
|
||||||
|
<span class="sf-toolbar-status {{- collector.violationsCount > 0 ? ' sf-toolbar-status-red' }}">{{ collector.violationsCount }}</span>
|
||||||
|
</div>
|
||||||
|
{% endset %}
|
||||||
|
|
||||||
|
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block menu %}
|
||||||
|
<span class="label {{- collector.violationsCount ? ' label-status-error' }} {{ collector.calls is empty ? 'disabled' }}">
|
||||||
|
<span class="icon">{{ include('@WebProfiler/Icon/validator.svg') }}</span>
|
||||||
|
<strong>Validator</strong>
|
||||||
|
{% if collector.violationsCount > 0 %}
|
||||||
|
<span class="count">
|
||||||
|
<span>{{ collector.violationsCount }}</span>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block panel %}
|
||||||
|
<h2>Validator calls</h2>
|
||||||
|
|
||||||
|
{% for call in collector.calls %}
|
||||||
|
<div class="sf-validator sf-reset">
|
||||||
|
<span class="metadata">In
|
||||||
|
{% set caller = call.caller %}
|
||||||
|
{% if caller.line %}
|
||||||
|
{% set link = caller.file|file_link(caller.line) %}
|
||||||
|
{% if link %}
|
||||||
|
<a href="{{ link }}" title="{{ caller.file }}">{{ caller.name }}</a>
|
||||||
|
{% else %}
|
||||||
|
<abbr title="{{ caller.file }}">{{ caller.name }}</abbr>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ caller.name }}
|
||||||
|
{% endif %}
|
||||||
|
line <a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}">{{ caller.line }}</a> (<a class="text-small sf-toggle" data-toggle-selector="#sf-context-{{ loop.index0 }}">context</a>):
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="sf-validator-compact hidden" id="sf-trace-{{ loop.index0 }}">
|
||||||
|
<div class="trace">
|
||||||
|
{{ caller.file|file_excerpt(caller.line) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sf-validator-compact hidden sf-validator-context" id="sf-context-{{ loop.index0 }}">
|
||||||
|
{{ profiler_dump(call.context, maxDepth=1) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if call.violations|length %}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Path</th>
|
||||||
|
<th>Message</th>
|
||||||
|
<th>Invalid value</th>
|
||||||
|
<th>Violation</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for violation in call.violations %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ violation.propertyPath }}</td>
|
||||||
|
<td>{{ violation.message }}</td>
|
||||||
|
<td>{{ profiler_dump(violation.seek('invalidValue')) }}</td>
|
||||||
|
<td>{{ profiler_dump(violation) }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
No violations
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="empty">
|
||||||
|
<p>No calls to the validator were collected during this request.</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"><path fill="#aaaaaa" d="M19.54,22.5H4.29a2.88,2.88,0,0,1-2.87-2.87V4.37A2.88,2.88,0,0,1,4.29,1.5H18.54a1,1,0,0,1,0,2H4.29a.88.88,0,0,0-.87.87V19.63a.88.88,0,0,0,.87.87H19.54a.88.88,0,0,0,.87-.87V11.29a1,1,0,1,1,2,0v8.33A2.88,2.88,0,0,1,19.54,22.5ZM13,17.29,22.88,6a1.5,1.5,0,1,0-2.26-2L12,14,8,9.11A1.5,1.5,0,0,0,5.65,11l5.1,6.25a1.5,1.5,0,0,0,1.14.55h0A1.5,1.5,0,0,0,13,17.29Z"/></svg>
|
After Width: | Height: | Size: 541 B |
@ -880,6 +880,31 @@ table.logs .metadata {
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{# Validator panel
|
||||||
|
========================================================================= #}
|
||||||
|
|
||||||
|
#collector-content .sf-validator {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#collector-content .sf-validator .sf-validator-context,
|
||||||
|
#collector-content .sf-validator .trace {
|
||||||
|
border: 1px solid #DDD;
|
||||||
|
background: #FFF;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
#collector-content .sf-validator .trace {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
#collector-content .sf-validator .trace li {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#collector-content .sf-validator .trace li.selected {
|
||||||
|
background: rgba(255, 255, 153, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
{# Dump panel
|
{# Dump panel
|
||||||
========================================================================= #}
|
========================================================================= #}
|
||||||
#collector-content .sf-dump {
|
#collector-content .sf-dump {
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Validator\DataCollector;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||||
|
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\VarCloner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
|
||||||
|
*/
|
||||||
|
class ValidatorDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||||
|
{
|
||||||
|
private $validator;
|
||||||
|
private $cloner;
|
||||||
|
|
||||||
|
public function __construct(TraceableValidator $validator)
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
$this->data = array(
|
||||||
|
'calls' => array(),
|
||||||
|
'violations_count' => 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||||
|
{
|
||||||
|
// Everything is collected once, on kernel terminate.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function lateCollect()
|
||||||
|
{
|
||||||
|
$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']);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCalls()
|
||||||
|
{
|
||||||
|
return $this->data['calls'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getViolationsCount()
|
||||||
|
{
|
||||||
|
return $this->data['violations_count'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'validator';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function cloneVar($var)
|
||||||
|
{
|
||||||
|
if ($var instanceof Data) {
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $this->cloner) {
|
||||||
|
$this->cloner = new VarCloner();
|
||||||
|
$this->cloner->setMaxItems(-1);
|
||||||
|
$this->cloner->addCasters(array(
|
||||||
|
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())),
|
||||||
|
Caster::PREFIX_VIRTUAL.'data' => $f->getData(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cloner->cloneVar($var, Caster::EXCLUDE_VERBOSE);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Validator\Tests\DataCollector;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolation;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolationList;
|
||||||
|
use Symfony\Component\Validator\DataCollector\ValidatorDataCollector;
|
||||||
|
use Symfony\Component\Validator\Validator\TraceableValidator;
|
||||||
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||||
|
|
||||||
|
class ValidatorDataCollectorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testCollectsValidatorCalls()
|
||||||
|
{
|
||||||
|
$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();
|
||||||
|
|
||||||
|
$calls = $collector->getCalls();
|
||||||
|
|
||||||
|
$this->assertCount(1, $calls);
|
||||||
|
$this->assertSame(2, $collector->getViolationsCount());
|
||||||
|
|
||||||
|
$call = $calls[0];
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('caller', $call);
|
||||||
|
$this->assertArrayHasKey('context', $call);
|
||||||
|
$this->assertArrayHasKey('violations', $call);
|
||||||
|
$this->assertCount(2, $call['violations']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createMock($classname)
|
||||||
|
{
|
||||||
|
return $this->getMockBuilder($classname)->disableOriginalConstructor()->getMock();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Validator\Tests\Validator;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolation;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolationList;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||||
|
use Symfony\Component\Validator\Validator\ContextualValidatorInterface;
|
||||||
|
use Symfony\Component\Validator\Validator\TraceableValidator;
|
||||||
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||||
|
|
||||||
|
class TraceableValidatorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testValidate()
|
||||||
|
{
|
||||||
|
$originalValidator = $this->createMock(ValidatorInterface::class);
|
||||||
|
$violations = new ConstraintViolationList(array(
|
||||||
|
$this->createMock(ConstraintViolation::class),
|
||||||
|
$this->createMock(ConstraintViolation::class),
|
||||||
|
));
|
||||||
|
$originalValidator->expects($this->exactly(2))->method('validate')->willReturn($violations);
|
||||||
|
|
||||||
|
$validator = new TraceableValidator($originalValidator);
|
||||||
|
|
||||||
|
$object = new \stdClass();
|
||||||
|
$constraints = array($this->createMock(Constraint::class));
|
||||||
|
$groups = array('Default', 'Create');
|
||||||
|
|
||||||
|
$validator->validate($object, $constraints, $groups);
|
||||||
|
$line = __LINE__ - 1;
|
||||||
|
|
||||||
|
$collectedData = $validator->getCollectedData();
|
||||||
|
|
||||||
|
$this->assertCount(1, $collectedData);
|
||||||
|
|
||||||
|
$callData = $collectedData[0];
|
||||||
|
|
||||||
|
$this->assertSame(iterator_to_array($violations), $callData['violations']);
|
||||||
|
|
||||||
|
$this->assertSame(array(
|
||||||
|
'value' => $object,
|
||||||
|
'constraints' => $constraints,
|
||||||
|
'groups' => $groups,
|
||||||
|
), $callData['context']);
|
||||||
|
|
||||||
|
$this->assertEquals(array(
|
||||||
|
'name' => 'TraceableValidatorTest.php',
|
||||||
|
'file' => __FILE__,
|
||||||
|
'line' => $line,
|
||||||
|
), $callData['caller']);
|
||||||
|
|
||||||
|
$validator->validate($object, $constraints, $groups);
|
||||||
|
$collectedData = $validator->getCollectedData();
|
||||||
|
|
||||||
|
$this->assertCount(2, $collectedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testForwardsToOriginalValidator()
|
||||||
|
{
|
||||||
|
$originalValidator = $this->createMock(ValidatorInterface::class);
|
||||||
|
$validator = new TraceableValidator($originalValidator);
|
||||||
|
|
||||||
|
$expects = function ($method) use ($originalValidator) { return $originalValidator->expects($this->once())->method($method); };
|
||||||
|
|
||||||
|
$expects('getMetadataFor')->willReturn($expected = $this->createMock(MetadataInterface::class));
|
||||||
|
$this->assertSame($expected, $validator->getMetadataFor('value'), 'returns original validator getMetadataFor() result');
|
||||||
|
|
||||||
|
$expects('hasMetadataFor')->willReturn($expected = false);
|
||||||
|
$this->assertSame($expected, $validator->hasMetadataFor('value'), 'returns original validator hasMetadataFor() result');
|
||||||
|
|
||||||
|
$expects('inContext')->willReturn($expected = $this->createMock(ContextualValidatorInterface::class));
|
||||||
|
$this->assertSame($expected, $validator->inContext($this->createMock(ExecutionContextInterface::class)), 'returns original validator inContext() result');
|
||||||
|
|
||||||
|
$expects('startContext')->willReturn($expected = $this->createMock(ContextualValidatorInterface::class));
|
||||||
|
$this->assertSame($expected, $validator->startContext(), 'returns original validator startContext() result');
|
||||||
|
|
||||||
|
$expects('validate')->willReturn($expected = $this->createMock(ConstraintViolationListInterface::class));
|
||||||
|
$this->assertSame($expected, $validator->validate('value'), 'returns original validator validate() result');
|
||||||
|
|
||||||
|
$expects('validateProperty')->willReturn($expected = $this->createMock(ConstraintViolationListInterface::class));
|
||||||
|
$this->assertSame($expected, $validator->validateProperty(new \stdClass(), 'property'), 'returns original validator validateProperty() result');
|
||||||
|
|
||||||
|
$expects('validatePropertyValue')->willReturn($expected = $this->createMock(ConstraintViolationListInterface::class));
|
||||||
|
$this->assertSame($expected, $validator->validatePropertyValue(new \stdClass(), 'property', 'value'), 'returns original validator validatePropertyValue() result');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createMock($classname)
|
||||||
|
{
|
||||||
|
return $this->getMockBuilder($classname)->disableOriginalConstructor()->getMock();
|
||||||
|
}
|
||||||
|
}
|
131
src/Symfony/Component/Validator/Validator/TraceableValidator.php
Normal file
131
src/Symfony/Component/Validator/Validator/TraceableValidator.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Validator\Validator;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\ConstraintViolationList;
|
||||||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects some data about validator calls.
|
||||||
|
*
|
||||||
|
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
|
||||||
|
*/
|
||||||
|
class TraceableValidator implements ValidatorInterface
|
||||||
|
{
|
||||||
|
private $validator;
|
||||||
|
private $collectedData = array();
|
||||||
|
|
||||||
|
public function __construct(ValidatorInterface $validator)
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ConstraintViolationList[]
|
||||||
|
*/
|
||||||
|
public function getCollectedData()
|
||||||
|
{
|
||||||
|
return $this->collectedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getMetadataFor($value)
|
||||||
|
{
|
||||||
|
return $this->validator->getMetadataFor($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function hasMetadataFor($value)
|
||||||
|
{
|
||||||
|
return $this->validator->hasMetadataFor($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validate($value, $constraints = null, $groups = null)
|
||||||
|
{
|
||||||
|
$violations = $this->validator->validate($value, $constraints, $groups);
|
||||||
|
|
||||||
|
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7);
|
||||||
|
|
||||||
|
$file = $trace[0]['file'];
|
||||||
|
$line = $trace[0]['line'];
|
||||||
|
|
||||||
|
for ($i = 1; $i < 7; ++$i) {
|
||||||
|
if (isset($trace[$i]['class'], $trace[$i]['function'])
|
||||||
|
&& 'validate' === $trace[$i]['function']
|
||||||
|
&& is_a($trace[$i]['class'], ValidatorInterface::class, true)
|
||||||
|
) {
|
||||||
|
$file = $trace[$i]['file'];
|
||||||
|
$line = $trace[$i]['line'];
|
||||||
|
|
||||||
|
while (++$i < 7) {
|
||||||
|
if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) {
|
||||||
|
$file = $trace[$i]['file'];
|
||||||
|
$line = $trace[$i]['line'];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = str_replace('\\', '/', $file);
|
||||||
|
$name = substr($name, strrpos($name, '/') + 1);
|
||||||
|
|
||||||
|
$this->collectedData[] = array(
|
||||||
|
'caller' => compact('name', 'file', 'line'),
|
||||||
|
'context' => compact('value', 'constraints', 'groups'),
|
||||||
|
'violations' => iterator_to_array($violations),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validateProperty($object, $propertyName, $groups = null)
|
||||||
|
{
|
||||||
|
return $this->validator->validateProperty($object, $propertyName, $groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validatePropertyValue($objectOrClass, $propertyName, $value, $groups = null)
|
||||||
|
{
|
||||||
|
return $this->validator->validatePropertyValue($objectOrClass, $propertyName, $value, $groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function startContext()
|
||||||
|
{
|
||||||
|
return $this->validator->startContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function inContext(ExecutionContextInterface $context)
|
||||||
|
{
|
||||||
|
return $this->validator->inContext($context);
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,8 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/http-foundation": "~2.8|~3.0|~4.0",
|
"symfony/http-foundation": "~2.8|~3.0|~4.0",
|
||||||
|
"symfony/http-kernel": "~2.8|~3.0|~4.0.0",
|
||||||
|
"symfony/var-dumper": "~3.3|~4.0.0",
|
||||||
"symfony/intl": "^2.8.18|^3.2.5|~4.0",
|
"symfony/intl": "^2.8.18|^3.2.5|~4.0",
|
||||||
"symfony/yaml": "~3.3|~4.0",
|
"symfony/yaml": "~3.3|~4.0",
|
||||||
"symfony/config": "~2.8|~3.0|~4.0",
|
"symfony/config": "~2.8|~3.0|~4.0",
|
||||||
|
Reference in New Issue
Block a user