merged branch stof/serializer_improvement (PR #4904)

Commits
-------

12bdec3 Moved the NormalizationAwareInterface check to the ChainEncoder
28e137c [Serializer] Added a ChainEncoder and a ChainDecoder

Discussion
----------

[Serializer] Added a ChainEncoder and a ChainDecoder

Bug fix: no
Feature addition: yes
Backwards compatibility break: no
Symfony2 tests pass: [![Build Status](https://secure.travis-ci.org/stof/symfony.png?branch=serializer_improvement)](http://travis-ci.org/stof/symfony)
Fixes the following tickets: -
Todo: -

These classes contains the logic previously defined in the Serializer itself to handle the choice of a serializer. This allows reusing it when using only the encoding part of the component, without having to use the Serializer class (which is not as handy to bootstrap when you want to use only encoders and decoders as normalizers come first)

I was wondering if these classes should have adders but I kept the constructor injection only to be consistent with the current code (encoders cannot be registered after the instantiation) and to avoid implementing the SerializerAwareInterface in them (to allow injecting the Serializer in serializer-aware encoders and decoders added later).

Note that this change is fully BC as it only changes the internal implementation of the Serializer.

---------------------------------------------------------------------------

by fabpot at 2012-07-14T11:07:32Z

ping @lsmith77 @Seldaek

---------------------------------------------------------------------------

by Seldaek at 2012-07-14T15:17:42Z

After a quick look, I'd say +1
This commit is contained in:
Fabien Potencier 2012-07-14 18:10:40 +02:00
commit 36d12dde5b
3 changed files with 206 additions and 72 deletions

View File

@ -0,0 +1,82 @@
<?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;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
/**
* Decoder delegating the decoding to a chain of decoders.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
*/
class ChainDecoder implements DecoderInterface
{
protected $decoders = array();
protected $decoderByFormat = array();
public function __construct(array $decoders = array())
{
$this->decoders = $decoders;
}
/**
* {@inheritdoc}
*/
final public function decode($data, $format)
{
return $this->getDecoder($format)->decode($data, $format);
}
/**
* {@inheritdoc}
*/
public function supportsDecoding($format)
{
try {
$this->getDecoder($format);
} catch (RuntimeException $e) {
return false;
}
return true;
}
/**
* Gets the decoder supporting the format.
*
* @param string $format
*
* @return DecoderInterface
* @throws RuntimeException if no decoder is found
*/
private function getDecoder($format)
{
if (isset($this->decoderByFormat[$format])
&& isset($this->decoders[$this->decoderByFormat[$format]])
) {
return $this->decoders[$this->decoderByFormat[$format]];
}
foreach ($this->decoders as $i => $decoder) {
if ($decoder->supportsDecoding($format)) {
$this->decoderByFormat[$format] = $i;
return $decoder;
}
}
throw new RuntimeException(sprintf('No decoder found for format "%s".', $format));
}
}

View File

@ -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\Serializer\Encoder;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
/**
* Encoder delegating the decoding to a chain of encoders.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
*/
class ChainEncoder implements EncoderInterface
{
protected $encoders = array();
protected $encoderByFormat = array();
public function __construct(array $encoders = array())
{
$this->encoders = $encoders;
}
/**
* {@inheritdoc}
*/
final public function encode($data, $format)
{
return $this->getEncoder($format)->encode($data, $format);
}
/**
* {@inheritdoc}
*/
public function supportsEncoding($format)
{
try {
$this->getEncoder($format);
} catch (RuntimeException $e) {
return false;
}
return true;
}
/**
* Checks whether the normalization is needed for the given format.
*
* @param string $format
*
* @return Boolean
*/
public function needsNormalization($format)
{
$encoder = $this->getEncoder($format);
if (!$encoder instanceof NormalizationAwareInterface) {
return true;
}
if ($encoder instanceof self) {
return $encoder->needsNormalization($format);
}
return false;
}
/**
* Gets the encoder supporting the format.
*
* @param string $format
*
* @return EncoderInterface
* @throws RuntimeException if no encoder is found
*/
private function getEncoder($format)
{
if (isset($this->encoderByFormat[$format])
&& isset($this->encoders[$this->encoderByFormat[$format]])
) {
return $this->encoders[$this->encoderByFormat[$format]];
}
foreach ($this->encoders as $i => $encoder) {
if ($encoder->supportsEncoding($format)) {
$this->encoderByFormat[$format] = $i;
return $encoder;
}
}
throw new RuntimeException(sprintf('No encoder found for format "%s".', $format));
}
}

View File

@ -11,9 +11,10 @@
namespace Symfony\Component\Serializer;
use Symfony\Component\Serializer\Encoder\ChainDecoder;
use Symfony\Component\Serializer\Encoder\ChainEncoder;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
@ -36,12 +37,11 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException;
*/
class Serializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface, EncoderInterface, DecoderInterface
{
protected $encoder;
protected $decoder;
protected $normalizers = array();
protected $encoders = array();
protected $normalizerCache = array();
protected $denormalizerCache = array();
protected $encoderByFormat = array();
protected $decoderByFormat = array();
public function __construct(array $normalizers = array(), array $encoders = array())
{
@ -52,12 +52,21 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
}
$this->normalizers = $normalizers;
$decoders = array();
$realEncoders = array();
foreach ($encoders as $encoder) {
if ($encoder instanceof SerializerAwareInterface) {
$encoder->setSerializer($this);
}
if ($encoder instanceof DecoderInterface) {
$decoders[] = $encoder;
}
if ($encoder instanceof EncoderInterface) {
$realEncoders[] = $encoder;
}
}
$this->encoders = $encoders;
$this->encoder = new ChainEncoder($realEncoders);
$this->decoder = new ChainDecoder($decoders);
}
/**
@ -69,9 +78,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
throw new UnexpectedValueException('Serialization for the format '.$format.' is not supported');
}
$encoder = $this->getEncoder($format);
if (!$encoder instanceof NormalizationAwareInterface) {
if ($this->encoder->needsNormalization($format)) {
$data = $this->normalize($data, $format);
}
@ -197,7 +204,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
final public function encode($data, $format)
{
return $this->getEncoder($format)->encode($data, $format);
return $this->encoder->encode($data, $format);
}
/**
@ -205,7 +212,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
final public function decode($data, $format)
{
return $this->getEncoder($format)->decode($data, $format);
return $this->decoder->decode($data, $format);
}
/**
@ -271,13 +278,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
public function supportsEncoding($format)
{
try {
$this->getEncoder($format);
} catch (RuntimeException $e) {
return false;
}
return true;
return $this->encoder->supportsEncoding($format);
}
/**
@ -285,60 +286,6 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
public function supportsDecoding($format)
{
try {
$this->getDecoder($format);
} catch (RuntimeException $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
private function getEncoder($format)
{
if (isset($this->encoderByFormat[$format])
&& isset($this->encoders[$this->encoderByFormat[$format]])
) {
return $this->encoders[$this->encoderByFormat[$format]];
}
foreach ($this->encoders as $i => $encoder) {
if ($encoder instanceof EncoderInterface
&& $encoder->supportsEncoding($format)
) {
$this->encoderByFormat[$format] = $i;
return $encoder;
}
}
throw new RuntimeException(sprintf('No encoder found for format "%s".', $format));
}
/**
* {@inheritdoc}
*/
private function getDecoder($format)
{
if (isset($this->decoderByFormat[$format])
&& isset($this->encoders[$this->decoderByFormat[$format]])
) {
return $this->encoders[$this->decoderByFormat[$format]];
}
foreach ($this->encoders as $i => $encoder) {
if ($encoder instanceof DecoderInterface
&& $encoder->supportsDecoding($format)
) {
$this->decoderByFormat[$format] = $i;
return $encoder;
}
}
throw new RuntimeException(sprintf('No decoder found for format "%s".', $format));
return $this->decoder->supportsDecoding($format);
}
}