vendor/symfony/serializer/Serializer.php line 154

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