feature #10457 [Serializer] Unify usage of normalizer cache (Berdir)

This PR was squashed before being merged into the 2.5-dev branch (closes #10457).

Discussion
----------

[Serializer] Unify usage of normalizer cache

| Q             | A
| ------------- | ---
| Bug fix?      | no (unless performance problems are considered bugs)
| New feature?  | no
| BC breaks?    | no (unless the exception structure should be simplified)
| Deprecations? | no
| Tests pass?   | yes ( I had some test fails locally when running the whole suite, but not related to Serializer)
| Fixed tickets |
| License       | MIT
| Doc PR        |

Some clean-up of the Serialization class, which has methods to get the Normalizer/Denormalizer but then repeats that logic in normalizeObject()/denormalizeObject().

Took great care to keep the exception behavior exactly like it is now as the tests depend on it, but it's strange (the methods use different exception classes if no normalizer is found and the "no normalizer" LogicException only exists in normalizeObject/denomalizeObject. That could easily be simplified further, to the point where those functions could easily be merged into the calling methods, as it would just be a single line of code. There is also a duplicate call to $this->normalizeObject() in normalize() that just exists to throw the LogicException for Symfony\Component\Serializer\Tests\SerializerTest::testSerializeNoNormalizer. Other tests do not except that to be thrown there, though.

Also noticed that getNormalizer()/getDenormalizer() are documented as @inheritdocs, which is a lie as they are private. Added some basic docs there.

This is performance relevant, as not having the cache in the getter methods means all normalizers are checked again to verify if a normalizer supports the data.

Commits
-------

e7389aa Move normalizer cache to getNormalier()/getDenormalizer(), use those in normalizeObject()/denormalizeObject()
This commit is contained in:
Fabien Potencier 2014-03-27 08:44:58 +01:00
commit cb147eccb2
1 changed files with 41 additions and 50 deletions

View File

@ -17,7 +17,6 @@ use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@ -144,13 +143,7 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
public function supportsNormalization($data, $format = null)
{
try {
$this->getNormalizer($data, $format);
} catch (RuntimeException $e) {
return false;
}
return true;
return (bool) $this->getNormalizer($data, $format);
}
/**
@ -158,41 +151,60 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
public function supportsDenormalization($data, $type, $format = null)
{
try {
$this->getDenormalizer($data, $type, $format = null);
} catch (RuntimeException $e) {
return false;
}
return true;
return (bool) $this->getDenormalizer($data, $type, $format);
}
/**
* {@inheritdoc}
* Returns a matching normalizer.
*
* @param object $data The object to get the serializer for
* @param string $format format name, present to give the option to normalizers to act differently based on formats
*
* @return NormalizerInterface|null
*/
private function getNormalizer($data, $format = null)
private function getNormalizer($data, $format)
{
$class = get_class($data);
if (isset($this->normalizerCache[$class][$format])) {
return $this->normalizerCache[$class][$format];
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format)) {
$this->normalizerCache[$class][$format] = $normalizer;
return $normalizer;
}
}
throw new RuntimeException(sprintf('No normalizer found for format "%s".', $format));
return null;
}
/**
* {@inheritdoc}
* 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
*
* @return DenormalizerInterface|null
*/
private function getDenormalizer($data, $type, $format = null)
private function getDenormalizer($data, $class, $format)
{
if (isset($this->denormalizerCache[$class][$format])) {
return $this->denormalizerCache[$class][$format];
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $type, $format)) {
if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format)) {
$this->denormalizerCache[$class][$format] = $normalizer;
return $normalizer;
}
}
throw new RuntimeException(sprintf('No denormalizer found for format "%s".', $format));
return null;
}
/**
@ -223,27 +235,16 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
* @throws LogicException
* @throws UnexpectedValueException
*/
private function normalizeObject($object, $format = null, array $context = array())
private function normalizeObject($object, $format, array $context = array())
{
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
}
$class = get_class($object);
if (isset($this->normalizerCache[$class][$format])) {
return $this->normalizerCache[$class][$format]->normalize($object, $format, $context);
if ($normalizer = $this->getNormalizer($object, $format)) {
return $normalizer->normalize($object, $format, $context);
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof NormalizerInterface
&& $normalizer->supportsNormalization($object, $format)) {
$this->normalizerCache[$class][$format] = $normalizer;
return $normalizer->normalize($object, $format, $context);
}
}
throw new UnexpectedValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', $class));
throw new UnexpectedValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', get_class($object)));
}
/**
@ -259,25 +260,15 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
* @throws LogicException
* @throws UnexpectedValueException
*/
private function denormalizeObject($data, $class, $format = null, array $context = array())
private function denormalizeObject($data, $class, $format, array $context = array())
{
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
}
if (isset($this->denormalizerCache[$class][$format])) {
return $this->denormalizerCache[$class][$format]->denormalize($data, $class, $format, $context);
if ($normalizer = $this->getDenormalizer($data, $class, $format)) {
return $normalizer->denormalize($data, $class, $format, $context);
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof DenormalizerInterface
&& $normalizer->supportsDenormalization($data, $class, $format)) {
$this->denormalizerCache[$class][$format] = $normalizer;
return $normalizer->denormalize($data, $class, $format, $context);
}
}
throw new UnexpectedValueException(sprintf('Could not denormalize object of type %s, no supporting normalizer found.', $class));
}