Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [11.1.0] - 2025.07.08
### Added
- Support for array of enum elements in Schemas

## [11.0.0] - 2025.04.21
### Added
- Support for OpenAPI 3.1
Expand Down
4 changes: 4 additions & 0 deletions src/Generator/EnumGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public function generate(Specification $specification, PhpFileCollection $fileRe
}
}
}

if ($field->isEnum()) {
$this->generateEnum($field, $fileRegistry);
}
}
foreach ($specification->getOperations() as $operation) {
foreach ($operation->request->fields as $field) {
Expand Down
2 changes: 1 addition & 1 deletion src/Generator/MutatorAccessorClassGeneratorAbstract.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ protected function generateEnumStatements(Field $field): array
}

$statements = [];
$enumValues = $field->getEnumValues();
$enumValues = $field->isArrayOfEnums() ? $field->getArrayItem()->getEnumValues() : $field->getEnumValues();
if (!empty($enumValues)) {
foreach ($enumValues as $enumValue) {
if (is_string($enumValue)) {
Expand Down
21 changes: 21 additions & 0 deletions src/Generator/SchemaGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,27 @@ private function collectSerializationFields(Field $root, Variable $arrayVariable
if ($propertyField->isNullable()) {
$value = $this->builder->nullsafePropertyFetch($value, 'value');
}
} elseif ($propertyField->isArrayOfEnums() && $this->phpVersion->isEnumSupported()) {
$enumField = $propertyField->getArrayItem();
$arrayMapCall = $this->builder->funcCall(
'array_map',
[
$this->builder->arrowFunction(
$this->builder->propertyFetch($this->builder->var('item'), 'value'),
[$this->builder->param('item')->setType($enumField->getPhpClassName())->getNode()],
$enumField->getType()->toPhpType(),
),
$value,
]
);

$value = $propertyField->isNullable()
? $this->builder->ternary(
$this->builder->notEquals($value, $this->builder->val(null)),
$arrayMapCall,
$this->builder->val(null)
)
: $arrayMapCall;
}

$fieldName = $this->builder->val($propertyField->getName());
Expand Down
46 changes: 46 additions & 0 deletions src/Generator/SchemaMapperGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,29 @@ protected function generateMapStatementsForObject(Field $root, Variable $payload
} else {
$requiredVars[] = $newEnum;
}
} elseif ($field->isArrayOfEnums() && $this->phpVersion->isEnumSupported()) {
$enumField = $field->getArrayItem();
$this->addImport($this->fqdn($this->withSubNamespace(SchemaGenerator::NAMESPACE_SUBPATH), $enumField->getPhpClassName()));

$arrowFunctionParam = $this->builder->param('item')->setType($enumField->getType()->toPhpType())->getNode();
$arrayMapCall = $this->builder->funcCall(
'array_map',
[
$this->builder->arrowFunction(
$this->builder->staticCall($enumField->getPhpClassName(), 'from', [$this->builder->var('item')]),
[$arrowFunctionParam],
$enumField->getPhpClassName(),
),
$requiredResponseItems[$i],
]
);
$requiredVars[] = $field->isNullable()
? $this->builder->ternary(
$this->builder->notEquals($requiredResponseItems[$i], $this->builder->val(null)),
$arrayMapCall,
$this->builder->val(null)
)
: $arrayMapCall;
} else {
$requiredVars[] = $requiredResponseItems[$i];
}
Expand Down Expand Up @@ -409,6 +432,29 @@ protected function generateMapStatementsForObject(Field $root, Variable $payload
} elseif ($field->isEnum() && $this->phpVersion->isEnumSupported()) {
$this->addImport($this->fqdn($this->withSubNamespace(SchemaGenerator::NAMESPACE_SUBPATH), $field->getPhpClassName()));
$optionalVar = $this->builder->staticCall($field->getPhpClassName(), 'from', [$optionalResponseItems[$i]]);
} elseif ($field->isArrayOfEnums() && $this->phpVersion->isEnumSupported()) {
$enumField = $field->getArrayItem();
$this->addImport($this->fqdn($this->withSubNamespace(SchemaGenerator::NAMESPACE_SUBPATH), $enumField->getPhpClassName()));

$arrowFunctionParam = $this->builder->param('item')->setType($enumField->getType()->toPhpType())->getNode();
$arrayMapCall = $this->builder->funcCall(
'array_map',
[
$this->builder->arrowFunction(
$this->builder->staticCall($enumField->getPhpClassName(), 'from', [$this->builder->var('item')]),
[$arrowFunctionParam],
$enumField->getPhpClassName(),
),
$optionalResponseItems[$i],
]
);
$optionalVar = $field->isNullable()
? $this->builder->ternary(
$this->builder->notEquals($optionalResponseItems[$i], $this->builder->val(null)),
$arrayMapCall,
$this->builder->val(null)
)
: $arrayMapCall;
} else {
$optionalVar = $optionalResponseItems[$i];
}
Expand Down
11 changes: 9 additions & 2 deletions src/Input/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@

use cebe\openapi\ReferenceContext;
use cebe\openapi\spec\OpenApi;
use DoclerLabs\ApiClientGenerator\Ast\PhpVersion;
use DoclerLabs\ApiClientGenerator\Entity\Field;
use DoclerLabs\ApiClientGenerator\Entity\FieldCollection;
use DoclerLabs\ApiClientGenerator\Entity\OperationCollection;
use DoclerLabs\ApiClientGenerator\Input\Factory\OperationCollectionFactory;

class Parser
{
public function __construct(private OperationCollectionFactory $operationCollectionFactory)
{
public function __construct(
private OperationCollectionFactory $operationCollectionFactory,
private PhpVersion $phpVersion
) {
}

public function parse(array $data, string $contextUri): Specification
Expand Down Expand Up @@ -79,6 +82,10 @@ private function extractField(Field $field, FieldCollection $allFields): void
$allFields->add($field->getArrayItem());
$this->extractPropertyFields($field->getArrayItem(), $allFields);
}

if ($field->isArrayOfEnums() && $this->phpVersion->isEnumSupported()) {
$allFields->add($field->getArrayItem());
}
}

private function extractPropertyFields(Field $rootObject, FieldCollection $allFields): void
Expand Down
3 changes: 2 additions & 1 deletion src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ public function register(Container $pimple): void
$pimple[FileReader::class] = static fn () => new FileReader();

$pimple[OpenApiParser::class] = static fn (Container $container) => new OpenApiParser(
$container[OperationCollectionFactory::class]
$container[OperationCollectionFactory::class],
$container[PhpVersion::class]
);

$pimple[CodeBuilder::class] = static fn (Container $container) => new CodeBuilder(
Expand Down
12 changes: 12 additions & 0 deletions test/suite/functional/Generator/EnumGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ public function exampleProvider(): array
self::BASE_NAMESPACE . SchemaGenerator::NAMESPACE_SUBPATH . '\\ItemMandatoryEnum',
ConfigurationBuilder::fake()->withPhpVersion(PhpVersion::VERSION_PHP81)->build(),
],
'Array of Enums - String Enum' => [
'/Schema/arrayOfEnums.yaml',
'/Schema/StringParamOneEnum.php',
self::BASE_NAMESPACE . SchemaGenerator::NAMESPACE_SUBPATH . '\\StringParamOneEnum',
ConfigurationBuilder::fake()->withPhpVersion(PhpVersion::VERSION_PHP81)->build(),
],
'Array of Enums - Integer Enum' => [
'/Schema/arrayOfEnums.yaml',
'/Schema/IntParamEnum.php',
self::BASE_NAMESPACE . SchemaGenerator::NAMESPACE_SUBPATH . '\\IntParamEnum',
ConfigurationBuilder::fake()->withPhpVersion(PhpVersion::VERSION_PHP81)->build(),
],
];
}

Expand Down
18 changes: 18 additions & 0 deletions test/suite/functional/Generator/Schema/IntParamEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

/*
* This file was generated by docler-labs/api-client-generator.
*
* Do not edit it manually.
*/

namespace Test\Schema;

enum IntParamEnum: int
{
case V_2 = 2;
case V_4 = 4;
case V_6 = 6;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?php

declare(strict_types=1);

/*
* This file was generated by docler-labs/api-client-generator.
*
* Do not edit it manually.
*/

namespace Test\Schema;

use JsonSerializable;

class ItemWithArraysOfEnumProperties implements SerializableInterface, JsonSerializable
{
public const MANDATORY_STRING_ENUM_ARRAY_OPTION1 = 'option1';

public const MANDATORY_STRING_ENUM_ARRAY_OPTION2 = 'option2';

public const MANDATORY_STRING_ENUM_ARRAY_OPTION3 = 'option3';

public const MANDATORY_NULLABLE_STRING_ENUM_ARRAY_OPTION1 = 'option1';

public const MANDATORY_NULLABLE_STRING_ENUM_ARRAY_OPTION2 = 'option2';

public const MANDATORY_NULLABLE_STRING_ENUM_ARRAY_OPTION3 = 'option3';

public const OPTIONAL_STRING_ENUM_ARRAY_OPTION_A = 'optionA';

public const OPTIONAL_STRING_ENUM_ARRAY_OPTION_B = 'optionB';

public const OPTIONAL_STRING_ENUM_ARRAY_OPTION_C = 'optionC';

public const NULLABLE_STRING_ENUM_ARRAY_OPTION_A = 'optionA';

public const NULLABLE_STRING_ENUM_ARRAY_OPTION_B = 'optionB';

public const NULLABLE_STRING_ENUM_ARRAY_OPTION_C = 'optionC';

private array $mandatoryStringEnumArray;

private ?array $mandatoryNullableStringEnumArray = null;

private ?array $optionalStringEnumArray = null;

private array $mandatoryIntegerEnumArray;

private ?array $nullableStringEnumArray = null;

private array $optionalPropertyChanged = ['optionalStringEnumArray' => false, 'nullableStringEnumArray' => false];

/**
* @param string[] $mandatoryStringEnumArray
* @param string[]|null $mandatoryNullableStringEnumArray
* @param int[] $mandatoryIntegerEnumArray
*/
public function __construct(array $mandatoryStringEnumArray, ?array $mandatoryNullableStringEnumArray, array $mandatoryIntegerEnumArray)
{
$this->mandatoryStringEnumArray = $mandatoryStringEnumArray;
$this->mandatoryNullableStringEnumArray = $mandatoryNullableStringEnumArray;
$this->mandatoryIntegerEnumArray = $mandatoryIntegerEnumArray;
}

/**
* @param string[] $optionalStringEnumArray
*/
public function setOptionalStringEnumArray(array $optionalStringEnumArray): self
{
$this->optionalStringEnumArray = $optionalStringEnumArray;
$this->optionalPropertyChanged['optionalStringEnumArray'] = true;

return $this;
}

/**
* @param string[]|null $nullableStringEnumArray
*/
public function setNullableStringEnumArray(?array $nullableStringEnumArray): self
{
$this->nullableStringEnumArray = $nullableStringEnumArray;
$this->optionalPropertyChanged['nullableStringEnumArray'] = true;

return $this;
}

public function hasOptionalStringEnumArray(): bool
{
return $this->optionalPropertyChanged['optionalStringEnumArray'];
}

public function hasNullableStringEnumArray(): bool
{
return $this->optionalPropertyChanged['nullableStringEnumArray'];
}

/**
* @return string[]
*/
public function getMandatoryStringEnumArray(): array
{
return $this->mandatoryStringEnumArray;
}

/**
* @return string[]|null
*/
public function getMandatoryNullableStringEnumArray(): ?array
{
return $this->mandatoryNullableStringEnumArray;
}

/**
* @return string[]|null
*/
public function getOptionalStringEnumArray(): ?array
{
return $this->optionalStringEnumArray;
}

/**
* @return int[]
*/
public function getMandatoryIntegerEnumArray(): array
{
return $this->mandatoryIntegerEnumArray;
}

/**
* @return string[]|null
*/
public function getNullableStringEnumArray(): ?array
{
return $this->nullableStringEnumArray;
}

public function toArray(): array
{
$fields = [];
$fields['mandatoryStringEnumArray'] = $this->mandatoryStringEnumArray;
$fields['mandatoryNullableStringEnumArray'] = $this->mandatoryNullableStringEnumArray;
if ($this->hasOptionalStringEnumArray()) {
$fields['optionalStringEnumArray'] = $this->optionalStringEnumArray;
}
$fields['mandatoryIntegerEnumArray'] = $this->mandatoryIntegerEnumArray;
if ($this->hasNullableStringEnumArray()) {
$fields['nullableStringEnumArray'] = $this->nullableStringEnumArray;
}

return $fields;
}

public function jsonSerialize(): array
{
return $this->toArray();
}
}
Loading