From 28e137c92038c139445c325ea4c15e0021e5bf5b Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 14 Jul 2012 10:46:16 +0200 Subject: [PATCH 1/2] [Serializer] Added a ChainEncoder and a ChainDecoder 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. --- .../Serializer/Encoder/ChainDecoder.php | 82 ++++++++++++++++++ .../Serializer/Encoder/ChainEncoder.php | 82 ++++++++++++++++++ .../Component/Serializer/Serializer.php | 85 +++++-------------- 3 files changed, 183 insertions(+), 66 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Encoder/ChainDecoder.php create mode 100644 src/Symfony/Component/Serializer/Encoder/ChainEncoder.php diff --git a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php new file mode 100644 index 0000000000..f555c6a8c9 --- /dev/null +++ b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php @@ -0,0 +1,82 @@ + + * + * 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 + * @author Johannes M. Schmitt + * @author Lukas Kahwe Smith + */ +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)); + } +} diff --git a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php new file mode 100644 index 0000000000..73fdd64959 --- /dev/null +++ b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php @@ -0,0 +1,82 @@ + + * + * 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\Exception\RuntimeException; + +/** + * Encoder delegating the decoding to a chain of encoders. + * + * @author Jordi Boggiano + * @author Johannes M. Schmitt + * @author Lukas Kahwe Smith + */ +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; + } + + /** + * Gets the encoder supporting the format. + * + * @param string $format + * + * @return EncoderInterface + * @throws RuntimeException if no encoder is found + */ + public 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)); + } +} diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 77500a1064..15587fbf81 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -11,6 +11,8 @@ 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; @@ -36,6 +38,8 @@ 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(); @@ -52,12 +56,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,7 +82,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz throw new UnexpectedValueException('Serialization for the format '.$format.' is not supported'); } - $encoder = $this->getEncoder($format); + $encoder = $this->encoder->getEncoder($format); if (!$encoder instanceof NormalizationAwareInterface) { $data = $this->normalize($data, $format); @@ -197,7 +210,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 +218,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 +284,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 +292,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); } } From 12bdec3cd2e6367498a889a84305c992b192d80a Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sat, 14 Jul 2012 11:05:55 +0200 Subject: [PATCH 2/2] Moved the NormalizationAwareInterface check to the ChainEncoder This allows nesting a ChainEncoder inside another one without breaking the check. --- .../Serializer/Encoder/ChainEncoder.php | 25 ++++++++++++++++++- .../Component/Serializer/Serializer.php | 8 +----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php index 73fdd64959..ebb81efb9f 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Serializer\Encoder; use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface; use Symfony\Component\Serializer\Exception\RuntimeException; /** @@ -53,6 +54,28 @@ class ChainEncoder implements EncoderInterface 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. * @@ -61,7 +84,7 @@ class ChainEncoder implements EncoderInterface * @return EncoderInterface * @throws RuntimeException if no encoder is found */ - public function getEncoder($format) + private function getEncoder($format) { if (isset($this->encoderByFormat[$format]) && isset($this->encoders[$this->encoderByFormat[$format]]) diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 15587fbf81..f2c513baec 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -15,7 +15,6 @@ 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; @@ -41,11 +40,8 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz 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()) { @@ -82,9 +78,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz throw new UnexpectedValueException('Serialization for the format '.$format.' is not supported'); } - $encoder = $this->encoder->getEncoder($format); - - if (!$encoder instanceof NormalizationAwareInterface) { + if ($this->encoder->needsNormalization($format)) { $data = $this->normalize($data, $format); }