Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions src/Processors/Concerns/DocblockTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,38 @@ public function parseVarLine(?string $docblock): array
$comment = str_replace("\r\n", "\n", (string) $docblock);
$comment = preg_replace('/\*\/[ \t]*$/', '', $comment); // strip '*/'

preg_match('/@var\s+(?<type>[^\s]+)([ \t])?(?<description>.+)?+$/im', (string) $comment, $matches);
if (!preg_match('/@var\s+(.+)$/im', (string) $comment, $matches)) {
return ['type' => null, 'description' => null];
}

$rest = $matches[1];
$type = '';
$depth = 0;
$len = strlen($rest);
$pos = 0;

while ($pos < $len) {
$char = $rest[$pos];
if ('<' === $char || '{' === $char) {
++$depth;
$type .= $char;
} elseif ('>' === $char || '}' === $char) {
--$depth;
$type .= $char;
} elseif (0 === $depth && ctype_space($char)) {
break;
} else {
$type .= $char;
}
++$pos;
}

$result = array_merge(
['type' => null, 'description' => null],
array_filter($matches, static fn ($key): bool => in_array($key, ['type', 'description']), ARRAY_FILTER_USE_KEY)
);
$description = trim(substr($rest, $pos));

return array_map(static fn (?string $value): ?string => null !== $value ? trim($value) : null, $result);
return [
'type' => '' !== $type ? trim($type) : null,
'description' => '' !== $description ? $description : null,
];
}

/**
Expand Down
77 changes: 77 additions & 0 deletions tests/Fixtures/ComplexVarTypes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Tests\Fixtures;

use OpenApi\Attributes as OAT;

#[OAT\Schema]
class ComplexVarTypes
{
/**
* An associative array with string values.
*
* @var array<string, string>
*/
#[OAT\Property]
public array $map;

/**
* A map from int to user objects.
*
* @var array<int, User>
*/
#[OAT\Property]
public array $userMap;

/** @var array<string, string> Inline generic with description */
#[OAT\Property]
public array $inlineGenericDesc;

/**
* A map using namespaced class.
*
* @var array<int, \OpenApi\Tests\Fixtures\Customer>
*/
#[OAT\Property]
public array $namespacedMap;

/**
* List of integer IDs.
*
* @var int[]
*/
#[OAT\Property]
public array $intList;

/**
* Either an array or a string list.
*
* @var array|string[]
*/
#[OAT\Property]
public $arrayOrStringList;

/**
* Nullable map of strings.
*
* @var array<string, string>|null
*/
#[OAT\Property]
public ?array $nullableMap;

/**
* A collection of users or a single user array.
*
* @var array<int, User>|User[]
*/
#[OAT\Property]
public array $mixedUserList;

/** @var array<string, string>|null Nullable inline with description */
#[OAT\Property]
public ?array $nullableInlineDesc;
}
25 changes: 25 additions & 0 deletions tests/Processors/AugmentPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,31 @@ public function testTypedProperties(): void
);
}

public function testComplexVarTypeDescription(): void
{
$analysis = $this->analysisFromFixtures([
'ComplexVarTypes.php',
], $this->processorPipeline([
new MergeIntoOpenApi(),
new MergeIntoComponents(),
new AugmentSchemas(),
new AugmentProperties(),
]));

[$map, $userMap, $inlineGenericDesc, $namespacedMap, $intList, $arrayOrStringList, $nullableMap, $mixedUserList, $nullableInlineDesc] = $analysis->openapi->components->schemas[0]->properties;

// Description should come from docblock text, not the generic type fragment
$this->assertSame('An associative array with string values.', $map->description);
$this->assertSame('A map from int to user objects.', $userMap->description);
$this->assertSame('Inline generic with description', $inlineGenericDesc->description);
$this->assertSame('A map using namespaced class.', $namespacedMap->description);
$this->assertSame('List of integer IDs.', $intList->description);
$this->assertSame('Either an array or a string list.', $arrayOrStringList->description);
$this->assertSame('Nullable map of strings.', $nullableMap->description);
$this->assertSame('A collection of users or a single user array.', $mixedUserList->description);
$this->assertSame('Nullable inline with description', $nullableInlineDesc->description);
}

protected function assertName(OA\Property $property, array $expectedValues): void
{
foreach ($expectedValues as $key => $val) {
Expand Down
Loading