[Serializer] Give access to the context to support* methods

This commit is contained in:
Kévin Dunglas 2016-07-16 18:39:49 +02:00 committed by Fabien Potencier
parent a0f1e850c0
commit 6a7a16e517
11 changed files with 227 additions and 49 deletions

View File

@ -22,7 +22,7 @@ use Symfony\Component\Serializer\Exception\RuntimeException;
*
* @final since version 3.3.
*/
class ChainDecoder implements DecoderInterface
class ChainDecoder implements DecoderInterface /*, ContextAwareDecoderInterface*/
{
protected $decoders = array();
protected $decoderByFormat = array();
@ -37,16 +37,18 @@ class ChainDecoder implements DecoderInterface
*/
final public function decode($data, $format, array $context = array())
{
return $this->getDecoder($format)->decode($data, $format, $context);
return $this->getDecoder($format, $context)->decode($data, $format, $context);
}
/**
* {@inheritdoc}
*/
public function supportsDecoding($format)
public function supportsDecoding($format/*, array $context = array()*/)
{
$context = func_num_args() > 1 ? func_get_arg(1) : array();
try {
$this->getDecoder($format);
$this->getDecoder($format, $context);
} catch (RuntimeException $e) {
return false;
}
@ -58,12 +60,13 @@ class ChainDecoder implements DecoderInterface
* Gets the decoder supporting the format.
*
* @param string $format
* @param array $context
*
* @return DecoderInterface
*
* @throws RuntimeException If no decoder is found.
*/
private function getDecoder($format)
private function getDecoder($format, array $context)
{
if (isset($this->decoderByFormat[$format])
&& isset($this->decoders[$this->decoderByFormat[$format]])
@ -72,7 +75,7 @@ class ChainDecoder implements DecoderInterface
}
foreach ($this->decoders as $i => $decoder) {
if ($decoder->supportsDecoding($format)) {
if ($decoder->supportsDecoding($format, $context)) {
$this->decoderByFormat[$format] = $i;
return $decoder;

View File

@ -22,7 +22,7 @@ use Symfony\Component\Serializer\Exception\RuntimeException;
*
* @final since version 3.3.
*/
class ChainEncoder implements EncoderInterface
class ChainEncoder implements EncoderInterface /*, ContextAwareEncoderInterface*/
{
protected $encoders = array();
protected $encoderByFormat = array();
@ -37,16 +37,18 @@ class ChainEncoder implements EncoderInterface
*/
final public function encode($data, $format, array $context = array())
{
return $this->getEncoder($format)->encode($data, $format, $context);
return $this->getEncoder($format, $context)->encode($data, $format, $context);
}
/**
* {@inheritdoc}
*/
public function supportsEncoding($format)
public function supportsEncoding($format/*, array $context = array()*/)
{
$context = func_num_args() > 1 ? func_get_arg(1) : array();
try {
$this->getEncoder($format);
$this->getEncoder($format, $context);
} catch (RuntimeException $e) {
return false;
}
@ -58,19 +60,21 @@ class ChainEncoder implements EncoderInterface
* Checks whether the normalization is needed for the given format.
*
* @param string $format
* @param array $context
*
* @return bool
*/
public function needsNormalization($format)
public function needsNormalization($format/*, array $context = array()*/)
{
$encoder = $this->getEncoder($format);
$context = func_num_args() > 1 ? func_get_arg(1) : array();
$encoder = $this->getEncoder($format, $context);
if (!$encoder instanceof NormalizationAwareInterface) {
return true;
}
if ($encoder instanceof self) {
return $encoder->needsNormalization($format);
return $encoder->needsNormalization($format, $context);
}
return false;
@ -80,12 +84,13 @@ class ChainEncoder implements EncoderInterface
* Gets the encoder supporting the format.
*
* @param string $format
* @param array $context
*
* @return EncoderInterface
*
* @throws RuntimeException if no encoder is found
*/
private function getEncoder($format)
private function getEncoder($format, array $context)
{
if (isset($this->encoderByFormat[$format])
&& isset($this->encoders[$this->encoderByFormat[$format]])
@ -94,7 +99,7 @@ class ChainEncoder implements EncoderInterface
}
foreach ($this->encoders as $i => $encoder) {
if ($encoder->supportsEncoding($format)) {
if ($encoder->supportsEncoding($format, $context)) {
$this->encoderByFormat[$format] = $i;
return $encoder;

View File

@ -0,0 +1,27 @@
<?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\Serializer\Encoder;
/**
* Adds the support of an extra $context parameter for the supportsDecoding method.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface ContextAwareDecoderInterface extends DecoderInterface
{
/**
* {@inheritdoc}
*
* @param array $context options that decoders have access to
*/
public function supportsDecoding($format, array $context = array());
}

View File

@ -0,0 +1,27 @@
<?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\Serializer\Encoder;
/**
* Adds the support of an extra $context parameter for the supportsEncoding method.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface ContextAwareEncoderInterface extends EncoderInterface
{
/**
* {@inheritdoc}
*
* @param array $context options that encoders have access to
*/
public function supportsEncoding($format, array $context = array());
}

View File

@ -267,7 +267,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));
}
if ($this->serializer->supportsDenormalization($data, $class, $format)) {
if ($this->serializer->supportsDenormalization($data, $class, $format, $context)) {
return $this->serializer->denormalize($data, $class, $format, $context);
}
}

View File

@ -21,6 +21,8 @@ use Symfony\Component\Serializer\SerializerInterface;
* Denormalizes arrays of objects.
*
* @author Alexander M. Turek <me@derrabus.de>
*
* @final since version 3.3.
*/
class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterface
{
@ -64,10 +66,12 @@ class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterfa
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
public function supportsDenormalization($data, $type, $format = null/*, array $context = array()*/)
{
$context = func_num_args() > 3 ? func_get_arg(3) : array();
return substr($type, -2) === '[]'
&& $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format);
&& $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format, $context);
}
/**

View File

@ -0,0 +1,27 @@
<?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\Serializer\Normalizer;
/**
* Adds the support of an extra $context parameter for the supportsDenormalization method.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface ContextAwareDenormalizerInterface extends DenormalizerInterface
{
/**
* {@inheritdoc}
*
* @param array $context options that denormalizers have access to
*/
public function supportsDenormalization($data, $type, $format = null, array $context = array());
}

View File

@ -0,0 +1,27 @@
<?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\Serializer\Normalizer;
/**
* Adds the support of an extra $context parameter for the supportsNormalization method.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface ContextAwareNormalizerInterface extends NormalizerInterface
{
/**
* {@inheritdoc}
*
* @param array $context options that normalizers have access to
*/
public function supportsNormalization($data, $format = null, array $context = array());
}

View File

@ -138,7 +138,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
public function normalize($data, $format = null, array $context = array())
{
// If a normalizer supports the given data, use it
if ($normalizer = $this->getNormalizer($data, $format)) {
if ($normalizer = $this->getNormalizer($data, $format, $context)) {
return $normalizer->normalize($data, $format, $context);
}
@ -177,31 +177,58 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
/**
* {@inheritdoc}
*/
public function supportsNormalization($data, $format = null)
public function supportsNormalization($data, $format = null/*, array $context = array()*/)
{
return null !== $this->getNormalizer($data, $format);
if (func_num_args() > 2) {
$context = func_get_arg(2);
} else {
if (__CLASS__ !== get_class($this)) {
$r = new \ReflectionMethod($this, __FUNCTION__);
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Method %s() will have a third `$context = array()` argument in version 4.0. Not defining it is deprecated since 3.3.', get_class($this), __FUNCTION__), E_USER_DEPRECATED);
}
}
$context = array();
}
return null !== $this->getNormalizer($data, $format, $context);
}
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
public function supportsDenormalization($data, $type, $format = null/*, array $context = array()*/)
{
return null !== $this->getDenormalizer($data, $type, $format);
if (func_num_args() > 3) {
$context = func_get_arg(3);
} else {
if (__CLASS__ !== get_class($this)) {
$r = new \ReflectionMethod($this, __FUNCTION__);
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Method %s() will have a fourth `$context = array()` argument in version 4.0. Not defining it is deprecated since 3.3.', get_class($this), __FUNCTION__), E_USER_DEPRECATED);
}
}
$context = array();
}
return null !== $this->getDenormalizer($data, $type, $format, $context);
}
/**
* Returns a matching normalizer.
*
* @param mixed $data Data to get the serializer for
* @param string $format format name, present to give the option to normalizers to act differently based on formats
* @param mixed $data data to get the serializer for
* @param string $format format name, present to give the option to normalizers to act differently based on formats
* @param array $context options available to the normalizer
*
* @return NormalizerInterface|null
*/
private function getNormalizer($data, $format)
private function getNormalizer($data, $format, array $context)
{
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format)) {
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format, $context)) {
return $normalizer;
}
}
@ -210,16 +237,17 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
/**
* Returns a matching denormalizer.
*
* @param mixed $data data to restore
* @param string $class the expected class to instantiate
* @param string $format format name, present to give the option to normalizers to act differently based on formats
* @param mixed $data data to restore
* @param string $class the expected class to instantiate
* @param string $format format name, present to give the option to normalizers to act differently based on formats
* @param array $context options available to the denormalizer
*
* @return DenormalizerInterface|null
*/
private function getDenormalizer($data, $class, $format)
private function getDenormalizer($data, $class, $format, array $context)
{
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format)) {
if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format, $context)) {
return $normalizer;
}
}
@ -260,7 +288,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
}
if ($normalizer = $this->getDenormalizer($data, $class, $format)) {
if ($normalizer = $this->getDenormalizer($data, $class, $format, $context)) {
return $normalizer->denormalize($data, $class, $format, $context);
}
@ -270,16 +298,42 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
/**
* {@inheritdoc}
*/
public function supportsEncoding($format)
public function supportsEncoding($format/*, array $context = array()*/)
{
return $this->encoder->supportsEncoding($format);
if (func_num_args() > 1) {
$context = func_get_arg(1);
} else {
if (__CLASS__ !== get_class($this)) {
$r = new \ReflectionMethod($this, __FUNCTION__);
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Method %s() will have a second `$context = array()` argument in version 4.0. Not defining it is deprecated since 3.3.', get_class($this), __FUNCTION__), E_USER_DEPRECATED);
}
}
$context = array();
}
return $this->encoder->supportsEncoding($format, $context);
}
/**
* {@inheritdoc}
*/
public function supportsDecoding($format)
public function supportsDecoding($format/*, array $context = array()*/)
{
return $this->decoder->supportsDecoding($format);
if (func_num_args() > 1) {
$context = func_get_arg(1);
} else {
if (__CLASS__ !== get_class($this)) {
$r = new \ReflectionMethod($this, __FUNCTION__);
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Method %s() will have a second `$context = array()` argument in version 4.0. Not defining it is deprecated since 3.3.', get_class($this), __FUNCTION__), E_USER_DEPRECATED);
}
}
$context = array();
}
return $this->decoder->supportsDecoding($format, $context);
}
}

