AnnotationLoader.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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\Mapping\Loader;
  11. use Doctrine\Common\Annotations\Reader;
  12. use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
  13. use Symfony\Component\Serializer\Annotation\Groups;
  14. use Symfony\Component\Serializer\Annotation\Ignore;
  15. use Symfony\Component\Serializer\Annotation\MaxDepth;
  16. use Symfony\Component\Serializer\Annotation\SerializedName;
  17. use Symfony\Component\Serializer\Exception\MappingException;
  18. use Symfony\Component\Serializer\Mapping\AttributeMetadata;
  19. use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
  20. use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
  21. /**
  22. * Loader for Doctrine annotations and PHP 8 attributes.
  23. *
  24. * @author Kévin Dunglas <dunglas@gmail.com>
  25. * @author Alexander M. Turek <me@derrabus.de>
  26. */
  27. class AnnotationLoader implements LoaderInterface
  28. {
  29. private const KNOWN_ANNOTATIONS = [
  30. DiscriminatorMap::class => true,
  31. Groups::class => true,
  32. Ignore::class => true,
  33. MaxDepth::class => true,
  34. SerializedName::class => true,
  35. ];
  36. private $reader;
  37. public function __construct(Reader $reader = null)
  38. {
  39. $this->reader = $reader;
  40. }
  41. /**
  42. * {@inheritdoc}
  43. */
  44. public function loadClassMetadata(ClassMetadataInterface $classMetadata)
  45. {
  46. $reflectionClass = $classMetadata->getReflectionClass();
  47. $className = $reflectionClass->name;
  48. $loaded = false;
  49. $attributesMetadata = $classMetadata->getAttributesMetadata();
  50. foreach ($this->loadAnnotations($reflectionClass) as $annotation) {
  51. if ($annotation instanceof DiscriminatorMap) {
  52. $classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping(
  53. $annotation->getTypeProperty(),
  54. $annotation->getMapping()
  55. ));
  56. }
  57. }
  58. foreach ($reflectionClass->getProperties() as $property) {
  59. if (!isset($attributesMetadata[$property->name])) {
  60. $attributesMetadata[$property->name] = new AttributeMetadata($property->name);
  61. $classMetadata->addAttributeMetadata($attributesMetadata[$property->name]);
  62. }
  63. if ($property->getDeclaringClass()->name === $className) {
  64. foreach ($this->loadAnnotations($property) as $annotation) {
  65. if ($annotation instanceof Groups) {
  66. foreach ($annotation->getGroups() as $group) {
  67. $attributesMetadata[$property->name]->addGroup($group);
  68. }
  69. } elseif ($annotation instanceof MaxDepth) {
  70. $attributesMetadata[$property->name]->setMaxDepth($annotation->getMaxDepth());
  71. } elseif ($annotation instanceof SerializedName) {
  72. $attributesMetadata[$property->name]->setSerializedName($annotation->getSerializedName());
  73. } elseif ($annotation instanceof Ignore) {
  74. $attributesMetadata[$property->name]->setIgnore(true);
  75. }
  76. $loaded = true;
  77. }
  78. }
  79. }
  80. foreach ($reflectionClass->getMethods() as $method) {
  81. if ($method->getDeclaringClass()->name !== $className) {
  82. continue;
  83. }
  84. $accessorOrMutator = preg_match('/^(get|is|has|set)(.+)$/i', $method->name, $matches);
  85. if ($accessorOrMutator) {
  86. $attributeName = lcfirst($matches[2]);
  87. if (isset($attributesMetadata[$attributeName])) {
  88. $attributeMetadata = $attributesMetadata[$attributeName];
  89. } else {
  90. $attributesMetadata[$attributeName] = $attributeMetadata = new AttributeMetadata($attributeName);
  91. $classMetadata->addAttributeMetadata($attributeMetadata);
  92. }
  93. }
  94. foreach ($this->loadAnnotations($method) as $annotation) {
  95. if ($annotation instanceof Groups) {
  96. if (!$accessorOrMutator) {
  97. throw new MappingException(sprintf('Groups on "%s::%s()" cannot be added. Groups can only be added on methods beginning with "get", "is", "has" or "set".', $className, $method->name));
  98. }
  99. foreach ($annotation->getGroups() as $group) {
  100. $attributeMetadata->addGroup($group);
  101. }
  102. } elseif ($annotation instanceof MaxDepth) {
  103. if (!$accessorOrMutator) {
  104. throw new MappingException(sprintf('MaxDepth on "%s::%s()" cannot be added. MaxDepth can only be added on methods beginning with "get", "is", "has" or "set".', $className, $method->name));
  105. }
  106. $attributeMetadata->setMaxDepth($annotation->getMaxDepth());
  107. } elseif ($annotation instanceof SerializedName) {
  108. if (!$accessorOrMutator) {
  109. throw new MappingException(sprintf('SerializedName on "%s::%s()" cannot be added. SerializedName can only be added on methods beginning with "get", "is", "has" or "set".', $className, $method->name));
  110. }
  111. $attributeMetadata->setSerializedName($annotation->getSerializedName());
  112. } elseif ($annotation instanceof Ignore) {
  113. $attributeMetadata->setIgnore(true);
  114. }
  115. $loaded = true;
  116. }
  117. }
  118. return $loaded;
  119. }
  120. /**
  121. * @param \ReflectionClass|\ReflectionMethod|\ReflectionProperty $reflector
  122. */
  123. public function loadAnnotations(object $reflector): iterable
  124. {
  125. if (\PHP_VERSION_ID >= 80000) {
  126. foreach ($reflector->getAttributes() as $attribute) {
  127. if (self::KNOWN_ANNOTATIONS[$attribute->getName()] ?? false) {
  128. yield $attribute->newInstance();
  129. }
  130. }
  131. }
  132. if (null === $this->reader) {
  133. return;
  134. }
  135. if ($reflector instanceof \ReflectionClass) {
  136. yield from $this->reader->getClassAnnotations($reflector);
  137. }
  138. if ($reflector instanceof \ReflectionMethod) {
  139. yield from $this->reader->getMethodAnnotations($reflector);
  140. }
  141. if ($reflector instanceof \ReflectionProperty) {
  142. yield from $this->reader->getPropertyAnnotations($reflector);
  143. }
  144. }
  145. }