Skip to content

Commit

Permalink
Set nullable true when default value is null
Browse files Browse the repository at this point in the history
When reflection shows that the default parameter of a a property is
null, then the `nullable` parameter is set to `true` for that property.

Currently that needs to be set via the attribute but that is a bit
redundant as the information is already available from reflection

This change only does that for properties and for setter methods.

Right now getter methods that allow a null value to be returned are not
taken into account for setting the `nullable` property.
  • Loading branch information
heiglandreas committed Nov 11, 2024
1 parent c431718 commit 3cb0e0c
Showing 1 changed file with 62 additions and 11 deletions.
73 changes: 62 additions & 11 deletions src/ModelDescriber/Annotations/ReflectionReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Nelmio\ApiDocBundle\Util\SetsContextTrait;
use OpenApi\Annotations as OA;
use OpenApi\Generator;
use ReflectionProperty;

/**
* Read default values of a property from the function or property signature.
Expand Down Expand Up @@ -54,16 +55,26 @@ public function updateProperty(
if ($reflection instanceof \ReflectionMethod) {
$methodDefault = $this->getDefaultFromMethodReflection($reflection);
if (Generator::UNDEFINED !== $methodDefault) {
if (null === $methodDefault) {
$property->nullable = true;

return;
}
$property->default = $methodDefault;

return;
}
}

if ($reflection instanceof \ReflectionProperty) {
$methodDefault = $this->getDefaultFromPropertyReflection($reflection);
if (Generator::UNDEFINED !== $methodDefault) {
$property->default = $methodDefault;
$propertyDefault = $this->getDefaultFromPropertyReflection($reflection);
if (Generator::UNDEFINED !== $propertyDefault) {
if (Generator::UNDEFINED === $property->nullable && null === $propertyDefault) {
$property->nullable = true;

Check warning on line 73 in src/ModelDescriber/Annotations/ReflectionReader.php

View check run for this annotation

Codecov / codecov/patch

src/ModelDescriber/Annotations/ReflectionReader.php#L73

Added line #L73 was not covered by tests

return;

Check warning on line 75 in src/ModelDescriber/Annotations/ReflectionReader.php

View check run for this annotation

Codecov / codecov/patch

src/ModelDescriber/Annotations/ReflectionReader.php#L75

Added line #L75 was not covered by tests
}
$property->default = $propertyDefault;

return;
}
Expand All @@ -77,6 +88,7 @@ public function updateProperty(
if ($parameter->name !== $serializedName) {
continue;
}

if (!$parameter->isDefaultValueAvailable()) {
continue;
}
Expand All @@ -89,7 +101,12 @@ public function updateProperty(
continue;
}

$property->default = $parameter->getDefaultValue();
$default = $parameter->getDefaultValue();
if (Generator::UNDEFINED === $property->nullable && null === $default) {
$property->nullable = true;
}

$property->default = $default;
}
}

Expand Down Expand Up @@ -117,10 +134,6 @@ private function getDefaultFromMethodReflection(\ReflectionMethod $reflection)
return Generator::UNDEFINED;
}

if (null === $param->getDefaultValue()) {
return Generator::UNDEFINED;
}

return $param->getDefaultValue();
}

Expand All @@ -129,17 +142,55 @@ private function getDefaultFromMethodReflection(\ReflectionMethod $reflection)
*/
public function getDefaultFromPropertyReflection(\ReflectionProperty $reflection)
{

$propertyName = $reflection->name;
if (!$reflection->getDeclaringClass()->hasProperty($propertyName)) {
return Generator::UNDEFINED;
}

$defaultValue = $reflection->getDeclaringClass()->getDefaultProperties()[$propertyName] ?? null;
if (PHP_VERSION_ID < 80000) {
return $reflection->getDeclaringClass()->getDefaultProperties()[$propertyName] ?? Generator::UNDEFINED;

Check warning on line 152 in src/ModelDescriber/Annotations/ReflectionReader.php

View check run for this annotation

Codecov / codecov/patch

src/ModelDescriber/Annotations/ReflectionReader.php#L152

Added line #L152 was not covered by tests
}

if (! $reflection->hasDefaultValue()) {
return Generator::UNDEFINED;
}

if (null === $defaultValue) {
if ($this->hasImplicitNullDefaultValue($reflection)) {
return Generator::UNDEFINED;
}

return $defaultValue;
return $reflection->getDefaultValue();
}

/**
* Check whether the default value is an implicit null
*
* An implicit null would be any null value that is not explicitly set and
* contradicts a set DocBlock @ var annotation
*
* So a property without an explicit value but an `@var int` docblock
* would be considered as not having an implicit null default value as
* that contradicts the annotation.
*
* A property without a default value and no docblock would be considered
* to have an explicit NULL default value to be set though.
*/
private function hasImplicitNullDefaultValue(ReflectionProperty $reflection): bool
{
if (null !== $reflection->getDefaultValue()) {
return false;
}

if (false === $reflection->getDocComment()) {
return true;
}

$docComment = $reflection->getDocComment();
if (! preg_match('/@var\s+([\s]+)/', $docComment, $matches)){
return true;
}

return false !== strpos(strtolower($matches[1]), 'null');

Check warning on line 194 in src/ModelDescriber/Annotations/ReflectionReader.php

View check run for this annotation

Codecov / codecov/patch

src/ModelDescriber/Annotations/ReflectionReader.php#L194

Added line #L194 was not covered by tests
}
}

0 comments on commit 3cb0e0c

Please sign in to comment.