vendor/symfony/serializer/Serializer.php line 72

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Serializer;
  11. use Symfony\Component\Serializer\Encoder\ChainDecoder;
  12. use Symfony\Component\Serializer\Encoder\ChainEncoder;
  13. use Symfony\Component\Serializer\Encoder\ContextAwareDecoderInterface;
  14. use Symfony\Component\Serializer\Encoder\ContextAwareEncoderInterface;
  15. use Symfony\Component\Serializer\Encoder\DecoderInterface;
  16. use Symfony\Component\Serializer\Encoder\EncoderInterface;
  17. use Symfony\Component\Serializer\Exception\LogicException;
  18. use Symfony\Component\Serializer\Exception\NotEncodableValueException;
  19. use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
  20. use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
  21. use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
  22. use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
  23. use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
  24. use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
  25. use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
  26. use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
  27. use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
  28. /**
  29.  * Serializer serializes and deserializes data.
  30.  *
  31.  * objects are turned into arrays by normalizers.
  32.  * arrays are turned into various output formats by encoders.
  33.  *
  34.  *     $serializer->serialize($obj, 'xml')
  35.  *     $serializer->decode($data, 'xml')
  36.  *     $serializer->denormalize($data, 'Class', 'xml')
  37.  *
  38.  * @author Jordi Boggiano <j.boggiano@seld.be>
  39.  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  40.  * @author Lukas Kahwe Smith <smith@pooteeweet.org>
  41.  * @author Kévin Dunglas <dunglas@gmail.com>
  42.  */
  43. class Serializer implements SerializerInterfaceContextAwareNormalizerInterfaceContextAwareDenormalizerInterfaceContextAwareEncoderInterfaceContextAwareDecoderInterface
  44. {
  45.     /**
  46.      * @var Encoder\ChainEncoder
  47.      */
  48.     protected $encoder;
  49.     /**
  50.      * @var Encoder\ChainDecoder
  51.      */
  52.     protected $decoder;
  53.     /**
  54.      * @internal since Symfony 4.1
  55.      */
  56.     protected $normalizers = [];
  57.     private $cachedNormalizers;
  58.     private $denormalizerCache = [];
  59.     private $normalizerCache = [];
  60.     /**
  61.      * @param array<NormalizerInterface|DenormalizerInterface> $normalizers
  62.      * @param array<EncoderInterface|DecoderInterface>         $encoders
  63.      */
  64.     public function __construct(array $normalizers = [], array $encoders = [])
  65.     {
  66.         foreach ($normalizers as $normalizer) {
  67.             if ($normalizer instanceof SerializerAwareInterface) {
  68.                 $normalizer->setSerializer($this);
  69.             }
  70.             if ($normalizer instanceof DenormalizerAwareInterface) {
  71.                 $normalizer->setDenormalizer($this);
  72.             }
  73.             if ($normalizer instanceof NormalizerAwareInterface) {
  74.                 $normalizer->setNormalizer($this);
  75.             }
  76.             if (!($normalizer instanceof NormalizerInterface || $normalizer instanceof DenormalizerInterface)) {
  77.                 @trigger_error(sprintf('Passing normalizers ("%s") which do not implement either "%s" or "%s" has been deprecated since Symfony 4.2.', \get_class($normalizer), NormalizerInterface::class, DenormalizerInterface::class), \E_USER_DEPRECATED);
  78.                 // throw new \InvalidArgumentException(sprintf('The class "%s" does not implement "%s" or "%s".', \get_class($normalizer), NormalizerInterface::class, DenormalizerInterface::class));
  79.             }
  80.         }
  81.         $this->normalizers $normalizers;
  82.         $decoders = [];
  83.         $realEncoders = [];
  84.         foreach ($encoders as $encoder) {
  85.             if ($encoder instanceof SerializerAwareInterface) {
  86.                 $encoder->setSerializer($this);
  87.             }
  88.             if ($encoder instanceof DecoderInterface) {
  89.                 $decoders[] = $encoder;
  90.             }
  91.             if ($encoder instanceof EncoderInterface) {
  92.                 $realEncoders[] = $encoder;
  93.             }
  94.             if (!($encoder instanceof EncoderInterface || $encoder instanceof DecoderInterface)) {
  95.                 @trigger_error(sprintf('Passing encoders ("%s") which do not implement either "%s" or "%s" has been deprecated since Symfony 4.2.', \get_class($encoder), EncoderInterface::class, DecoderInterface::class), \E_USER_DEPRECATED);
  96.                 // throw new \InvalidArgumentException(sprintf('The class "%s" does not implement "%s" or "%s".', \get_class($normalizer), EncoderInterface::class, DecoderInterface::class));
  97.             }
  98.         }
  99.         $this->encoder = new ChainEncoder($realEncoders);
  100.         $this->decoder = new ChainDecoder($decoders);
  101.     }
  102.     /**
  103.      * {@inheritdoc}
  104.      */
  105.     final public function serialize($data$format, array $context = []): string
  106.     {
  107.         if (!$this->supportsEncoding($format$context)) {
  108.             throw new NotEncodableValueException(sprintf('Serialization for the format "%s" is not supported.'$format));
  109.         }
  110.         if ($this->encoder->needsNormalization($format$context)) {
  111.             $data $this->normalize($data$format$context);
  112.         }
  113.         return $this->encode($data$format$context);
  114.     }
  115.     /**
  116.      * {@inheritdoc}
  117.      */
  118.     final public function deserialize($data$type$format, array $context = [])
  119.     {
  120.         if (!$this->supportsDecoding($format$context)) {
  121.             throw new NotEncodableValueException(sprintf('Deserialization for the format "%s" is not supported.'$format));
  122.         }
  123.         $data $this->decode($data$format$context);
  124.         return $this->denormalize($data$type$format$context);
  125.     }
  126.     /**
  127.      * {@inheritdoc}
  128.      */
  129.     public function normalize($data$format null, array $context = [])
  130.     {
  131.         // If a normalizer supports the given data, use it
  132.         if ($normalizer $this->getNormalizer($data$format$context)) {
  133.             return $normalizer->normalize($data$format$context);
  134.         }
  135.         if (null === $data || is_scalar($data)) {
  136.             return $data;
  137.         }
  138.         if (is_iterable($data)) {
  139.             if (($context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] ?? false) === true && $data instanceof \Countable && === $data->count()) {
  140.                 return $data;
  141.             }
  142.             $normalized = [];
  143.             foreach ($data as $key => $val) {
  144.                 $normalized[$key] = $this->normalize($val$format$context);
  145.             }
  146.             return $normalized;
  147.         }
  148.         if (\is_object($data)) {
  149.             if (!$this->normalizers) {
  150.                 throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
  151.             }
  152.             throw new NotNormalizableValueException(sprintf('Could not normalize object of type "%s", no supporting normalizer found.', \get_class($data)));
  153.         }
  154.         throw new NotNormalizableValueException('An unexpected value could not be normalized: '.(!\is_resource($data) ? var_export($datatrue) : sprintf('%s resource'get_resource_type($data))));
  155.     }
  156.     /**
  157.      * {@inheritdoc}
  158.      *
  159.      * @throws NotNormalizableValueException
  160.      */
  161.     public function denormalize($data$type$format null, array $context = [])
  162.     {
  163.         if (!$this->normalizers) {
  164.             throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
  165.         }
  166.         if ($normalizer $this->getDenormalizer($data$type$format$context)) {
  167.             return $normalizer->denormalize($data$type$format$context);
  168.         }
  169.         throw new NotNormalizableValueException(sprintf('Could not denormalize object of type "%s", no supporting normalizer found.'$type));
  170.     }
  171.     /**
  172.      * {@inheritdoc}
  173.      */
  174.     public function supportsNormalization($data$format null, array $context = [])
  175.     {
  176.         return null !== $this->getNormalizer($data$format$context);
  177.     }
  178.     /**
  179.      * {@inheritdoc}
  180.      */
  181.     public function supportsDenormalization($data$type$format null, array $context = [])
  182.     {
  183.         return null !== $this->getDenormalizer($data$type$format$context);
  184.     }
  185.     /**
  186.      * Returns a matching normalizer.
  187.      *
  188.      * @param mixed  $data    Data to get the serializer for
  189.      * @param string $format  Format name, present to give the option to normalizers to act differently based on formats
  190.      * @param array  $context Options available to the normalizer
  191.      */
  192.     private function getNormalizer($data, ?string $format, array $context): ?NormalizerInterface
  193.     {
  194.         if ($this->cachedNormalizers !== $this->normalizers) {
  195.             $this->cachedNormalizers $this->normalizers;
  196.             $this->denormalizerCache $this->normalizerCache = [];
  197.         }
  198.         $type = \is_object($data) ? \get_class($data) : 'native-'.\gettype($data);
  199.         if (!isset($this->normalizerCache[$format][$type])) {
  200.             $this->normalizerCache[$format][$type] = [];
  201.             foreach ($this->normalizers as $k => $normalizer) {
  202.                 if (!$normalizer instanceof NormalizerInterface) {
  203.                     continue;
  204.                 }
  205.                 if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) {
  206.                     $this->normalizerCache[$format][$type][$k] = false;
  207.                 } elseif ($normalizer->supportsNormalization($data$format$context)) {
  208.                     $this->normalizerCache[$format][$type][$k] = true;
  209.                     break;
  210.                 }
  211.             }
  212.         }
  213.         foreach ($this->normalizerCache[$format][$type] as $k => $cached) {
  214.             $normalizer $this->normalizers[$k];
  215.             if ($cached || $normalizer->supportsNormalization($data$format$context)) {
  216.                 return $normalizer;
  217.             }
  218.         }
  219.         return null;
  220.     }
  221.     /**
  222.      * Returns a matching denormalizer.
  223.      *
  224.      * @param mixed  $data    Data to restore
  225.      * @param string $class   The expected class to instantiate
  226.      * @param string $format  Format name, present to give the option to normalizers to act differently based on formats
  227.      * @param array  $context Options available to the denormalizer
  228.      */
  229.     private function getDenormalizer($datastring $class, ?string $format, array $context): ?DenormalizerInterface
  230.     {
  231.         if ($this->cachedNormalizers !== $this->normalizers) {
  232.             $this->cachedNormalizers $this->normalizers;
  233.             $this->denormalizerCache $this->normalizerCache = [];
  234.         }
  235.         if (!isset($this->denormalizerCache[$format][$class])) {
  236.             $this->denormalizerCache[$format][$class] = [];
  237.             foreach ($this->normalizers as $k => $normalizer) {
  238.                 if (!$normalizer instanceof DenormalizerInterface) {
  239.                     continue;
  240.                 }
  241.                 if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) {
  242.                     $this->denormalizerCache[$format][$class][$k] = false;
  243.                 } elseif ($normalizer->supportsDenormalization(null$class$format$context)) {
  244.                     $this->denormalizerCache[$format][$class][$k] = true;
  245.                     break;
  246.                 }
  247.             }
  248.         }
  249.         foreach ($this->denormalizerCache[$format][$class] as $k => $cached) {
  250.             $normalizer $this->normalizers[$k];
  251.             if ($cached || $normalizer->supportsDenormalization($data$class$format$context)) {
  252.                 return $normalizer;
  253.             }
  254.         }
  255.         return null;
  256.     }
  257.     /**
  258.      * {@inheritdoc}
  259.      */
  260.     final public function encode($data$format, array $context = [])
  261.     {
  262.         return $this->encoder->encode($data$format$context);
  263.     }
  264.     /**
  265.      * {@inheritdoc}
  266.      */
  267.     final public function decode($data$format, array $context = [])
  268.     {
  269.         return $this->decoder->decode($data$format$context);
  270.     }
  271.     /**
  272.      * {@inheritdoc}
  273.      */
  274.     public function supportsEncoding($format, array $context = [])
  275.     {
  276.         return $this->encoder->supportsEncoding($format$context);
  277.     }
  278.     /**
  279.      * {@inheritdoc}
  280.      */
  281.     public function supportsDecoding($format, array $context = [])
  282.     {
  283.         return $this->decoder->supportsDecoding($format$context);
  284.     }
  285. }