Merge remote-tracking branch 'origin/3.x' into HEAD

This commit is contained in:
Guilhem Niot 2020-07-12 15:04:20 +02:00
commit ad4e5551f5
9 changed files with 76 additions and 31 deletions

View File

@ -8,8 +8,8 @@ CHANGELOG
3.7.0 3.7.0
----- -----
* remove pattern added from the Expression Violation message.
* Added `@SerializedName` annotation support and name converters when using Symfony >= 4.2. * Added `@SerializedName` annotation support and name converters when using Symfony >= 4.2.
* remove pattern added from the Expression Violation message.
3.3.0 3.3.0
----- -----

View File

@ -43,15 +43,15 @@ class AnnotationsReader
$this->symfonyConstraintAnnotationReader->setSchema($schema); $this->symfonyConstraintAnnotationReader->setSchema($schema);
} }
public function getPropertyName(\ReflectionProperty $reflectionProperty, string $default): string public function getPropertyName($reflection, string $default): string
{ {
return $this->openApiAnnotationsReader->getPropertyName($reflectionProperty, $default); return $this->openApiAnnotationsReader->getPropertyName($reflection, $default);
} }
public function updateProperty(\ReflectionProperty $reflectionProperty, OA\Property $property, array $serializationGroups = null): void public function updateProperty($reflection, OA\Property $property, array $serializationGroups = null): void
{ {
$this->openApiAnnotationsReader->updateProperty($reflectionProperty, $property, $serializationGroups); $this->openApiAnnotationsReader->updateProperty($reflection, $property, $serializationGroups);
$this->phpDocReader->updateProperty($reflectionProperty, $property); $this->phpDocReader->updateProperty($reflection, $property);
$this->symfonyConstraintAnnotationReader->updateProperty($reflectionProperty, $property); $this->symfonyConstraintAnnotationReader->updateProperty($reflection, $property);
} }
} }

View File