View File

@ -32,9 +32,10 @@ class ChainDecoderTest extends \PHPUnit_Framework_TestCase
$this->decoder1
->method('supportsDecoding')
->will($this->returnValueMap(array(
array(self::FORMAT_1, true),
array(self::FORMAT_2, false),
array(self::FORMAT_3, false),
array(self::FORMAT_1, array(), true),
array(self::FORMAT_2, array(), false),
array(self::FORMAT_3, array(), false),
array(self::FORMAT_3, array('foo' => 'bar'), true),
)));
$this->decoder2 = $this
@ -44,9 +45,9 @@ class ChainDecoderTest extends \PHPUnit_Framework_TestCase
$this->decoder2
->method('supportsDecoding')
->will($this->returnValueMap(array(
array(self::FORMAT_1, false),
array(self::FORMAT_2, true),
array(self::FORMAT_3, false),
array(self::FORMAT_1, array(), false),
array(self::FORMAT_2, array(), true),
array(self::FORMAT_3, array(), false),
)));
$this->chainDecoder = new ChainDecoder(array($this->decoder1, $this->decoder2));
@ -57,6 +58,7 @@ class ChainDecoderTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_1));
$this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_2));
$this->assertFalse($this->chainDecoder->supportsDecoding(self::FORMAT_3));
$this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_3, array('foo' => 'bar')));
}
public function testDecode()

