diff --git a/src/Processors/AugmentProperties.php b/src/Processors/AugmentProperties.php index 1c65bdb51..b320eecce 100644 --- a/src/Processors/AugmentProperties.php +++ b/src/Processors/AugmentProperties.php @@ -59,14 +59,43 @@ public function __invoke(Analysis $analysis) if ($property->property === UNDEFINED) { $property->property = $context->property; } - if ($property->type === UNDEFINED) { - $property->type = $context->type; - } if ($property->ref !== UNDEFINED) { continue; } $comment = str_replace("\r\n", "\n", $context->comment); - if (preg_match('/@var\s+(?[^\s]+)([ \t])?(?.+)?$/im', $comment, $varMatches)) { + if ($property->type === UNDEFINED && $context->type !== UNDEFINED) { + if ($context->nullable === true) { + $property->nullable = true; + } + $type = strtolower($context->type); + if (self::$types[$type] ?? false) { + $type = static::$types[strtolower($type)]; + if (is_array($type)) { + if ($property->format === UNDEFINED) { + $property->format = $type[1]; + } + $type = $type[0]; + } + $property->type = $type; + } else { + $key = strtolower($context->fullyQualifiedName($type)); + + if ($property->ref === UNDEFINED && array_key_exists($key, $refs)) { + if ($property->nullable === true) { + $property->oneOf = [ + new Schema([ + '_context' => $property->_context, + 'ref' => $refs[$key] + ]) + ]; + $property->nullable = true; + } else { + $property->ref = $refs[$key]; + } + continue; + } + } + } else if (preg_match('/@var\s+(?[^\s]+)([ \t])?(?.+)?$/im', $comment, $varMatches)) { if ($property->type === UNDEFINED) { preg_match('/^([^\[]+)(.*$)/', trim($varMatches['type']), $typeMatches); $isNullable = $this->isNullable($typeMatches[1]); diff --git a/src/StaticAnalyser.php b/src/StaticAnalyser.php index c15b3a620..f5f791a06 100644 --- a/src/StaticAnalyser.php +++ b/src/StaticAnalyser.php @@ -182,12 +182,13 @@ protected function fromTokens($tokens, $parseContext) } if (in_array($token[0], [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR])) { // Scope - [$type, $token] = $this->extractTypeAndNextToken($tokens, $parseContext); + [$type, $nullable, $token] = $this->extractTypeAndNextToken($tokens, $parseContext); if ($token[0] === T_VARIABLE) { // instance property $propertyContext = new Context( [ 'property' => substr($token[1], 1), 'type' => $type, + 'nullable' => $nullable, 'line' => $line, ], $schemaContext @@ -393,6 +394,7 @@ private function parseUseStatement(&$tokens, &$token, $parseContext) private function extractTypeAndNextToken(array &$tokens, Context $parseContext): array { $type = UNDEFINED; + $nullable = false; $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_STATIC) { @@ -400,6 +402,7 @@ private function extractTypeAndNextToken(array &$tokens, Context $parseContext): } if ($token === '?') { // nullable type + $nullable = true; $token = $this->nextToken($tokens, $parseContext); } @@ -411,6 +414,6 @@ private function extractTypeAndNextToken(array &$tokens, Context $parseContext): $token = $this->nextToken($tokens, $parseContext); } - return [$type, $token]; + return [$type, $nullable, $token]; } } diff --git a/tests/AugmentPropertiesTest.php b/tests/AugmentPropertiesTest.php index 093b312df..65d08e98b 100644 --- a/tests/AugmentPropertiesTest.php +++ b/tests/AugmentPropertiesTest.php @@ -20,8 +20,10 @@ class AugmentPropertiesTest extends OpenApiTestCase { const KEY_PROPERTY = 'property'; + const KEY_REFERENCE = 'ref'; const KEY_EXAMPLE = 'example'; const KEY_DESCRIPTION = 'description'; + const KEY_FORMAT = 'format'; const KEY_TYPE = 'type'; public function testAugmentProperties() @@ -143,6 +145,7 @@ public function testTypedProperties() $annotationTrumpsAll, $undefined, $onlyAnnotated, + $onlyVar, $staticUndefined, $staticString, $staticNullableString, @@ -182,11 +185,11 @@ public function testTypedProperties() ]); $this->assertName($annotationTrumpsNative, [ self::KEY_PROPERTY => UNDEFINED, - self::KEY_TYPE => 'int', + self::KEY_TYPE => 'integer', ]); $this->assertName($annotationTrumpsAll, [ self::KEY_PROPERTY => UNDEFINED, - self::KEY_TYPE => 'int', + self::KEY_TYPE => 'integer', ]); $this->assertName($undefined, [ self::KEY_PROPERTY => UNDEFINED, @@ -194,7 +197,11 @@ public function testTypedProperties() ]); $this->assertName($onlyAnnotated, [ self::KEY_PROPERTY => UNDEFINED, - self::KEY_TYPE => 'int', + self::KEY_TYPE => 'integer', + ]); + $this->assertName($onlyVar, [ + self::KEY_PROPERTY => UNDEFINED, + self::KEY_TYPE => UNDEFINED, ]); $this->assertName($staticUndefined, [ self::KEY_PROPERTY => UNDEFINED, @@ -217,7 +224,7 @@ public function testTypedProperties() ]); $this->assertName($intType, [ self::KEY_PROPERTY => 'intType', - self::KEY_TYPE => 'int', + self::KEY_TYPE => 'integer', ]); $this->assertName($nullableString, [ self::KEY_PROPERTY => 'nullableString', @@ -225,19 +232,21 @@ public function testTypedProperties() ]); $this->assertName($dateTime, [ self::KEY_PROPERTY => 'dateTime', - self::KEY_TYPE => 'DateTime', + self::KEY_TYPE => 'string', + self::KEY_FORMAT => 'date-time', ]); $this->assertName($qualified, [ self::KEY_PROPERTY => 'qualified', - self::KEY_TYPE => 'DateTimeInterface', + self::KEY_TYPE => 'string', + self::KEY_FORMAT => 'date-time', ]); $this->assertName($namespace, [ self::KEY_PROPERTY => 'namespace', - self::KEY_TYPE => 'Foo', + self::KEY_REFERENCE => '#/components/schemas/TypedProperties', ]); $this->assertName($importedNamespace, [ self::KEY_PROPERTY => 'importedNamespace', - self::KEY_TYPE => 'Foo', + self::KEY_REFERENCE => '#/components/schemas/TypedProperties', ]); $this->assertName($nativeTrumpsVar, [ self::KEY_PROPERTY => 'nativeTrumpsVar', @@ -245,11 +254,11 @@ public function testTypedProperties() ]); $this->assertName($annotationTrumpsNative, [ self::KEY_PROPERTY => 'annotationTrumpsNative', - self::KEY_TYPE => 'int', + self::KEY_TYPE => 'integer', ]); $this->assertName($annotationTrumpsAll, [ self::KEY_PROPERTY => 'annotationTrumpsAll', - self::KEY_TYPE => 'int', + self::KEY_TYPE => 'integer', ]); $this->assertName($undefined, [ self::KEY_PROPERTY => 'undefined', @@ -257,7 +266,11 @@ public function testTypedProperties() ]); $this->assertName($onlyAnnotated, [ self::KEY_PROPERTY => 'onlyAnnotated', - self::KEY_TYPE => 'int', + self::KEY_TYPE => 'integer', + ]); + $this->assertName($onlyVar, [ + self::KEY_PROPERTY => 'onlyVar', + self::KEY_TYPE => 'integer', ]); $this->assertName($staticUndefined, [ self::KEY_PROPERTY => 'staticUndefined', diff --git a/tests/Fixtures/TypedProperties.php b/tests/Fixtures/TypedProperties.php index 2651f2ce7..6fece05d3 100644 --- a/tests/Fixtures/TypedProperties.php +++ b/tests/Fixtures/TypedProperties.php @@ -1,7 +1,7 @@