@ -50,29 +50,33 @@ class OpenApiAnnotationsReader
$schema->mergeProperties($oaSchema); $schema->mergeProperties($oaSchema);
} }
public function getPropertyName(\ReflectionProperty $reflectionProperty, string $default): string public function getPropertyName($reflection, string $default): string
{ {
/** @var OA\Property $oaProperty */ /** @var OA\Property $oaProperty */
if (!$oaProperty = $this->annotationsReader->getPropertyAnnotation($reflectionProperty, OA\Property::class)) { if ($reflection instanceof \ReflectionProperty && !$oaProperty = $this->annotationsReader->getPropertyAnnotation($reflection, OA\Property::class)) {
return $default;
} elseif ($reflection instanceof \ReflectionMethod && !$oaProperty = $this->annotationsReader->getMethodAnnotation($reflection, OA\Property::class)) {
return $default; return $default;
} }
return OA\UNDEFINED !== $oaProperty->property ? $oaProperty->property : $default; return OA\UNDEFINED !== $oaProperty->property ? $oaProperty->property : $default;
} }
public function updateProperty(\ReflectionProperty $reflectionProperty, OA\Property $property, array $serializationGroups = null): void public function updateProperty($reflection, OA\Property $property, array $serializationGroups = null): void
{ {
// In order to have nicer errors // In order to have nicer errors
$declaringClass = $reflectionProperty->getDeclaringClass(); $declaringClass = $reflection->getDeclaringClass();
Analyser::$context = new Context([ Analyser::$context = new Context([
'namespace' => $declaringClass->getNamespaceName(), 'namespace' => $declaringClass->getNamespaceName(),
'class' => $declaringClass->getShortName(), 'class' => $declaringClass->getShortName(),
'property' => $reflectionProperty->name, 'property' => $reflection->name,
'filename' => $declaringClass->getFileName(), 'filename' => $declaringClass->getFileName(),
]); ]);
/** @var OA\Property $oaProperty */ /** @var OA\Property $oaProperty */
if (!$oaProperty = $this->annotationsReader->getPropertyAnnotation($reflectionProperty, OA\Property::class)) { if ($reflection instanceof \ReflectionProperty && !$oaProperty = $this->annotationsReader->getPropertyAnnotation($reflection, OA\Property::class)) {
return;
} elseif ($reflection instanceof \ReflectionMethod && !$oaProperty = $this->annotationsReader->getMethodAnnotation($reflection, OA\Property::class)) {
return; return;
} }
Analyser::$context = null; Analyser::$context = null;

View File

@ -32,10 +32,10 @@ class PropertyPhpDocReader
/** /**
* Update the Swagger information with information from the DocBlock comment. * Update the Swagger information with information from the DocBlock comment.
*/ */
public function updateProperty(\ReflectionProperty $reflectionProperty, OA\Property $property): void public function updateProperty($reflection, OA\Property $property): void
{ {
try { try {
$docBlock = $this->docBlockFactory->create($reflectionProperty); $docBlock = $this->docBlockFactory->create($reflection);
} catch (\Exception $e) { } catch (\Exception $e) {
// ignore // ignore
return; return;

View File

@ -38,9 +38,13 @@ class SymfonyConstraintAnnotationReader
/** /**
* Update the given property and schema with defined Symfony constraints. * Update the given property and schema with defined Symfony constraints.
*/ */
public function updateProperty(\ReflectionProperty $reflectionProperty, OA\Property $property): void public function updateProperty($reflection, OA\Property $property): void
{ {
$annotations = $this->annotationsReader->getPropertyAnnotations($reflectionProperty); if ($reflection instanceof \ReflectionProperty) {
$annotations = $this->annotationsReader->getPropertyAnnotations($reflection);
} else {
$annotations = $this->annotationsReader->getMethodAnnotations($reflection);
}
foreach ($annotations as $annotation) { foreach ($annotations as $annotation) {
if ($annotation instanceof Assert\NotBlank || $annotation instanceof Assert\NotNull) { if ($annotation instanceof Assert\NotBlank || $annotation instanceof Assert\NotNull) {
@ -67,7 +71,7 @@ class SymfonyConstraintAnnotationReader
$property->minItems = (int) $annotation->min; $property->minItems = (int) $annotation->min;
$property->maxItems = (int) $annotation->max; $property->maxItems = (int) $annotation->max;
} elseif ($annotation instanceof Assert\Choice) { } elseif ($annotation instanceof Assert\Choice) {
$values = $annotation->callback ? call_user_func(is_array($annotation->callback) ? $annotation->callback : [$reflectionProperty->class, $annotation->callback]) : $annotation->choices; $values = $annotation->callback ? call_user_func(is_array($annotation->callback) ? $annotation->callback : [$reflection->class, $annotation->callback]) : $annotation->choices;
$property->enum = array_values($values); $property->enum = array_values($values);
} elseif ($annotation instanceof Assert\Range) { } elseif ($annotation instanceof Assert\Range) {
$property->minimum = (int) $annotation->min; $property->minimum = (int) $annotation->min;

View File

@ -66,8 +66,9 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
$context['serializer_groups'] = array_filter($model->getGroups(), 'is_string'); $context['serializer_groups'] = array_filter($model->getGroups(), 'is_string');
} }
$reflClass = new \ReflectionClass($class);
$annotationsReader = new AnnotationsReader($this->doctrineReader, $this->modelRegistry, $this->mediaTypes); $annotationsReader = new AnnotationsReader($this->doctrineReader, $this->modelRegistry, $this->mediaTypes);
$annotationsReader->updateDefinition(new \ReflectionClass($class), $schema); $annotationsReader->updateDefinition($reflClass, $schema);
$propertyInfoProperties = $this->propertyInfo->getProperties($class, $context); $propertyInfoProperties = $this->propertyInfo->getProperties($class, $context);
if (null === $propertyInfoProperties) { if (null === $propertyInfoProperties) {
@ -77,19 +78,22 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
foreach ($propertyInfoProperties as $propertyName) { foreach ($propertyInfoProperties as $propertyName) {
$serializedName = null !== $this->nameConverter ? $this->nameConverter->normalize($propertyName, $class, null, null !== $model->getGroups() ? ['groups' => $model->getGroups()] : []) : $propertyName; $serializedName = null !== $this->nameConverter ? $this->nameConverter->normalize($propertyName, $class, null, null !== $model->getGroups() ? ['groups' => $model->getGroups()] : []) : $propertyName;
// read property options from OpenApi Property annotation if it exists $reflections = $this->getReflections($reflClass, $propertyName);
if (property_exists($class, $propertyName)) {
$reflectionProperty = new \ReflectionProperty($class, $propertyName);
$property = Util::getProperty($schema, $annotationsReader->getPropertyName($reflectionProperty, $serializedName));
$groups = $model->getGroups(); // Check if a custom name is set
if (isset($groups[$propertyName]) && is_array($groups[$propertyName])) { foreach ($reflections as $reflection) {
$groups = $model->getGroups()[$propertyName]; $serializedName = $annotationsReader->getPropertyName($reflection, $serializedName);
} }
$annotationsReader->updateProperty($reflectionProperty, $property, $groups); $property = Util::getProperty($schema, $annotationsReader->getPropertyName($reflection, $serializedName));
} else {
$property = Util::getProperty($schema, $serializedName); // Interpret additional options
$groups = $model->getGroups();
if (isset($groups[$propertyName]) && is_array($groups[$propertyName])) {
$groups = $model->getGroups()[$propertyName];
}
foreach ($reflections as $reflection) {
$annotationsReader->updateProperty($reflection, $property, $groups);
} }
// If type manually defined // If type manually defined
@ -106,6 +110,34 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
} }
} }
/**
* @return \ReflectionProperty[]|\ReflectionMethod[]
*/
private function getReflections(\ReflectionClass $reflClass, string $propertyName): array
{
$reflections = [];
if ($reflClass->hasProperty($propertyName)) {
$reflections[] = $reflClass->getProperty($propertyName);
}
$camelProp = $this->camelize($propertyName);
foreach (['', 'get', 'is', 'has', 'can', 'add', 'remove', 'set'] as $prefix) {
if ($reflClass->hasMethod($prefix.$camelProp)) {
$reflections[] = $reflClass->getMethod($prefix.$camelProp);
}
}
return $reflections;
}
/**
* Camelizes a given string.
*/
private function camelize(string $string): string
{
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
}
/** /**
* @param Type[] $types * @param Type[] $types
*/ */

View File

@ -103,6 +103,8 @@ class SymfonyConstraints
private $propertyLessThanOrEqual; private $propertyLessThanOrEqual;
/** /**
* @Assert\Count(min="0", max="10")
*
* @param int $propertyNotBlank * @param int $propertyNotBlank
*/ */
public function setPropertyNotBlank(int $propertyNotBlank): void public function setPropertyNotBlank(int $propertyNotBlank): void

View File

@ -21,7 +21,7 @@ class User
/** /**
* @var int * @var int
* *
* @OA\Property(description = "User id", readOnly = true, title = "userid", example=1, default = null) * @OA\Property(description = "User id", readOnly = true, title = "userid", default = null)
*/ */
private $id; private $id;
@ -102,6 +102,7 @@ class User
/** /**
* @param int $id * @param int $id
* @OA\Property(example=1)
*/ */
public function setId(int $id) public function setId(int $id)
{ {

View File

@ -342,6 +342,8 @@ class FunctionalTest extends WebTestCase
'properties' => [ 'properties' => [
'propertyNotBlank' => [ 'propertyNotBlank' => [
'type' => 'integer', 'type' => 'integer',
'maxItems' => '10',
'minItems' => '0',
], ],
'propertyNotNull' => [ 'propertyNotNull' => [
'type' => 'integer', 'type' => 'integer',