8000 [Serializer] Add a NormalizerDumper by GuilhemN · Pull Request #22051 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Serializer] Add a NormalizerDumper #22051

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add a config option to enable the feature
  • Loading branch information
GuilhemN committed Feb 19, 2018
commit 32abf6c45b9200d5b6348e705705cb15d1e619ea
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode)
->info('serializer configuration')
->{!class_exists(FullStack::class) && class_exists(Serializer::class) ? 'canBeDisabled' : 'canBeEnabled'}()
->children()
->booleanNode('enable_normalizer_generation')->defaultFalse()->end()
->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end()
->scalarNode('name_converter')->end()
->scalarNode('circular_reference_handler')->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,12 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder

if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) {
$container->getDefinition('serializer.normalizer.object')->addMethodCall('setCircularReferenceHandler', array(new Reference($config['circular_reference_handler'])));
$container->getDefinition('serializer.normalizer.object.generated')->addMethodCall('setCircularReferenceHandler', array(new Reference($config['circular_reference_handler'])));
}

if (!$config['enable_normalizer_generation']) {
$container->removeDefinition('serializer.normalizer.object.generated');
$container->removeDefinition('serializer.normalizer.object.dumper');
}

if ($config['max_depth_handler'] ?? false) {
Expand Down
13 changes: 13 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@
</service>
<service id="Symfony\Component\Serializer\Normalizer\ObjectNormalizer" alias="serializer.normalizer.object" />

<service id="serializer.normalizer.object.generated" class="Symfony\Component\Serializer\Normalizer\GeneratedObjectNormalizer">
<argument type="service" id="serializer.normalizer.object.dumper" />
<argument>%kernel.cache_dir%</argument>
<argument>%kernel.debug%</argument>

<!-- Run after all custom normalizers but before the object normalizer -->
<tag name="serializer.normalizer" priority="-995" />
</service>

<service id="serializer.normalizer.object.dumper" class="Symfony\Component\Serializer\Dumper\NormalizerDumper">
<argument type="service" id="serializer.mapping.class_metadata_factory" />
</service>

<service id="serializer.denormalizer.array" class="Symfony\Component\Serializer\Normalizer\ArrayDenormalizer">
<!-- Run before the object normalizer -->
<tag name="serializer.normalizer" priority="-990" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ protected static function getBundleDefaultConfig()
),
'serializer' => array(
'enabled' => !class_exists(FullStack::class),
'enable_normalizer_generation' => false,
'enable_annotations' => !class_exists(FullStack::class),
'mapping' => array('paths' => array()),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,32 @@ public function testDeserializeArrayOfObject()

$this->assertEquals($expected, $result);
}

/**
* @dataProvider caseProvider
*/
public function testSerializeArrayOfObject($testCase)
{
static::bootKernel(array('test_case' => $testCase));
$container = static::$kernel->getContainer();

$bar1 = new Bar();
$bar1->id = 1;
$bar2 = new Bar();
$bar2->id = 2;

$foo = new Foo();
$foo->bars = array($bar1, $bar2);

$result = $container->get('serializer')->normalize($foo);

$this->assertEquals(array('bars' => array(array('id' => 1), array('id' => 2))), $result);
}

public function caseProvider()
{
return array(array('Serializer'), array('GeneratedSerializer'));
}
}

class Foo
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?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.
*/

use Symfony\Bundle\FrameworkBundle\FrameworkBundle;

