Skip to content

Commit

Permalink
fix: set nullable true when default value is null (#2390)
Browse files Browse the repository at this point in the history
## Description

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.

This resets the state prior to v4.25.0 and is related to #2330

This is a refactor of #2377

## What type of PR is this? (check all applicable)
- [x] Bug Fix
- [ ] Feature
- [x] Refactor
- [ ] Deprecation
- [ ] Breaking Change
- [ ] Documentation Update
- [ ] CI

## Checklist
- [ ] I have made corresponding changes to the documentation (`docs/`)
- [ ] I have made corresponding changes to the changelog
(`CHANGELOG.md`)
  • Loading branch information
heiglandreas authored Jan 6, 2025
1 parent e9be0ec commit 8e56941
Showing 1 changed file with 60 additions and 11 deletions.
71 changes: 60 additions & 11 deletions src/ModelDescriber/Annotations/ReflectionReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,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;

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

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

if (!$parameter->isDefaultValueAvailable()) {
continue;
}
Expand All @@ -89,7 +100,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 +133,6 @@ private function getDefaultFromMethodReflection(\ReflectionMethod $reflection)
return Generator::UNDEFINED;
}

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

return $param->getDefaultValue();
}

Expand All @@ -134,12 +146,49 @@ public function getDefaultFromPropertyReflection(\ReflectionProperty $reflection
return Generator::UNDEFINED;
}

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

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');
}
}

0 comments on commit 8e56941

Please sign in to comment.