View File

@ -33,9 +33,10 @@ class ChainEncoderTest extends \PHPUnit_Framework_TestCase
$this->encoder1
->method('supportsEncoding')
->will($this->returnValueMap(array(
array(self::FORMAT_1, true),
array(self::FORMAT_2, false),
array(self::FORMAT_3, false),
array(self::FORMAT_1, array(), true),
array(self::FORMAT_2, array(), false),
array(self::FORMAT_3, array(), false),
array(self::FORMAT_3, array('foo' => 'bar'), true),
)));
$this->encoder2 = $this
@ -45,9 +46,9 @@ class ChainEncoderTest extends \PHPUnit_Framework_TestCase
$this->encoder2
->method('supportsEncoding')
->will($this->returnValueMap(array(
array(self::FORMAT_1, false),
array(self::FORMAT_2, true),
array(self::FORMAT_3, false),
array(self::FORMAT_1, array(), false),
array(self::FORMAT_2, array(), true),
array(self::FORMAT_3, array(), false),
)));
$this->chainEncoder = new ChainEncoder(array($this->encoder1, $this->encoder2));
@ -58,6 +59,7 @@ class ChainEncoderTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_1));
$this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_2));
$this->assertFalse($this->chainEncoder->supportsEncoding(self::FORMAT_3));
$this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_3, array('foo' => 'bar')));
}
public function testEncode()