return array(
new FrameworkBundle(),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
imports:
- { resource: ../config/default.yml }

framework:
serializer:
enable_normalizer_generation: true
enable_annotations: true # required to detect properties
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/FrameworkBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"symfony/process": "~3.4|~4.0",
"symfony/security-core": "~3.4|~4.0",
"symfony/security-csrf": "~3.4|~4.0",
"symfony/serializer": "~3.4|~4.0",
"symfony/serializer": "~4.1",
"symfony/stopwatch": "~3.4|~4.0",
"symfony/translation": "~3.4|~4.0",
"symfony/templating": "~3.4|~4.0",
Expand All @@ -66,7 +66,7 @@
"symfony/asset": "<3.4",
"symfony/console": "<3.4",
"symfony/form": "<3.4",
"symfony/property-info": "<3.4",
"symfony/property-info": "<4.1",
"symfony/serializer": "<3.4",
"symfony/stopwatch": "<3.4",
"symfony/translation": "<3.4",
Expand Down
11 changes: 5 additions & 6 deletions src/Symfony/Component/Serializer/Dumper/NormalizerDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
/**
* @author Guilhem Niot <guilhem.niot@gmail.com>
* @author Amrouche Hamza <hamza.simperfit@gmail.com>
*
* @experimental
*/
final class NormalizerDumper
Expand All @@ -42,6 +43,7 @@ public function dump(string $class, array $context = array())
<?php
$namespaceLine
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Normalizer\CircularReferenceTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
Expand All @@ -53,7 +55,7 @@ public function dump(string $class, array $context = array())
*/
class {$context['class']} implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
use CircularReferenceTrait, NormalizerAwareTrait;

{$this->generateNormalizeMethod($reflectionClass)}

Expand All @@ -79,11 +81,8 @@ private function generateNormalizeMethodInner(\ReflectionClass $reflectionClass)
{
$code = <<<EOL

\$objectHash = spl_object_hash(\$object);
if (isset(\$context[ObjectNormalizer::CIRCULAR_REFERENCE_LIMIT][\$objectHash])) {
return null;
} else {
\$context[ObjectNormalizer::CIRCULAR_REFERENCE_LIMIT][\$objectHash] = 1;
if (\$this->isCircularReference(\$object, \$context)) {
return \$this->handleCircularReference(\$object);
}

\$groups = isset(\$context[ObjectNormalizer::GROUPS]) && is_array(\$context[ObjectNormalizer::GROUPS]) ? \$context[ObjectNormalizer::GROUPS] : null;
Expand Down
95 changes: 6 additions & 89 deletions src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace Symfony\Component\Serializer\Normalizer;

use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\LogicException;
Expand All @@ -29,6 +28,7 @@
*/
abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface
{
use CircularReferenceTrait;
use ObjectToPopulateTrait;
use SerializerAwareTrait;

Expand All @@ -39,16 +39,6 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
const ALLOW_EXTRA_ATTRIBUTES = 'allow_extra_attributes';
const DEFAULT_CONSTRUCTOR_ARGUMENTS = 'default_constructor_arguments';

/**
* @var int
*/
protected $circularReferenceLimit = 1;

/**
* @var callable
*/
protected $circularReferenceHandler;

/**
* @var ClassMetadataFactoryInterface|null
*/
Expand Down Expand Up @@ -83,34 +73,6 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory
$this->nameConverter = $nameConverter;
}

/**
* Set circular reference limit.
*
* @param int $circularReferenceLimit Limit of iterations for the same object
*
* @return self
*/
public function setCircularReferenceLimit($circularReferenceLimit)
{
$this->circularReferenceLimit = $circularReferenceLimit;

return $this;
}

/**
* Set circular reference handler.
*
* @param callable $circularReferenceHandler
*
* @return self
*/
public function setCircularReferenceHandler(callable $circularReferenceHandler)
{
$this->circularReferenceHandler = $circularReferenceHandler;

return $this;
}

/**
* Set normalization callbacks.
*
Expand Down Expand Up @@ -147,56 +109,6 @@ public function setIgnoredAttributes(array $ignoredAttributes)
return $this;
}

/**
* Detects if the configured circular reference limit is reached.
*
* @param object $object
* @param array $context
*
* @return bool
*
* @throws CircularReferenceException
*/
protected function isCircularReference($object, &$context)
{
$objectHash = spl_object_hash($object);

if (isset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash])) {
if ($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash] >= $this->circularReferenceLimit) {
unset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash]);

return true;
}

++$context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash];
} else {
$context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash] = 1;
}

return false;
}

/**
* Handles a circular reference.
*
* If a circular reference handler is set, it will be called. Otherwise, a
* {@class CircularReferenceException} will be thrown.
*
* @param object $object
*
* @return mixed
*
* @throws CircularReferenceException
*/
protected function handleCircularReference($object)
{
if ($this->circularReferenceHandler) {
return \call_user_func($this->circularReferenceHandler, $object);
}

throw new CircularReferenceException(sprintf('A circular reference has been detected when serializing the object of class "%s" (configured limit: %d)', \get_class($object), $this->circularReferenceLimit));
}

/**
* Gets attributes to normalize using groups.
*
Expand Down Expand Up @@ -404,4 +316,9 @@ protected function createChildContext(array $parentContext, $attribute)

return $parentContext;
}

private function getCircularReferenceLimitField()
{
return static::CIRCULAR_REFERENCE_LIMIT;
}
}
Loading
0