vendor/api-platform/core/src/Core/Metadata/Property/Factory/AnnotationPropertyMetadataFactory.php line 116

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the API Platform project.
  4. *
  5. * (c) Kévin Dunglas <dunglas@gmail.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. declare(strict_types=1);
  11. namespace ApiPlatform\Core\Metadata\Property\Factory;
  12. use ApiPlatform\Core\Annotation\ApiProperty;
  13. use ApiPlatform\Core\Exception\PropertyNotFoundException;
  14. use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
  15. use ApiPlatform\Util\Reflection;
  16. use Doctrine\Common\Annotations\Reader;
  17. /**
  18. * Creates a property metadata from {@see ApiProperty} annotations.
  19. *
  20. * @author Kévin Dunglas <dunglas@gmail.com>
  21. */
  22. final class AnnotationPropertyMetadataFactory implements PropertyMetadataFactoryInterface
  23. {
  24. private $reader;
  25. private $decorated;
  26. public function __construct(Reader $reader = null, PropertyMetadataFactoryInterface $decorated = null)
  27. {
  28. $this->reader = $reader;
  29. $this->decorated = $decorated;
  30. }
  31. /**
  32. * {@inheritdoc}
  33. */
  34. public function create(string $resourceClass, string $property, array $options = []): PropertyMetadata
  35. {
  36. if (null === ($options['deprecate'] ?? null)) {
  37. trigger_deprecation('api-platform/core', '2.7', sprintf('Decorating the legacy %s is deprecated, use %s instead.', PropertyMetadataFactoryInterface::class, \ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface::class));
  38. }
  39. $parentPropertyMetadata = null;
  40. if ($this->decorated) {
  41. try {
  42. $parentPropertyMetadata = $this->decorated->create($resourceClass, $property, $options);
  43. } catch (PropertyNotFoundException $propertyNotFoundException) {
  44. // Ignore not found exception from decorated factories
  45. }
  46. }
  47. try {
  48. $reflectionClass = new \ReflectionClass($resourceClass);
  49. } catch (\ReflectionException $reflectionException) {
  50. return $this->handleNotFound($parentPropertyMetadata, $resourceClass, $property);
  51. }
  52. if ($reflectionClass->hasProperty($property)) {
  53. $annotation = null;
  54. $reflectionProperty = $reflectionClass->getProperty($property);
  55. if (\PHP_VERSION_ID >= 80000 && $attributes = $reflectionProperty->getAttributes(ApiProperty::class)) {
  56. $annotation = $attributes[0]->newInstance();
  57. } elseif (null !== $this->reader) {
  58. $annotation = $this->reader->getPropertyAnnotation($reflectionProperty, ApiProperty::class);
  59. }
  60. if ($annotation instanceof ApiProperty) {
  61. return $this->createMetadata($annotation, $parentPropertyMetadata);
  62. }
  63. }
  64. foreach (array_merge(Reflection::ACCESSOR_PREFIXES, Reflection::MUTATOR_PREFIXES) as $prefix) {
  65. $methodName = $prefix.ucfirst($property);
  66. if (!$reflectionClass->hasMethod($methodName)) {
  67. continue;
  68. }
  69. $reflectionMethod = $reflectionClass->getMethod($methodName);
  70. if (!$reflectionMethod->isPublic()) {
  71. continue;
  72. }
  73. $annotation = null;
  74. if (\PHP_VERSION_ID >= 80000 && $attributes = $reflectionMethod->getAttributes(ApiProperty::class)) {
  75. $annotation = $attributes[0]->newInstance();
  76. } elseif (null !== $this->reader) {
  77. $annotation = $this->reader->getMethodAnnotation($reflectionMethod, ApiProperty::class);
  78. }
  79. if ($annotation instanceof ApiProperty) {
  80. return $this->createMetadata($annotation, $parentPropertyMetadata);
  81. }
  82. }
  83. return $this->handleNotFound($parentPropertyMetadata, $resourceClass, $property);
  84. }
  85. /**
  86. * Returns the metadata from the decorated factory if available or throws an exception.
  87. *
  88. * @throws PropertyNotFoundException
  89. */
  90. private function handleNotFound(?PropertyMetadata $parentPropertyMetadata, string $resourceClass, string $property): PropertyMetadata
  91. {
  92. if (null !== $parentPropertyMetadata) {
  93. return $parentPropertyMetadata;
  94. }
  95. throw new PropertyNotFoundException(sprintf('Property "%s" of class "%s" not found.', $property, $resourceClass));
  96. }
  97. private function createMetadata(ApiProperty $annotation, PropertyMetadata $parentPropertyMetadata = null): PropertyMetadata
  98. {
  99. if (null === $parentPropertyMetadata) {
  100. return new PropertyMetadata(
  101. null,
  102. $annotation->description,
  103. $annotation->readable,
  104. $annotation->writable,
  105. $annotation->readableLink,
  106. $annotation->writableLink,
  107. $annotation->required,
  108. $annotation->identifier,
  109. $annotation->iri,
  110. null,
  111. $annotation->attributes,
  112. null,
  113. null,
  114. $annotation->default,
  115. $annotation->example
  116. );
  117. }
  118. $propertyMetadata = $parentPropertyMetadata;
  119. foreach ([['get', 'description'], ['is', 'readable'], ['is', 'writable'], ['is', 'readableLink'], ['is', 'writableLink'], ['is', 'required'], ['get', 'iri'], ['is', 'identifier'], ['get', 'attributes'], ['get', 'default'], ['get', 'example']] as $property) {
  120. if (null !== $value = $annotation->{$property[1]}) {
  121. $propertyMetadata = $this->createWith($propertyMetadata, $property, $value);
  122. }
  123. }
  124. return $propertyMetadata;
  125. }
  126. private function createWith(PropertyMetadata $propertyMetadata, array $property, $value): PropertyMetadata
  127. {
  128. $wither = 'with'.ucfirst($property[1]);
  129. return $propertyMetadata->{$wither}($value);
  130. }
  131. }