Merge branch '4.2'
* 4.2: [serializer] validate that the specified callbacks and max_depth_handler are actually callable [Serializer] Respect ignored attributes in cache key of normalizer fix resetting the COLUMN environment variable Fix TestRunner compatibility to PhpUnit 8 Fix dark themed componnents prevent mixup of the object to populate
This commit is contained in:
commit
9d3c86fdb6
@ -23,6 +23,24 @@ class CommandForV5 extends \PHPUnit_TextUI_Command
|
|||||||
*/
|
*/
|
||||||
protected function createRunner()
|
protected function createRunner()
|
||||||
{
|
{
|
||||||
return new TestRunnerForV5($this->arguments['loader']);
|
$listener = new SymfonyTestsListenerForV5();
|
||||||
|
|
||||||
|
$this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : array();
|
||||||
|
|
||||||
|
$registeredLocally = false;
|
||||||
|
|
||||||
|
foreach ($this->arguments['listeners'] as $registeredListener) {
|
||||||
|
if ($registeredListener instanceof SymfonyTestsListenerForV5) {
|
||||||
|
$registeredListener->globalListenerDisabled();
|
||||||
|
$registeredLocally = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$registeredLocally) {
|
||||||
|
$this->arguments['listeners'][] = $listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::createRunner();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace Symfony\Bridge\PhpUnit\Legacy;
|
|||||||
|
|
||||||
use PHPUnit\TextUI\Command as BaseCommand;
|
use PHPUnit\TextUI\Command as BaseCommand;
|
||||||
use PHPUnit\TextUI\TestRunner as BaseRunner;
|
use PHPUnit\TextUI\TestRunner as BaseRunner;
|
||||||
use Symfony\Bridge\PhpUnit\TextUI\TestRunner;
|
use Symfony\Bridge\PhpUnit\SymfonyTestsListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
@ -27,6 +27,24 @@ class CommandForV6 extends BaseCommand
|
|||||||
*/
|
*/
|
||||||
protected function createRunner(): BaseRunner
|
protected function createRunner(): BaseRunner
|
||||||
{
|
{
|
||||||
return new TestRunner($this->arguments['loader']);
|
$listener = new SymfonyTestsListener();
|
||||||
|
|
||||||
|
$this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : [];
|
||||||
|
|
||||||
|
$registeredLocally = false;
|
||||||
|
|
||||||
|
foreach ($this->arguments['listeners'] as $registeredListener) {
|
||||||
|
if ($registeredListener instanceof SymfonyTestsListener) {
|
||||||
|
$registeredListener->globalListenerDisabled();
|
||||||
|
$registeredLocally = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$registeredLocally) {
|
||||||
|
$this->arguments['listeners'][] = $listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::createRunner();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
<?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\Bridge\PhpUnit\Legacy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
class TestRunnerForV5 extends \PHPUnit_TextUI_TestRunner
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function handleConfiguration(array &$arguments)
|
|
||||||
{
|
|
||||||
$listener = new SymfonyTestsListenerForV5();
|
|
||||||
|
|
||||||
$result = parent::handleConfiguration($arguments);
|
|
||||||
|
|
||||||
$arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array();
|
|
||||||
|
|
||||||
$registeredLocally = false;
|
|
||||||
|
|
||||||
foreach ($arguments['listeners'] as $registeredListener) {
|
|
||||||
if ($registeredListener instanceof SymfonyTestsListenerForV5) {
|
|
||||||
$registeredListener->globalListenerDisabled();
|
|
||||||
$registeredLocally = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$registeredLocally) {
|
|
||||||
$arguments['listeners'][] = $listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
<?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\Bridge\PhpUnit\Legacy;
|
|
||||||
|
|
||||||
use PHPUnit\TextUI\TestRunner as BaseRunner;
|
|
||||||
use Symfony\Bridge\PhpUnit\SymfonyTestsListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
class TestRunnerForV6 extends BaseRunner
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function handleConfiguration(array &$arguments)
|
|
||||||
{
|
|
||||||
$listener = new SymfonyTestsListener();
|
|
||||||
|
|
||||||
parent::handleConfiguration($arguments);
|
|
||||||
|
|
||||||
$arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array();
|
|
||||||
|
|
||||||
$registeredLocally = false;
|
|
||||||
|
|
||||||
foreach ($arguments['listeners'] as $registeredListener) {
|
|
||||||
if ($registeredListener instanceof SymfonyTestsListener) {
|
|
||||||
$registeredListener->globalListenerDisabled();
|
|
||||||
$registeredLocally = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$registeredLocally) {
|
|
||||||
$arguments['listeners'][] = $listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
<?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\Bridge\PhpUnit\Legacy;
|
|
||||||
|
|
||||||
use PHPUnit\TextUI\TestRunner as BaseRunner;
|
|
||||||
use Symfony\Bridge\PhpUnit\SymfonyTestsListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
class TestRunnerForV7 extends BaseRunner
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function handleConfiguration(array &$arguments): void
|
|
||||||
{
|
|
||||||
$listener = new SymfonyTestsListener();
|
|
||||||
|
|
||||||
parent::handleConfiguration($arguments);
|
|
||||||
|
|
||||||
$arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array();
|
|
||||||
|
|
||||||
$registeredLocally = false;
|
|
||||||
|
|
||||||
foreach ($arguments['listeners'] as $registeredListener) {
|
|
||||||
if ($registeredListener instanceof SymfonyTestsListener) {
|
|
||||||
$registeredListener->globalListenerDisabled();
|
|
||||||
$registeredLocally = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$registeredLocally) {
|
|
||||||
$arguments['listeners'][] = $listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?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\Bridge\PhpUnit\TextUI;
|
|
||||||
|
|
||||||
if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) {
|
|
||||||
class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV5', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner');
|
|
||||||
} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) {
|
|
||||||
class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV6', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner');
|
|
||||||
} else {
|
|
||||||
class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV7', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false) {
|
|
||||||
class TestRunner
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -58,6 +58,7 @@
|
|||||||
|
|
||||||
.message-item tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; }
|
.message-item tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; }
|
||||||
td.message-bus-dispatch-caller { background: #f1f2f3; }
|
td.message-bus-dispatch-caller { background: #f1f2f3; }
|
||||||
|
.theme-dark td.message-bus-dispatch-caller { background: var(--base-1); }
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1011,6 +1011,8 @@ table.logs .metadata {
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
.theme-dark tr.status-error td,
|
||||||
|
.theme-dark tr.status-warning td { border-bottom: unset; border-top: unset; }
|
||||||
|
|
||||||
{# Doctrine panel
|
{# Doctrine panel
|
||||||
========================================================================= #}
|
========================================================================= #}
|
||||||
|
@ -48,6 +48,14 @@ class ApplicationTest extends TestCase
|
|||||||
$this->colSize = getenv('COLUMNS');
|
$this->colSize = getenv('COLUMNS');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS');
|
||||||
|
putenv('SHELL_VERBOSITY');
|
||||||
|
unset($_ENV['SHELL_VERBOSITY']);
|
||||||
|
unset($_SERVER['SHELL_VERBOSITY']);
|
||||||
|
}
|
||||||
|
|
||||||
public static function setUpBeforeClass()
|
public static function setUpBeforeClass()
|
||||||
{
|
{
|
||||||
self::$fixturesPath = realpath(__DIR__.'/Fixtures/');
|
self::$fixturesPath = realpath(__DIR__.'/Fixtures/');
|
||||||
@ -1749,14 +1757,6 @@ class ApplicationTest extends TestCase
|
|||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
$tester->run(['command' => 'foo']);
|
$tester->run(['command' => 'foo']);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
|
||||||
{
|
|
||||||
putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize);
|
|
||||||
putenv('SHELL_VERBOSITY');
|
|
||||||
unset($_ENV['SHELL_VERBOSITY']);
|
|
||||||
unset($_SERVER['SHELL_VERBOSITY']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomApplication extends Application
|
class CustomApplication extends Application
|
||||||
|
@ -33,7 +33,7 @@ class ProgressBarTest extends TestCase
|
|||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize);
|
putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMultipleStart()
|
public function testMultipleStart()
|
||||||
|
@ -38,7 +38,7 @@ class SymfonyStyleTest extends TestCase
|
|||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize);
|
putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS');
|
||||||
$this->command = null;
|
$this->command = null;
|
||||||
$this->tester = null;
|
$this->tester = null;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,12 @@ class TerminalTest extends TestCase
|
|||||||
$this->lineSize = getenv('LINES');
|
$this->lineSize = getenv('LINES');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS');
|
||||||
|
putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize);
|
||||||
|
}
|
||||||
|
|
||||||
public function test()
|
public function test()
|
||||||
{
|
{
|
||||||
putenv('COLUMNS=100');
|
putenv('COLUMNS=100');
|
||||||
@ -40,12 +46,6 @@ class TerminalTest extends TestCase
|
|||||||
$this->assertSame(60, $terminal->getHeight());
|
$this->assertSame(60, $terminal->getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
|
||||||
{
|
|
||||||
putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize);
|
|
||||||
putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_zero_values()
|
public function test_zero_values()
|
||||||
{
|
{
|
||||||
putenv('COLUMNS=0');
|
putenv('COLUMNS=0');
|
||||||
|
@ -99,10 +99,14 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
|
|||||||
$this->nameConverter = $nameConverter;
|
$this->nameConverter = $nameConverter;
|
||||||
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
|
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
|
||||||
|
|
||||||
if (\is_array($this->defaultContext[self::CALLBACKS] ?? null)) {
|
if (isset($this->defaultContext[self::CALLBACKS])) {
|
||||||
|
if (!\is_array($this->defaultContext[self::CALLBACKS])) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The "%s" default context option must be an array of callables.', self::CALLBACKS));
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->defaultContext[self::CALLBACKS] as $attribute => $callback) {
|
foreach ($this->defaultContext[self::CALLBACKS] as $attribute => $callback) {
|
||||||
if (!\is_callable($callback)) {
|
if (!\is_callable($callback)) {
|
||||||
throw new InvalidArgumentException(sprintf('The given callback for attribute "%s" is not callable.', $attribute));
|
throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" default context option.', $attribute, self::CALLBACKS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,6 +389,8 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
|
|||||||
|
|
||||||
return $object;
|
return $object;
|
||||||
}
|
}
|
||||||
|
// clean up even if no match
|
||||||
|
unset($context[static::OBJECT_TO_POPULATE]);
|
||||||
|
|
||||||
$constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes);
|
$constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes);
|
||||||
if ($constructor) {
|
if ($constructor) {
|
||||||
@ -453,7 +459,7 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
|
|||||||
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $parameter->getClass(), self::class));
|
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $parameter->getClass(), self::class));
|
||||||
}
|
}
|
||||||
$parameterClass = $parameter->getClass()->getName();
|
$parameterClass = $parameter->getClass()->getName();
|
||||||
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName));
|
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName, $format));
|
||||||
}
|
}
|
||||||
} catch (\ReflectionException $e) {
|
} catch (\ReflectionException $e) {
|
||||||
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
|
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
|
||||||
@ -468,14 +474,15 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $parentContext
|
* @param array $parentContext
|
||||||
* @param string $attribute
|
* @param string $attribute Attribute name
|
||||||
|
* @param string|null $format
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected function createChildContext(array $parentContext, $attribute)
|
protected function createChildContext(array $parentContext, $attribute/*, string $format = null */)
|
||||||
{
|
{
|
||||||
if (isset($parentContext[self::ATTRIBUTES][$attribute])) {
|
if (isset($parentContext[self::ATTRIBUTES][$attribute])) {
|
||||||
$parentContext[self::ATTRIBUTES] = $parentContext[self::ATTRIBUTES][$attribute];
|
$parentContext[self::ATTRIBUTES] = $parentContext[self::ATTRIBUTES][$attribute];
|
||||||
|
@ -61,6 +61,11 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
|
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
|
||||||
{
|
{
|
||||||
parent::__construct($classMetadataFactory, $nameConverter, $defaultContext);
|
parent::__construct($classMetadataFactory, $nameConverter, $defaultContext);
|
||||||
|
|
||||||
|
if (isset($this->defaultContext[self::MAX_DEPTH_HANDLER]) && !\is_callable($this->defaultContext[self::MAX_DEPTH_HANDLER])) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The "%s" given in the default context is not callable.', self::MAX_DEPTH_HANDLER));
|
||||||
|
}
|
||||||
|
|
||||||
$this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS];
|
$this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS];
|
||||||
|
|
||||||
$this->propertyTypeExtractor = $propertyTypeExtractor;
|
$this->propertyTypeExtractor = $propertyTypeExtractor;
|
||||||
@ -89,6 +94,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
$context['cache_key'] = $this->getCacheKey($format, $context);
|
$context['cache_key'] = $this->getCacheKey($format, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($context[self::CALLBACKS])) {
|
||||||
|
if (!\is_array($context[self::CALLBACKS])) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The "%s" context option must be an array of callables.', self::CALLBACKS));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($context[self::CALLBACKS] as $attribute => $callback) {
|
||||||
|
if (!\is_callable($callback)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" context option.', $attribute, self::CALLBACKS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->isCircularReference($object, $context)) {
|
if ($this->isCircularReference($object, $context)) {
|
||||||
return $this->handleCircularReference($object, $format, $context);
|
return $this->handleCircularReference($object, $format, $context);
|
||||||
}
|
}
|
||||||
@ -98,7 +115,15 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
$attributes = $this->getAttributes($object, $format, $context);
|
$attributes = $this->getAttributes($object, $format, $context);
|
||||||
$class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
|
$class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
|
||||||
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;
|
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;
|
||||||
$maxDepthHandler = $context[self::MAX_DEPTH_HANDLER] ?? $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler;
|
if (isset($context[self::MAX_DEPTH_HANDLER])) {
|
||||||
|
$maxDepthHandler = $context[self::MAX_DEPTH_HANDLER];
|
||||||
|
if (!\is_callable($maxDepthHandler)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The "%s" given in the context is not callable.', self::MAX_DEPTH_HANDLER));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// already validated in constructor resp by type declaration of setMaxDepthHandler
|
||||||
|
$maxDepthHandler = $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($attributes as $attribute) {
|
foreach ($attributes as $attribute) {
|
||||||
$maxDepthReached = false;
|
$maxDepthReached = false;
|
||||||
@ -131,7 +156,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer', $attribute));
|
throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer', $attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute)), $class, $format, $context);
|
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute, $format)), $class, $format, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@ -187,21 +212,17 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
return $allowedAttributes;
|
return $allowedAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($context[self::ATTRIBUTES] ?? $this->defaultContext[self::ATTRIBUTES] ?? false) {
|
|
||||||
return $this->extractAttributes($object, $format, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->attributesCache[$class])) {
|
|
||||||
return $this->attributesCache[$class];
|
|
||||||
}
|
|
||||||
|
|
||||||
$attributes = $this->extractAttributes($object, $format, $context);
|
$attributes = $this->extractAttributes($object, $format, $context);
|
||||||
|
|
||||||
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
|
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
|
||||||
array_unshift($attributes, $mapping->getTypeProperty());
|
array_unshift($attributes, $mapping->getTypeProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->attributesCache[$class] = $attributes;
|
if ($context['cache_key']) {
|
||||||
|
$this->attributesCache[$key] = $attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -356,7 +377,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer', $attribute, $class));
|
throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer', $attribute, $class));
|
||||||
}
|
}
|
||||||
|
|
||||||
$childContext = $this->createChildContext($context, $attribute);
|
$childContext = $this->createChildContext($context, $attribute, $format);
|
||||||
if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) {
|
if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) {
|
||||||
return $this->serializer->denormalize($data, $class, $format, $childContext);
|
return $this->serializer->denormalize($data, $class, $format, $childContext);
|
||||||
}
|
}
|
||||||
@ -486,7 +507,32 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the cache key to use.
|
* Overwritten to update the cache key for the child.
|
||||||
|
*
|
||||||
|
* We must not mix up the attribute cache between parent and children.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function createChildContext(array $parentContext, $attribute/*, string $format = null */)
|
||||||
|
{
|
||||||
|
if (\func_num_args() >= 3) {
|
||||||
|
$format = \func_get_arg(2);
|
||||||
|
} else {
|
||||||
|
// will be deprecated in version 4
|
||||||
|
$format = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = parent::createChildContext($parentContext, $attribute, $format);
|
||||||
|
// format is already included in the cache_key of the parent.
|
||||||
|
$context['cache_key'] = $this->getCacheKey($format, $context);
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the cache key for the attributes cache.
|
||||||
|
*
|
||||||
|
* The key must be different for every option in the context that could change which attributes should be handled.
|
||||||
*
|
*
|
||||||
* @return bool|string
|
* @return bool|string
|
||||||
*/
|
*/
|
||||||
@ -496,9 +542,14 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
|||||||
unset($context[$key]);
|
unset($context[$key]);
|
||||||
}
|
}
|
||||||
unset($context[self::EXCLUDE_FROM_CACHE_KEY]);
|
unset($context[self::EXCLUDE_FROM_CACHE_KEY]);
|
||||||
|
unset($context['cache_key']); // avoid artificially different keys
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return md5($format.serialize($context));
|
return md5($format.serialize([
|
||||||
|
'context' => $context,
|
||||||
|
'ignored' => $this->ignoredAttributes,
|
||||||
|
'camelized' => $this->camelizedAttributes,
|
||||||
|
]));
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
// The context cannot be serialized, skip the cache
|
// The context cannot be serialized, skip the cache
|
||||||
return false;
|
return false;
|
||||||
|
@ -394,6 +394,30 @@ class ObjectNormalizerTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testObjectToPopulateNoMatch()
|
||||||
|
{
|
||||||
|
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||||
|
$this->normalizer = new ObjectNormalizer($classMetadataFactory, null, null, new PhpDocExtractor());
|
||||||
|
new Serializer([$this->normalizer]);
|
||||||
|
|
||||||
|
$objectToPopulate = new ObjectInner();
|
||||||
|
$objectToPopulate->foo = 'foo';
|
||||||
|
|
||||||
|
$outer = $this->normalizer->denormalize([
|
||||||
|
'foo' => 'foo',
|
||||||
|
'inner' => [
|
||||||
|
'bar' => 'bar',
|
||||||
|
],
|
||||||
|
], ObjectOuter::class, null, [ObjectNormalizer::OBJECT_TO_POPULATE => $objectToPopulate]);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(ObjectOuter::class, $outer);
|
||||||
|
$inner = $outer->getInner();
|
||||||
|
$this->assertInstanceOf(ObjectInner::class, $inner);
|
||||||
|
$this->assertNotSame($objectToPopulate, $inner);
|
||||||
|
$this->assertSame('bar', $inner->bar);
|
||||||
|
$this->assertNull($inner->foo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider provideCallbacks
|
* @dataProvider provideCallbacks
|
||||||
*/
|
*/
|
||||||
@ -472,6 +496,16 @@ class ObjectNormalizerTest extends TestCase
|
|||||||
['fooBar' => 'foobar'],
|
['fooBar' => 'foobar'],
|
||||||
$this->normalizer->normalize($obj, 'any')
|
$this->normalizer->normalize($obj, 'any')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->normalizer->setIgnoredAttributes(['foo', 'baz', 'camelCase', 'object']);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
[
|
||||||
|
'fooBar' => 'foobar',
|
||||||
|
'bar' => 'bar',
|
||||||
|
],
|
||||||
|
$this->normalizer->normalize($obj, 'any')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIgnoredAttributesDenormalize()
|
public function testIgnoredAttributesDenormalize()
|
||||||
@ -781,7 +815,11 @@ class ObjectNormalizerTest extends TestCase
|
|||||||
$this->normalizer->setMaxDepthHandler($handler);
|
$this->normalizer->setMaxDepthHandler($handler);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->createNormalizer([ObjectNormalizer::MAX_DEPTH_HANDLER => $handler], $classMetadataFactory);
|
$context = [];
|
||||||
|
if (null !== $handler) {
|
||||||
|
$context[ObjectNormalizer::MAX_DEPTH_HANDLER] = $handler;
|
||||||
|
}
|
||||||
|
$this->createNormalizer($context, $classMetadataFactory);
|
||||||
}
|
}
|
||||||
$this->serializer = new Serializer([$this->normalizer]);
|
$this->serializer = new Serializer([$this->normalizer]);
|
||||||
$this->normalizer->setSerializer($this->serializer);
|
$this->normalizer->setSerializer($this->serializer);
|
||||||
@ -1208,6 +1246,9 @@ class ObjectOuter
|
|||||||
{
|
{
|
||||||
public $foo;
|
public $foo;
|
||||||
public $bar;
|
public $bar;
|
||||||
|
/**
|
||||||
|
* @var ObjectInner
|
||||||
|
*/
|
||||||
private $inner;
|
private $inner;
|
||||||
private $date;
|
private $date;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user