Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add enum support #1050

Merged
merged 3 commits into from
Jan 9, 2022
Merged
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
13 changes: 13 additions & 0 deletions Examples/openapi-spec-attributes/StockLevel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types=1);

namespace OpenApi\Examples\OpenapiSpecAttributes;

use OpenApi\Attributes\Schema;

#[Schema()]
enum StockLevel
{
case AVAILABLE;
case SOLD_OUT;
case BACK_ORDER;
}
6 changes: 6 additions & 0 deletions Examples/openapi-spec-attributes/openapi-spec-attributes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ components:
owner:
$ref: '#/components/schemas/user'
type: object
StockLevel:
type: string
enum:
- AVAILABLE
- SOLD_OUT
- BACK_ORDER
user:
properties:
username:
Expand Down
13 changes: 13 additions & 0 deletions Examples/using-refs/StockLevel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types=1);

namespace OpenApi\Examples\UsingRefs;

/**
* @OA\Schema()
*/
enum StockLevel: int
{
case AVAILABLE = 1;
case SOLD_OUT = 2;
case BACK_ORDER = 3;
}
6 changes: 6 additions & 0 deletions Examples/using-refs/using-refs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ components:
$ref: '#/components/schemas/product_status'
-
$ref: '#/components/schemas/Model'
StockLevel:
type: int
enum:
- AVAILABLE
- SOLD_OUT
- BACK_ORDER
product_status:
description: 'The status of a product'
type: string
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
"config": {
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"composer/package-versions-deprecated": true
}
},
"minimum-stability": "stable",
"extra": {
Expand Down
2 changes: 1 addition & 1 deletion src/Analysers/ReflectionAnalyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ protected function analyzeFqdn(string $fqdn, Analysis $analysis, array $details)
}

