Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] Serialization versioning #35617

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
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode)
->end()
->end()
->end()
->scalarNode('default_version')->end()
->end()
->end()
->end()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,12 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
$defaultContext += ['max_depth_handler' => new Reference($config['max_depth_handler'])];
$container->getDefinition('serializer.normalizer.object')->replaceArgument(6, $defaultContext);
}

if (isset($config['default_version']) && $config['default_version']) {
$defaultContext = $container->getDefinition('serializer.normalizer.object')->getArgument(6);
$defaultContext += ['version' => $config['default_version']];
$container->getDefinition('serializer.normalizer.object')->replaceArgument(6, $defaultContext);
}
}

private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@
<xsd:attribute name="name-converter" type="xsd:string" />
<xsd:attribute name="circular-reference-handler" type="xsd:string" />
<xsd:attribute name="max-depth-handler" type="xsd:string" />
<xsd:attribute name="default-version" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="property_info">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
'name_converter' => 'serializer.name_converter.camel_case_to_snake_case',
'circular_reference_handler' => 'my.circular.reference.handler',
'max_depth_handler' => 'my.max.depth.handler',
'default_version' => '1.0.0',
],
'property_info' => true,
'ide' => 'file%%link%%format',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</framework:translator>
<framework:validation enabled="true" />
<framework:annotations cache="file" debug="true" file-cache-dir="%kernel.cache_dir%/annotations" />
<framework:serializer enabled="true" enable-annotations="true" name-converter="serializer.name_converter.camel_case_to_snake_case" circular-reference-handler="my.circular.reference.handler" max-depth-handler="my.max.depth.handler" />
<framework:serializer enabled="true" enable-annotations="true" name-converter="serializer.name_converter.camel_case_to_snake_case" circular-reference-handler="my.circular.reference.handler" max-depth-handler="my.max.depth.handler" default-version="1.0.0" />
<framework:property-info />
</framework:config>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ framework:
name_converter: serializer.name_converter.camel_case_to_snake_case
circular_reference_handler: my.circular.reference.handler
max_depth_handler: my.max.depth.handler
default_version: 1.0.0
property_info: ~
ide: file%%link%%format
request:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,8 @@ public function testSerializerEnabled()
$this->assertArrayHasKey('circular_reference_handler', $container->getDefinition('serializer.normalizer.object')->getArgument(6));
$this->assertArrayHasKey('max_depth_handler', $container->getDefinition('serializer.normalizer.object')->getArgument(6));
$this->assertEquals($container->getDefinition('serializer.normalizer.object')->getArgument(6)['max_depth_handler'], new Reference('my.max.depth.handler'));
$this->assertArrayHasKey('version', $container->getDefinition('serializer.normalizer.object')->getArgument(6));
$this->assertEquals($container->getDefinition('serializer.normalizer.object')->getArgument(6)['version'], '1.0.0');
}

public function testRegisterSerializerExtractor()
Expand Down
48 changes: 48 additions & 0 deletions src/Symfony/Component/Serializer/Annotation/Since.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Annotation;

use Symfony\Component\Serializer\Exception\InvalidArgumentException;

/**
* Annotation class for @Since().
*
* @Annotation
* @Target({"PROPERTY", "METHOD"})
*
* @author Arnaud Tarroux <[email protected]>
*/
class Since
{
/**
* @var string
*/
private $version;

public function __construct(array $data)
{
if (!isset($data['value'])) {
throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" should be set.', static::class));
}

if (!\is_string($data['value']) || empty($data['value'])) {
throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a non-empty string.', static::class));
}

$this->version = $data['value'];
}

public function getVersion(): string
{
return $this->version;
}
}
48 changes: 48 additions & 0 deletions src/Symfony/Component/Serializer/Annotation/Until.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Annotation;

use Symfony\Component\Serializer\Exception\InvalidArgumentException;

/**
* Annotation class for @Until().
*
* @Annotation
* @Target({"PROPERTY", "METHOD"})
*
* @author Arnaud Tarroux <[email protected]>
*/
class Until
{
/**
* @var string
*/
private $version;

public function __construct(array $data)
{
if (!isset($data['value'])) {
throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" should be set.', static::class));
}

if (!\is_string($data['value']) || empty($data['value'])) {
throw new InvalidArgumentException(sprintf('Parameter of annotation "%s" must be a non-empty string.', static::class));
}

$this->version = $data['value'];
}

public function getVersion(): string
{
return $this->version;
}
}
52 changes: 51 additions & 1 deletion src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,24 @@ class AttributeMetadata implements AttributeMetadataInterface
*/
public $ignore = false;

/**
* @var string|null
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link getSince()} instead.
*/
public $since;

/**
* @var string|null
*
* @internal This property is public in order to reduce the size of the
* class' serialized representation. Do not access it. Use
* {@link getUntil()} instead.
*/
public $until;

public function __construct(string $name)
{
$this->name = $name;
Expand Down Expand Up @@ -162,13 +180,45 @@ public function merge(AttributeMetadataInterface $attributeMetadata)
}
}

