vendor/symfony/serializer/Serializer.php line 123

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <[email protected]>
  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 <[email protected]>
  40.  * @author Johannes M. Schmitt <[email protected]>
  41.  * @author Lukas Kahwe Smith <[email protected]>
  42.  * @author Kévin Dunglas <[email protected]>
  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.         if (isset(self::SCALAR_TYPES[$type])) {
  165.             if (!('is_'.$type)($data)) {
  166.                 throw new NotNormalizableValueException(sprintf('Data expected to be of type "%s" ("%s" given).'$typeget_debug_type($data)));
  167.             }
  168.             return $data;
  169.         }
  170.         if (!$this->normalizers) {
  171.             throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
  172.         }
  173.         if ($normalizer $this->getDenormalizer($data$type$format$context)) {
  174.             return $normalizer->denormalize($data$type$format$context);
  175.         }
  176.         throw new NotNormalizableValueException(sprintf('Could not denormalize object of type "%s", no supporting normalizer found.'$type));
  177.     }
  178.     /**
  179.      * {@inheritdoc}
  180.      */
  181.     public function supportsNormalization($datastring $format null, array $context = [])
  182.     {
  183.         return null !== $this->getNormalizer($data$format$context);
  184.     }
  185.     /**
  186.      * {@inheritdoc}
  187.      */
  188.     public function supportsDenormalization($datastring $typestring $format null, array $context = [])
  189.     {
  190.         return isset(self::SCALAR_TYPES[$type]) || null !== $this->getDenormalizer($data$type$format$context);
  191.     }
  192.     /**
  193.      * Returns a matching normalizer.
  194.      *
  195.      * @param mixed  $data    Data to get the serializer for
  196.      * @param string $format  Format name, present to give the option to normalizers to act differently based on formats
  197.      * @param array  $context Options available to the normalizer
  198.      */
  199.     private function getNormalizer($data, ?string $format, array $context): ?NormalizerInterface
  200.     {
  201.         $type = \is_object($data) ? \get_class($data) : 'native-'.\gettype($data);
  202.         if (!isset($this->normalizerCache[$format][$type])) {
  203.             $this->normalizerCache[$format][$type] = [];
  204.             foreach ($this->normalizers as $k => $normalizer) {
  205.                 if (!$normalizer instanceof NormalizerInterface) {
  206.                     continue;
  207.                 }
  208.                 if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) {
  209.                     $this->normalizerCache[$format][$type][$k] = false;
  210.                 } elseif ($normalizer->supportsNormalization($data$format$context)) {
  211.                     $this->normalizerCache[$format][$type][$k] = true;
  212.                     break;
  213.                 }
  214.             }
  215.         }
  216.         foreach ($this->normalizerCache[$format][$type] as $k => $cached) {
  217.             $normalizer $this->normalizers[$k];
  218.             if ($cached || $normalizer->supportsNormalization($data$format$context)) {
  219.                 return $normalizer;
  220.             }
  221.         }
  222.         return null;
  223.     }
  224.     /**
  225.      * Returns a matching denormalizer.
  226.      *
  227.      * @param mixed  $data    Data to restore
  228.      * @param string $class   The expected class to instantiate
  229.      * @param string $format  Format name, present to give the option to normalizers to act differently based on formats
  230.      * @param array  $context Options available to the denormalizer
  231.      */
  232.     private function getDenormalizer($datastring $class, ?string $format, array $context): ?DenormalizerInterface
  233.     {
  234.         if (!isset($this->denormalizerCache[$format][$class])) {
  235.             $this->denormalizerCache[$format][$class] = [];
  236.             foreach ($this->normalizers as $k => $normalizer) {
  237.                 if (!$normalizer instanceof DenormalizerInterface) {
  238.                     continue;
  239.                 }
  240.                 if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) {
  241.                     $this->denormalizerCache[$format][$class][$k] = false;
  242.                 } elseif ($normalizer->supportsDenormalization(null$class$format$context)) {
  243.                     $this->denormalizerCache[$format][$class][$k] = true;
  244.                     break;
  245.                 }
  246.             }
  247.         }
  248.         foreach ($this->denormalizerCache[$format][$class] as $k => $cached) {
  249.             $normalizer $this->normalizers[$k];
  250.             if ($cached || $normalizer->supportsDenormalization($data$class$format$context)) {
  251.                 return $normalizer;
  252.             }
  253.         }
  254.         return null;
  255.     }
  256.     /**
  257.      * {@inheritdoc}
  258.      */
  259.     final public function encode($datastring $format, array $context = [])
  260.     {
  261.         return $this->encoder->encode($data$format$context);
  262.     }
  263.     /**
  264.      * {@inheritdoc}
  265.      */
  266.     final public function decode(string $datastring $format, array $context = [])
  267.     {
  268.         return $this->decoder->decode($data$format$context);
  269.     }
  270.     /**
  271.      * {@inheritdoc}
  272.      */
  273.     public function supportsEncoding(string $format, array $context = [])
  274.     {
  275.         return $this->encoder->supportsEncoding($format$context);
  276.     }
  277.     /**
  278.      * {@inheritdoc}
  279.      */
  280.     public function supportsDecoding(string $format, array $context = [])
  281.     {
  282.         return $this->decoder->supportsDecoding($format$context);
  283.     }
  284. }