$rc = new \ReflectionClass($fqdn);
$contextType = $rc->isInterface() ? 'interface' : ($rc->isTrait() ? 'trait' : 'class');
$contextType = $rc->isInterface() ? 'interface' : ($rc->isTrait() ? 'trait' : ((method_exists($rc, 'isEnum') && $rc->isEnum()) ? 'enum' : 'class'));
$context = new Context([
$contextType => $rc->getShortName(),
'namespace' => $rc->getNamespaceName() ?: Generator::UNDEFINED,
Expand Down
40 changes: 40 additions & 0 deletions src/Analysers/TokenAnalyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
$classDefinition = false;
$interfaceDefinition = false;
$traitDefinition = false;
$enumDefinition = false;
$comment = false;

$line = 0;
Expand Down Expand Up @@ -142,6 +143,7 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis

$interfaceDefinition = false;
$traitDefinition = false;
$enumDefinition = false;

$schemaContext = new Context(['class' => $token[1], 'line' => $token[2]], $parseContext);
if ($classDefinition) {
Expand Down Expand Up @@ -180,6 +182,7 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
if ($token[0] === T_INTERFACE) { // Doc-comment before an interface?
$classDefinition = false;
$traitDefinition = false;
$enumDefinition = false;

$token = $this->nextToken($tokens, $parseContext);

Expand Down Expand Up @@ -220,6 +223,7 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
if ($token[0] === T_TRAIT) {
$classDefinition = false;
$interfaceDefinition = false;
$enumDefinition = false;

$token = $this->nextToken($tokens, $parseContext);

Expand Down Expand Up @@ -249,6 +253,39 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
// @todo detect end-of-trait and reset $schemaContext
}

if (defined('T_ENUM') && $token[0] === T_ENUM) {
$classDefinition = false;
$interfaceDefinition = false;
$traitDefinition = false;

$token = $this->nextToken($tokens, $parseContext);

if (!is_array($token)) {
// PHP 8 named argument
continue;
}

$schemaContext = new Context(['enum' => $token[1], 'line' => $token[2]], $parseContext);
if ($enumDefinition) {
$analysis->addEnumDefinition($enumDefinition);
}
$enumDefinition = [
'enum' => $token[1],
'properties' => [],
'methods' => [],
'context' => $schemaContext,
];

if ($comment) {
$schemaContext->line = $line;
$this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext);
$comment = false;
continue;
}

// @todo detect end-of-trait and reset $schemaContext
}

if ($token[0] === T_STATIC) {
$token = $this->nextToken($tokens, $parseContext);
if ($token[0] === T_VARIABLE) {
Expand Down Expand Up @@ -416,6 +453,9 @@ protected function fromTokens(array $tokens, Context $parseContext): Analysis
if ($traitDefinition) {
$analysis->addTraitDefinition($traitDefinition);
}
if ($enumDefinition) {
$analysis->addEnumDefinition($enumDefinition);
}

return $analysis;
}
Expand Down
44 changes: 29 additions & 15 deletions src/Analysers/TokenScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ protected function scanTokens(array $tokens): array
$lastToken = null;
$stack = [];

$initUnit = function ($uses) {
return [
'uses' => $uses,
'interfaces' => [],
'traits' => [],
'enums' => [],
'methods' => [],
'properties' => [],
];
};

while (false !== ($token = $this->nextToken($tokens))) {
if (!is_array($token)) {
switch ($token) {
Expand Down Expand Up @@ -100,7 +111,7 @@ protected function scanTokens(array $tokens): array
$isInterface = false;
$currentName = $namespace . '\\' . $token[1];
$unitLevel = count($stack);
$units[$currentName] = ['uses' => $uses, 'interfaces' => [], 'traits' => [], 'methods' => [], 'properties' => []];
$units[$currentName] = $initUnit($uses);
break;

case T_INTERFACE:
Expand All @@ -112,20 +123,7 @@ protected function scanTokens(array $tokens): array
$token = $this->nextToken($tokens);
$currentName = $namespace . '\\' . $token[1];
$unitLevel = count($stack);
$units[$currentName] = ['uses' => $uses, 'interfaces' => [], 'traits' => [], 'methods' => [], 'properties' => []];
break;

case T_TRAIT:
if ($currentName) {
break;
}

$isInterface = false;
$token = $this->nextToken($tokens);
$currentName = $namespace . '\\' . $token[1];
$unitLevel = count($stack);
$this->skipTo($tokens, '{', true);
$units[$currentName] = ['uses' => $uses, 'interfaces' => [], 'traits' => [], 'methods' => [], 'properties' => []];
$units[$currentName] = $initUnit($uses);
break;

case T_EXTENDS:
Expand Down Expand Up @@ -168,6 +166,22 @@ protected function scanTokens(array $tokens): array
$units[$currentName]['properties'][] = substr($token[1], 1);
}
break;
default:
// handle trait here too to avoid duplication
if (T_TRAIT === $token[0] || (defined('T_ENUM') && T_ENUM === $token[0])) {
if ($currentName) {
break;
}

$isInterface = false;
$token = $this->nextToken($tokens);
$currentName = $namespace . '\\' . $token[1];
$unitLevel = count($stack);
$this->skipTo($tokens, '{', true);
$units[$currentName] = $initUnit($uses);
}
break;

}
$lastToken = $token;
}
Expand Down
17 changes: 15 additions & 2 deletions src/Analysis.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ class Analysis
*/
public $classes = [];

/**
* Interface definitions.
*
* @var array
*/
public $interfaces = [];

/**
* Trait definitions.
*
Expand All @@ -39,11 +46,11 @@ class Analysis
public $traits = [];

/**
* Interface definitions.
* Enum definitions.
*
* @var array
*/
public $interfaces = [];
public $enums = [];

/**
* The target OpenApi annotation.
Expand Down Expand Up @@ -129,6 +136,12 @@ public function addTraitDefinition(array $definition): void
$this->traits[$trait] = $definition;
}

public function addEnumDefinition(array $definition): void
{
$enum = $definition['context']->fullyQualifiedName($definition['enum']);
$this->enums[$enum] = $definition;
}

public function addAnalysis(Analysis $analysis): void
{
foreach ($analysis->annotations as $annotation) {
Expand Down
43 changes: 15 additions & 28 deletions src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,6 @@
use OpenApi\Analysers\ReflectionAnalyser;
use OpenApi\Annotations\OpenApi;
use OpenApi\Loggers\DefaultLogger;
use OpenApi\Processors\AugmentParameters;
use OpenApi\Processors\AugmentProperties;
use OpenApi\Processors\AugmentSchemas;
use OpenApi\Processors\BuildPaths;
use OpenApi\Processors\CleanUnmerged;
use OpenApi\Processors\DocBlockDescriptions;
use OpenApi\Processors\ExpandClasses;
use OpenApi\Processors\ExpandInterfaces;
use OpenApi\Processors\ExpandTraits;
use OpenApi\Processors\MergeIntoComponents;
use OpenApi\Processors\MergeIntoOpenApi;
use OpenApi\Processors\MergeJsonContent;
use OpenApi\Processors\MergeXmlContent;
use OpenApi\Processors\OperationId;
use Psr\Log\LoggerInterface;

/**
Expand Down Expand Up @@ -178,20 +164,21 @@ public function getProcessors(): array
{
if (null === $this->processors) {
$this->processors = [
new DocBlockDescriptions(),
new MergeIntoOpenApi(),
new MergeIntoComponents(),
new ExpandClasses(),
new ExpandInterfaces(),
new ExpandTraits(),
new AugmentSchemas(),
new AugmentProperties(),
new BuildPaths(),
new AugmentParameters(),
new MergeJsonContent(),
new MergeXmlContent(),
new OperationId(),
new CleanUnmerged(),
new Processors\DocBlockDescriptions(),
new Processors\MergeIntoOpenApi(),
new Processors\MergeIntoComponents(),
new Processors\ExpandClasses(),
new Processors\ExpandInterfaces(),
new Processors\ExpandTraits(),
new Processors\ExpandEnums(),
new Processors\AugmentSchemas(),
new Processors\AugmentProperties(),
new Processors\BuildPaths(),
new Processors\AugmentParameters(),
new Processors\MergeJsonContent(),
new Processors\MergeXmlContent(),
new Processors\OperationId(),
new Processors\CleanUnmerged(),
];
}

Expand Down
46 changes: 46 additions & 0 deletions src/Processors/ExpandEnums.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types=1);

/**
* @license Apache 2.0
*/

namespace OpenApi\Processors;

use OpenApi\Analysis;
use OpenApi\Annotations\Schema as AnnotationSchema;
use OpenApi\Attributes\Schema as AttributeSchema;
use OpenApi\Generator;

/**
* Look at all enums with a schema and:
* - set the name `schema`
* - set `enum` values.
*/
class ExpandEnums
{
public function __invoke(Analysis $analysis)
{
if (!class_exists('\\ReflectionEnum')) {
return;
}

/** @var AnnotationSchema[] $schemas */
$schemas = $analysis->getAnnotationsOfType([AnnotationSchema::class, AttributeSchema::class], true);

foreach ($schemas as $schema) {
if ($schema->_context->is('enum')) {
$source = $schema->_context->enum;
$re = new \ReflectionEnum($schema->_context->fullyQualifiedName($source));
$schema->schema = $schema->schema !== Generator::UNDEFINED ? $schema->schema : $re->getShortName();
$schema->enum = array_map(function ($case) {
return $case->name;
}, $re->getCases());
$type = 'string';
if ($re->isBacked() && $backingType = $re->getBackingType()) {
$type = $schema->type !== Generator::UNDEFINED ? $schema->type : $backingType->getName();
}
$schema->type = $type;
}
}
}
}
Loading