/**
* {@inheritdoc}
*/
public function setSince(string $version)
{
$this->since = $version;
}

/**
* {@inheritdoc}
*/
public function getSince(): ?string
{
return $this->since;
}

/**
* {@inheritdoc}
*/
public function setUntil(string $version)
{
$this->until = $version;
}

/**
* {@inheritdoc}
*/
public function getUntil(): ?string
{
return $this->until;
}

/**
* Returns the names of the properties that should be serialized.
*
* @return string[]
*/
public function __sleep()
{
return ['name', 'groups', 'maxDepth', 'serializedName', 'ignore'];
return ['name', 'groups', 'maxDepth', 'serializedName', 'ignore', 'since', 'until'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,24 @@ public function isIgnored(): bool;
* Merges an {@see AttributeMetadataInterface} with in the current one.
*/
public function merge(self $attributeMetadata);

/**
* Sets the version number from which the attribute must be serialized.
*/
public function setSince(string $version);

/**
* Gets the version number from which the attribute must be serialized.
*/
public function getSince(): ?string;

/**
* Sets the version number after which the attribute must not be serialized.
*/
public function setUntil(string $version);

/**
* Gets the version number after which the attribute must not be serialized.
*/
public function getUntil(): ?string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use Symfony\Component\Serializer\Annotation\Ignore;
use Symfony\Component\Serializer\Annotation\MaxDepth;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Serializer\Annotation\Since;
use Symfony\Component\Serializer\Annotation\Until;
use Symfony\Component\Serializer\Exception\MappingException;
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
Expand Down Expand Up @@ -74,6 +76,10 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
$attributesMetadata[$property->name]->setSerializedName($annotation->getSerializedName());
} elseif ($annotation instanceof Ignore) {
$attributesMetadata[$property->name]->setIgnore(true);
} elseif ($annotation instanceof Since) {
$attributesMetadata[$property->name]->setSince($annotation->getVersion());
} elseif ($annotation instanceof Until) {
$attributesMetadata[$property->name]->setUntil($annotation->getVersion());
}

$loaded = true;
Expand Down Expand Up @@ -121,6 +127,18 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
$attributeMetadata->setSerializedName($annotation->getSerializedName());
} elseif ($annotation instanceof Ignore) {
$attributeMetadata->setIgnore(true);
} elseif ($annotation instanceof Since) {
if (!$accessorOrMutator) {
throw new MappingException(sprintf('Since on "%s::%s" cannot be added. Since can only be added on methods beginning with "get", "is", "has" or "set".', $className, $method->name));
}

$attributeMetadata->setSince($annotation->getVersion());
} elseif ($annotation instanceof Until) {
if (!$accessorOrMutator) {
throw new MappingException(sprintf('Until on "%s::%s" cannot be added. Until can only be added on methods beginning with "get", "is", "has" or "set".', $className, $method->name));
}

$attributeMetadata->setUntil($annotation->getVersion());
}

$loaded = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
if (isset($attribute['ignore'])) {
$attributeMetadata->setIgnore((bool) $attribute['ignore']);
}

if (isset($attribute['since'])) {
$attributeMetadata->setSince((string) $attribute['since']);
}

if (isset($attribute['until'])) {
$attributeMetadata->setUntil((string) $attribute['until']);
}
}

if (isset($xml->{'discriminator-map'})) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)

$attributeMetadata->setIgnore($data['ignore']);
}

if (isset($data['since'])) {
$attributeMetadata->setSince((string) $data['since']);
}

if (isset($data['until'])) {
$attributeMetadata->setUntil((string) $data['until']);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="ignore" type="xsd:boolean" />
<xsd:attribute name="since">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:minLength value="1" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="until">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:minLength value="1" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>

</xsd:schema>
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ protected function getAllowedAttributes($classOrObject, array $context, bool $at
if (
!$ignore &&
(false === $groups || array_intersect(array_merge($attributeMetadata->getGroups(), ['*']), $groups)) &&
$this->isAllowedAttribute($classOrObject, $name = $attributeMetadata->getName(), null, $context)
$this->isAllowedAttribute($classOrObject, $name = $attributeMetadata->getName(), null, $context) &&
$this->attributeAllowedWithVersion($context, $attributeMetadata->getSince(), $attributeMetadata->getUntil())
) {
$allowedAttributes[] = $attributesAsString ? $name : $attributeMetadata;
}
Expand Down Expand Up @@ -447,4 +448,25 @@ protected function createChildContext(array $parentContext, string $attribute, ?

return $parentContext;
}

/**
* @internal
*/
private function attributeAllowedWithVersion(array $context, ?string $sinceVersion, ?string $untilVersion)
{
if (!isset($context['version']) && !isset($this->defaultContext['version'])) {
return true;
}

$version = $context['version'] ?? $this->defaultContext['version'];
if (null !== $sinceVersion && version_compare($version, $sinceVersion, '<')) {
return false;
}

if (null !== $untilVersion && version_compare($version, $untilVersion, '>=')) {
return false;
}

return true;
}
}
Loading