MetadataAwareNameConverter.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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\NameConverter;
  11. use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
  12. use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
  13. /**
  14. * @author Fabien Bourigault <bourigaultfabien@gmail.com>
  15. */
  16. final class MetadataAwareNameConverter implements AdvancedNameConverterInterface
  17. {
  18. private $metadataFactory;
  19. /**
  20. * @var NameConverterInterface|AdvancedNameConverterInterface|null
  21. */
  22. private $fallbackNameConverter;
  23. private static $normalizeCache = [];
  24. private static $denormalizeCache = [];
  25. private static $attributesMetadataCache = [];
  26. public function __construct(ClassMetadataFactoryInterface $metadataFactory, NameConverterInterface $fallbackNameConverter = null)
  27. {
  28. $this->metadataFactory = $metadataFactory;
  29. $this->fallbackNameConverter = $fallbackNameConverter;
  30. }
  31. /**
  32. * {@inheritdoc}
  33. */
  34. public function normalize(string $propertyName, string $class = null, string $format = null, array $context = []): string
  35. {
  36. if (null === $class) {
  37. return $this->normalizeFallback($propertyName, $class, $format, $context);
  38. }
  39. if (!\array_key_exists($class, self::$normalizeCache) || !\array_key_exists($propertyName, self::$normalizeCache[$class])) {
  40. self::$normalizeCache[$class][$propertyName] = $this->getCacheValueForNormalization($propertyName, $class);
  41. }
  42. return self::$normalizeCache[$class][$propertyName] ?? $this->normalizeFallback($propertyName, $class, $format, $context);
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function denormalize(string $propertyName, string $class = null, string $format = null, array $context = []): string
  48. {
  49. if (null === $class) {
  50. return $this->denormalizeFallback($propertyName, $class, $format, $context);
  51. }
  52. $cacheKey = $this->getCacheKey($class, $context);
  53. if (!\array_key_exists($cacheKey, self::$denormalizeCache) || !\array_key_exists($propertyName, self::$denormalizeCache[$cacheKey])) {
  54. self::$denormalizeCache[$cacheKey][$propertyName] = $this->getCacheValueForDenormalization($propertyName, $class, $context);
  55. }
  56. return self::$denormalizeCache[$cacheKey][$propertyName] ?? $this->denormalizeFallback($propertyName, $class, $format, $context);
  57. }
  58. private function getCacheValueForNormalization(string $propertyName, string $class): ?string
  59. {
  60. if (!$this->metadataFactory->hasMetadataFor($class)) {
  61. return null;
  62. }
  63. $attributesMetadata = $this->metadataFactory->getMetadataFor($class)->getAttributesMetadata();
  64. if (!\array_key_exists($propertyName, $attributesMetadata)) {
  65. return null;
  66. }
  67. return $attributesMetadata[$propertyName]->getSerializedName() ?? null;
  68. }
  69. private function normalizeFallback(string $propertyName, string $class = null, string $format = null, array $context = []): string
  70. {
  71. return $this->fallbackNameConverter ? $this->fallbackNameConverter->normalize($propertyName, $class, $format, $context) : $propertyName;
  72. }
  73. private function getCacheValueForDenormalization(string $propertyName, string $class, array $context): ?string
  74. {
  75. $cacheKey = $this->getCacheKey($class, $context);
  76. if (!\array_key_exists($cacheKey, self::$attributesMetadataCache)) {
  77. self::$attributesMetadataCache[$cacheKey] = $this->getCacheValueForAttributesMetadata($class, $context);
  78. }
  79. return self::$attributesMetadataCache[$cacheKey][$propertyName] ?? null;
  80. }
  81. private function denormalizeFallback(string $propertyName, string $class = null, string $format = null, array $context = []): string
  82. {
  83. return $this->fallbackNameConverter ? $this->fallbackNameConverter->denormalize($propertyName, $class, $format, $context) : $propertyName;
  84. }
  85. private function getCacheValueForAttributesMetadata(string $class, array $context): array
  86. {
  87. if (!$this->metadataFactory->hasMetadataFor($class)) {
  88. return [];
  89. }
  90. $classMetadata = $this->metadataFactory->getMetadataFor($class);
  91. $cache = [];
  92. foreach ($classMetadata->getAttributesMetadata() as $name => $metadata) {
  93. if (null === $metadata->getSerializedName()) {
  94. continue;
  95. }
  96. $groups = $metadata->getGroups();
  97. if (!$groups && ($context[AbstractNormalizer::GROUPS] ?? [])) {
  98. continue;
  99. }
  100. if ($groups && !array_intersect($groups, (array) ($context[AbstractNormalizer::GROUPS] ?? []))) {
  101. continue;
  102. }
  103. $cache[$metadata->getSerializedName()] = $name;
  104. }
  105. return $cache;
  106. }
  107. private function getCacheKey(string $class, array $context): string
  108. {
  109. if (isset($context['cache_key'])) {
  110. return $class.'-'.$context['cache_key'];
  111. }
  112. return $class.md5(serialize($context[AbstractNormalizer::GROUPS] ?? []));
  113. }
  114. }