├── src ├── Model │ ├── OpenApiModel.php │ ├── SecuritySchemeIn.php │ ├── ParameterIn.php │ ├── OpenApiTrait.php │ ├── SecurityRequirement.php │ ├── CallbackRequest.php │ ├── ExternalDocumentation.php │ ├── Discriminator.php │ ├── Contact.php │ ├── License.php │ ├── Reference.php │ ├── Responses.php │ ├── Tag.php │ ├── ServerVariable.php │ ├── RequestBody.php │ ├── Server.php │ ├── Example.php │ ├── OauthFlows.php │ ├── Xml.php │ ├── OauthFlow.php │ ├── Response.php │ ├── MediaType.php │ ├── Encoding.php │ ├── Link.php │ ├── Info.php │ ├── SecurityScheme.php │ ├── PathItem.php │ ├── OpenApi.php │ ├── Parameter.php │ ├── Components.php │ ├── Operation.php │ └── Schema.php ├── Dumper │ ├── DumperInterface.php │ ├── JsonDumper.php │ └── YamlDumper.php ├── Documentation │ ├── PartialDocumentationInterface.php │ ├── DocumentationInterface.php │ └── AbstractDocumentation.php ├── Configurator │ ├── Traits │ │ ├── NameTrait.php │ │ ├── SummaryTrait.php │ │ ├── DeprecatedTrait.php │ │ ├── DescriptionTrait.php │ │ ├── ExtensionsTrait.php │ │ ├── ExternalDocsTrait.php │ │ ├── ServersTrait.php │ │ ├── SchemaTrait.php │ │ ├── ServerVariablesTrait.php │ │ ├── LinksTrait.php │ │ ├── ExamplesTrait.php │ │ ├── ParametersTrait.php │ │ ├── ResponsesTrait.php │ │ ├── ContentTrait.php │ │ ├── HeadersTrait.php │ │ └── QueryParametersTrait.php │ ├── TagConfigurator.php │ ├── ServerConfigurator.php │ ├── ResponseConfigurator.php │ ├── RequestBodyConfigurator.php │ ├── MediaTypeConfigurator.php │ ├── ResponsesConfigurator.php │ ├── ReferenceConfigurator.php │ ├── CallbackRequestConfigurator.php │ ├── ExampleConfigurator.php │ ├── EncodingConfigurator.php │ ├── QueryParametersConfigurator.php │ ├── LinkConfigurator.php │ ├── SecuritySchemeConfigurator.php │ ├── InfoConfigurator.php │ ├── QueryParameterConfigurator.php │ ├── ParameterConfigurator.php │ ├── DocumentationConfigurator.php │ ├── PathItemConfigurator.php │ ├── ComponentsConfigurator.php │ ├── OperationConfigurator.php │ └── SchemaConfigurator.php ├── Compiler │ ├── DocumentationCompilerInterface.php │ └── DocumentationCompiler.php ├── Loader │ ├── ComponentsLoaderInterface.php │ ├── SelfDescribingSchemaInterface.php │ ├── SelfDescribingQueryParametersInterface.php │ └── SelfDescribingSchemaLoader.php └── Builder │ ├── OpenApiBuilderInterface.php │ └── OpenApiBuilder.php ├── psalm.xml ├── .github └── workflows │ ├── coding-style.yaml │ ├── psalm.yaml │ └── unit-tests.yaml ├── LICENSE ├── composer.json ├── .php-cs-fixer.dist.php └── README.md /src/Model/OpenApiModel.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | interface OpenApiModel 17 | { 18 | public function toArray(): array; 19 | } 20 | -------------------------------------------------------------------------------- /src/Model/SecuritySchemeIn.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | enum SecuritySchemeIn: string 17 | { 18 | case QUERY = 'query'; 19 | case HEADER = 'header'; 20 | case COOKIE = 'cookie'; 21 | } 22 | -------------------------------------------------------------------------------- /src/Dumper/DumperInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Dumper; 11 | 12 | use Selency\OpenApi\Model\OpenApi; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | interface DumperInterface 19 | { 20 | public function dump(OpenApi $compiledDoc): string; 21 | } 22 | -------------------------------------------------------------------------------- /src/Model/ParameterIn.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | enum ParameterIn: string 17 | { 18 | case PATH = 'path'; 19 | case QUERY = 'query'; 20 | case HEADER = 'header'; 21 | case COOKIE = 'cookie'; 22 | } 23 | -------------------------------------------------------------------------------- /src/Documentation/PartialDocumentationInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Documentation; 11 | 12 | use Selency\OpenApi\Configurator\DocumentationConfigurator; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | interface PartialDocumentationInterface 19 | { 20 | public function configure(DocumentationConfigurator $doc): void; 21 | } 22 | -------------------------------------------------------------------------------- /src/Configurator/Traits/NameTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | trait NameTrait 17 | { 18 | private ?string $name = null; 19 | 20 | public function name(string $name): static 21 | { 22 | $this->name = $name; 23 | 24 | return $this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Compiler/DocumentationCompilerInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Compiler; 11 | 12 | use Selency\OpenApi\Documentation\DocumentationInterface; 13 | use Selency\OpenApi\Model\OpenApi; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | interface DocumentationCompilerInterface 20 | { 21 | public function compile(DocumentationInterface $doc): OpenApi; 22 | } 23 | -------------------------------------------------------------------------------- /src/Configurator/Traits/SummaryTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | trait SummaryTrait 17 | { 18 | private ?string $summary = null; 19 | 20 | public function summary(string $summary): static 21 | { 22 | $this->summary = $summary; 23 | 24 | return $this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Configurator/Traits/DeprecatedTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | trait DeprecatedTrait 17 | { 18 | private ?bool $deprecated = null; 19 | 20 | public function deprecated(bool $deprecated): static 21 | { 22 | $this->deprecated = $deprecated; 23 | 24 | return $this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Loader/ComponentsLoaderInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Loader; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Configurator\ComponentsConfigurator; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | interface ComponentsLoaderInterface 20 | { 21 | public function load(OpenApiBuilderInterface $openApi): ComponentsConfigurator; 22 | } 23 | -------------------------------------------------------------------------------- /src/Configurator/Traits/DescriptionTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | trait DescriptionTrait 17 | { 18 | private ?string $description = null; 19 | 20 | public function description(string $description): static 21 | { 22 | $this->description = $description; 23 | 24 | return $this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Loader/SelfDescribingSchemaInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Loader; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Configurator\SchemaConfigurator; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | interface SelfDescribingSchemaInterface 20 | { 21 | public static function describeSchema(SchemaConfigurator $schema, OpenApiBuilderInterface $openApi): void; 22 | } 23 | -------------------------------------------------------------------------------- /src/Loader/SelfDescribingQueryParametersInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Loader; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Configurator\QueryParametersConfigurator; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | interface SelfDescribingQueryParametersInterface 20 | { 21 | public static function describeQueryParameters(QueryParametersConfigurator $queryParams, OpenApiBuilderInterface $openApi): void; 22 | } 23 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ExtensionsTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | trait ExtensionsTrait 17 | { 18 | /** 19 | * @var array 20 | */ 21 | private array $specificationExtensions = []; 22 | 23 | public function specificationExtension(string $name, mixed $value): static 24 | { 25 | $this->specificationExtensions[$name] = $value; 26 | 27 | return $this; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Configurator/TagConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Tag; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class TagConfigurator 19 | { 20 | use Traits\DescriptionTrait; 21 | use Traits\ExtensionsTrait; 22 | use Traits\ExternalDocsTrait; 23 | use Traits\NameTrait; 24 | 25 | public function build(): Tag 26 | { 27 | return new Tag($this->name ?: '', $this->description, $this->externalDocs, $this->specificationExtensions); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ExternalDocsTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Model\ExternalDocumentation; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | trait ExternalDocsTrait 19 | { 20 | private ?ExternalDocumentation $externalDocs = null; 21 | 22 | public function externalDocs(string $url, string $description = null, array $specificationExtensions = []): static 23 | { 24 | $this->externalDocs = new ExternalDocumentation($url, $description, $specificationExtensions); 25 | 26 | return $this; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/coding-style.yaml: -------------------------------------------------------------------------------- 1 | name: Coding Style 2 | 3 | on: 4 | pull_request: ~ 5 | push: 6 | branches: 7 | - main 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 11 | cancel-in-progress: true 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | coding-style: 18 | name: Coding Style 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: shivammathur/setup-php@v2 23 | with: 24 | php-version: '8.1' 25 | coverage: none 26 | - name: php-cs-fixer 27 | run: | 28 | wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v3.13.2/php-cs-fixer.phar -q 29 | php php-cs-fixer.phar fix --dry-run --diff 30 | -------------------------------------------------------------------------------- /src/Configurator/ServerConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Server; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class ServerConfigurator 19 | { 20 | use Traits\DescriptionTrait; 21 | use Traits\ExtensionsTrait; 22 | use Traits\ServerVariablesTrait; 23 | 24 | public function __construct(private readonly string $url) 25 | { 26 | } 27 | 28 | public function build(): Server 29 | { 30 | return new Server($this->url, $this->description, $this->variables ?: null, $this->specificationExtensions); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ServersTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\ServerConfigurator; 13 | use Selency\OpenApi\Model\Server; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | trait ServersTrait 20 | { 21 | /** 22 | * @var Server[] 23 | */ 24 | private array $servers = []; 25 | 26 | public function server(ServerConfigurator|string $server): static 27 | { 28 | $this->servers[] = \is_string($server) ? (new ServerConfigurator($server))->build() : $server->build(); 29 | 30 | return $this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Documentation/DocumentationInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Documentation; 11 | 12 | use Selency\OpenApi\Configurator\DocumentationConfigurator; 13 | use Selency\OpenApi\Loader\ComponentsLoaderInterface; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | interface DocumentationInterface 20 | { 21 | public function getIdentifier(): string; 22 | 23 | public function getVersion(): string; 24 | 25 | public function configure(DocumentationConfigurator $doc): void; 26 | 27 | public function loadComponents(DocumentationConfigurator $doc, ComponentsLoaderInterface $loader): void; 28 | } 29 | -------------------------------------------------------------------------------- /src/Dumper/JsonDumper.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Dumper; 11 | 12 | use Selency\OpenApi\Model\OpenApi; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class JsonDumper implements DumperInterface 19 | { 20 | public function __construct(private int $flags = \JSON_PRETTY_PRINT | \JSON_THROW_ON_ERROR) 21 | { 22 | } 23 | 24 | public function dump(OpenApi $compiledDoc): string 25 | { 26 | $json = json_encode($compiledDoc->toArray(), $this->flags); 27 | 28 | // Marker to allow no security 29 | return str_replace('"__NO_SECURITY": []', '', $json); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Configurator/Traits/SchemaTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 13 | use Selency\OpenApi\Configurator\SchemaConfigurator; 14 | use Selency\OpenApi\Model\Reference; 15 | use Selency\OpenApi\Model\Schema; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | trait SchemaTrait 22 | { 23 | private Schema|Reference|null $schema = null; 24 | 25 | public function schema(SchemaConfigurator|ReferenceConfigurator|string $schema): static 26 | { 27 | $this->schema = SchemaConfigurator::createFromDefinition($schema)->build(); 28 | 29 | return $this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ServerVariablesTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Model\ServerVariable; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | trait ServerVariablesTrait 19 | { 20 | /** 21 | * @var array 22 | */ 23 | private array $variables = []; 24 | 25 | public function variable(string $name, string $default, string $description = null, array $enum = null, array $specificationExtensions = []): static 26 | { 27 | $this->variables[$name] = new ServerVariable($default, $description, $enum, $specificationExtensions); 28 | 29 | return $this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Configurator/ResponseConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Response; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class ResponseConfigurator 19 | { 20 | use Traits\ContentTrait; 21 | use Traits\DescriptionTrait; 22 | use Traits\ExtensionsTrait; 23 | use Traits\HeadersTrait; 24 | use Traits\LinksTrait; 25 | 26 | public function build(): Response 27 | { 28 | return new Response( 29 | $this->description ?: '', 30 | $this->headers ?: null, 31 | $this->content ?: null, 32 | $this->links ?: null, 33 | $this->specificationExtensions, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Configurator/RequestBodyConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\RequestBody; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class RequestBodyConfigurator 19 | { 20 | use Traits\ContentTrait; 21 | use Traits\DescriptionTrait; 22 | use Traits\ExtensionsTrait; 23 | 24 | private ?bool $required = null; 25 | 26 | public function build(): RequestBody 27 | { 28 | return new RequestBody($this->content, $this->description, $this->required, $this->specificationExtensions); 29 | } 30 | 31 | public function required(bool $required): static 32 | { 33 | $this->required = $required; 34 | 35 | return $this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Dumper/YamlDumper.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Dumper; 11 | 12 | use Selency\OpenApi\Model\OpenApi; 13 | use Symfony\Component\Yaml\Yaml; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | class YamlDumper implements DumperInterface 20 | { 21 | public function __construct( 22 | private int $inline = 10, 23 | private int $indent = 4, 24 | private int $flags = Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK | Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE, 25 | ) { 26 | } 27 | 28 | public function dump(OpenApi $compiledDoc): string 29 | { 30 | $yaml = Yaml::dump($compiledDoc->toArray(), $this->inline, $this->indent, $this->flags); 31 | 32 | // Marker to allow no security 33 | return str_replace('__NO_SECURITY: []', '{}', $yaml); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-present Selency 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/Configurator/Traits/LinksTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\LinkConfigurator; 13 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 14 | use Selency\OpenApi\Model\Link; 15 | use Selency\OpenApi\Model\Reference; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | trait LinksTrait 22 | { 23 | /** 24 | * @var array 25 | */ 26 | private array $links = []; 27 | 28 | public function link(string $name, LinkConfigurator|ReferenceConfigurator|string $link): static 29 | { 30 | if (\is_string($link)) { 31 | $link = new ReferenceConfigurator('#/components/links/'.ReferenceConfigurator::normalize($link)); 32 | } 33 | 34 | $this->links[$name] = $link->build(); 35 | 36 | return $this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Model/OpenApiTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | trait OpenApiTrait 17 | { 18 | public function toArray(): array 19 | { 20 | return []; 21 | } 22 | 23 | private function normalizeCollection(?array $items): ?array 24 | { 25 | if (null === $items) { 26 | return null; 27 | } 28 | 29 | $normalized = []; 30 | foreach ($items as $key => $item) { 31 | if ($item instanceof OpenApiModel) { 32 | $normalized[$key] = $item->toArray(); 33 | } elseif ($item instanceof \BackedEnum) { 34 | $normalized[$key] = $item->value; 35 | } else { 36 | $normalized[$key] = $item; 37 | } 38 | } 39 | 40 | return $normalized; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ExamplesTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\ExampleConfigurator; 13 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 14 | use Selency\OpenApi\Model\Example; 15 | use Selency\OpenApi\Model\Reference; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | trait ExamplesTrait 22 | { 23 | private mixed $example = null; 24 | 25 | /** 26 | * @var array|null 27 | */ 28 | private ?array $examples = null; 29 | 30 | public function example(mixed $name, ExampleConfigurator|ReferenceConfigurator $example = null): static 31 | { 32 | if ($example) { 33 | $this->examples[$name] = $example->build(); 34 | } else { 35 | $this->example = $name; 36 | } 37 | 38 | return $this; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ParametersTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\ParameterConfigurator; 13 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 14 | use Selency\OpenApi\Model\Parameter; 15 | use Selency\OpenApi\Model\Reference; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | trait ParametersTrait 22 | { 23 | /** 24 | * @var array 25 | */ 26 | private array $parameters = []; 27 | 28 | public function parameter(ParameterConfigurator|ReferenceConfigurator|string $parameter): static 29 | { 30 | if (\is_string($parameter)) { 31 | $parameter = new ReferenceConfigurator('#/components/parameters/'.ReferenceConfigurator::normalize($parameter)); 32 | } 33 | 34 | $this->parameters[] = $parameter->build(); 35 | 36 | return $this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/psalm.yaml: -------------------------------------------------------------------------------- 1 | name: Psalm 2 | 3 | on: 4 | pull_request: ~ 5 | push: 6 | branches: 7 | - main 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 11 | cancel-in-progress: true 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | psalm: 18 | name: Psalm 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | 25 | - name: Setup PHP 26 | uses: shivammathur/setup-php@v2 27 | with: 28 | php-version: 8.1 29 | coverage: none 30 | ini-values: date.timezone=UTC,memory_limit=-1,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 31 | 32 | - name: Install dependencies 33 | run: | 34 | composer update --prefer-dist --no-interaction --no-ansi --no-progress 35 | composer require --dev phpunit/phpunit:^9.5 sebastian/diff:^4.0 36 | 37 | - name: Psalm 38 | run: php vendor/bin/psalm --output-format=github --no-progress 39 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ResponsesTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 13 | use Selency\OpenApi\Configurator\ResponseConfigurator; 14 | use Selency\OpenApi\Model\Reference; 15 | use Selency\OpenApi\Model\Response; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | trait ResponsesTrait 22 | { 23 | /** 24 | * @var array 25 | */ 26 | private array $responses = []; 27 | 28 | public function response(string $name, ResponseConfigurator|ReferenceConfigurator|string $response): static 29 | { 30 | if (\is_string($response)) { 31 | $response = new ReferenceConfigurator('#/components/responses/'.ReferenceConfigurator::normalize($response)); 32 | } 33 | 34 | $this->responses[$name] = $response->build(); 35 | 36 | return $this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Configurator/Traits/ContentTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\MediaTypeConfigurator; 13 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 14 | use Selency\OpenApi\Configurator\SchemaConfigurator; 15 | use Selency\OpenApi\Model\MediaType; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | trait ContentTrait 22 | { 23 | /** 24 | * @var array 25 | */ 26 | private array $content = []; 27 | 28 | public function content(string $contentType, MediaTypeConfigurator|SchemaConfigurator|ReferenceConfigurator|string $mediaType): static 29 | { 30 | if (!$mediaType instanceof MediaTypeConfigurator) { 31 | $mediaType = (new MediaTypeConfigurator())->schema($mediaType); 32 | } 33 | 34 | $this->content[$contentType] = $mediaType->build(); 35 | 36 | return $this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Model/SecurityRequirement.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class SecurityRequirement implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public const NONE = '__NO_SECURITY'; 21 | 22 | /** 23 | * @param string[] $config 24 | */ 25 | public function __construct( 26 | private readonly string $name, 27 | private readonly array $config = [], 28 | ) { 29 | } 30 | 31 | public function getName(): string 32 | { 33 | return $this->name; 34 | } 35 | 36 | public function getConfig(): array 37 | { 38 | return $this->config; 39 | } 40 | 41 | public function toArray(): array 42 | { 43 | return [ 44 | $this->getName() => array_filter([ 45 | $this->normalizeCollection($this->getConfig()), 46 | ]), 47 | ]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Configurator/MediaTypeConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Encoding; 13 | use Selency\OpenApi\Model\MediaType; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | class MediaTypeConfigurator 20 | { 21 | use Traits\ExamplesTrait; 22 | use Traits\ExtensionsTrait; 23 | use Traits\SchemaTrait; 24 | 25 | /** 26 | * @var array 27 | */ 28 | private array $encodings = []; 29 | 30 | public function build(): MediaType 31 | { 32 | return new MediaType( 33 | $this->schema, 34 | $this->example, 35 | $this->examples ?: null, 36 | $this->encodings ?: null, 37 | $this->specificationExtensions, 38 | ); 39 | } 40 | 41 | public function encoding(string $name, EncodingConfigurator $encoding): static 42 | { 43 | $this->encodings[$name] = $encoding->build(); 44 | 45 | return $this; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Documentation/AbstractDocumentation.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Documentation; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilder; 13 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 14 | use Selency\OpenApi\Configurator\DocumentationConfigurator; 15 | use Selency\OpenApi\Loader\ComponentsLoaderInterface; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | abstract class AbstractDocumentation implements DocumentationInterface 22 | { 23 | protected function getOpenApiBuilder(): OpenApiBuilderInterface 24 | { 25 | return new OpenApiBuilder(); 26 | } 27 | 28 | public function getIdentifier(): string 29 | { 30 | return 'api'; 31 | } 32 | 33 | public function getVersion(): string 34 | { 35 | return '1.0.0'; 36 | } 37 | 38 | public function loadComponents(DocumentationConfigurator $doc, ComponentsLoaderInterface $loader): void 39 | { 40 | $doc->components($loader->load($this->getOpenApiBuilder())); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Configurator/ResponsesConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Reference; 13 | use Selency\OpenApi\Model\Response; 14 | use Selency\OpenApi\Model\Responses; 15 | 16 | /** 17 | * @author Titouan Galopin 18 | * @author Selency Team 19 | */ 20 | class ResponsesConfigurator 21 | { 22 | use Traits\ExtensionsTrait; 23 | use Traits\ResponsesTrait; 24 | 25 | private Response|Reference|null $default = null; 26 | 27 | public function build(): Responses 28 | { 29 | return new Responses($this->default, $this->responses ?: null, $this->specificationExtensions); 30 | } 31 | 32 | public function default(ResponseConfigurator|ReferenceConfigurator|string $response): static 33 | { 34 | if (\is_string($response)) { 35 | $response = new ReferenceConfigurator('#/components/responses/'.ReferenceConfigurator::normalize($response)); 36 | } 37 | 38 | $this->default = $response->build(); 39 | 40 | return $this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Configurator/ReferenceConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Reference; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class ReferenceConfigurator 19 | { 20 | use Traits\DescriptionTrait; 21 | use Traits\ExtensionsTrait; 22 | use Traits\SummaryTrait; 23 | 24 | public function __construct(private readonly string $ref) 25 | { 26 | } 27 | 28 | public static function normalize(?string $ref): ?string 29 | { 30 | if (!$ref) { 31 | throw new \InvalidArgumentException('Missing reference name passed to '.__CLASS__.'::'.__METHOD__.'()'); 32 | } 33 | 34 | return preg_replace('/[^a-zA-Z0-9\.\-\_]+/', '_', $ref); 35 | } 36 | 37 | public function build(): Reference 38 | { 39 | return new Reference( 40 | $this->ref, 41 | $this->summary, 42 | $this->description, 43 | $this->specificationExtensions, 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "selency/openapi", 3 | "type": "library", 4 | "description": "Provides a user-friendly, object-oriented library to build OpenAPI specifications using PHP.", 5 | "keywords": ["openapi", "api", "specification", "documentation"], 6 | "homepage": "https://selency.fr", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Titouan Galopin", 11 | "email": "galopintitouan@gmail.com" 12 | }, 13 | { 14 | "name": "Selency Team", 15 | "homepage": "https://selency.fr" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=8.1" 20 | }, 21 | "require-dev": { 22 | "symfony/phpunit-bridge": "^6.2", 23 | "symfony/var-dumper": "^6.2", 24 | "symfony/yaml": "^6.2", 25 | "vimeo/psalm": "^5.8", 26 | "phpunit/phpunit": "^9.5", 27 | "sebastian/diff": "^4.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Selency\\OpenApi\\": "src/" 32 | }, 33 | "exclude-from-classmap": [ 34 | "/tests/" 35 | ] 36 | }, 37 | "autoload-dev": { 38 | "psr-4": { 39 | "Selency\\OpenApi\\Tests\\": "tests/" 40 | } 41 | }, 42 | "minimum-stability": "dev" 43 | } 44 | -------------------------------------------------------------------------------- /src/Configurator/CallbackRequestConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\CallbackRequest; 13 | use Selency\OpenApi\Model\PathItem; 14 | use Selency\OpenApi\Model\Reference; 15 | 16 | /** 17 | * @author Titouan Galopin 18 | * @author Selency Team 19 | */ 20 | class CallbackRequestConfigurator 21 | { 22 | use Traits\ExtensionsTrait; 23 | 24 | private string $expression = ''; 25 | private PathItem|Reference|null $definition = null; 26 | 27 | public function build(): CallbackRequest 28 | { 29 | return new CallbackRequest($this->expression, $this->definition, $this->specificationExtensions); 30 | } 31 | 32 | public function expression(string $expression): static 33 | { 34 | $this->expression = $expression; 35 | 36 | return $this; 37 | } 38 | 39 | public function definition(ReferenceConfigurator|PathItemConfigurator $definition): static 40 | { 41 | $this->definition = $definition->build(); 42 | 43 | return $this; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Configurator/ExampleConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Example; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class ExampleConfigurator 19 | { 20 | use Traits\DescriptionTrait; 21 | use Traits\ExtensionsTrait; 22 | use Traits\SummaryTrait; 23 | 24 | private mixed $value = null; 25 | private ?string $externalValue = null; 26 | 27 | public function build(): Example 28 | { 29 | return new Example( 30 | $this->summary, 31 | $this->description, 32 | $this->value, 33 | $this->externalValue, 34 | $this->specificationExtensions, 35 | ); 36 | } 37 | 38 | public function value(mixed $value): static 39 | { 40 | $this->value = $value; 41 | 42 | return $this; 43 | } 44 | 45 | public function externalValue(string $externalValue): static 46 | { 47 | $this->externalValue = $externalValue; 48 | 49 | return $this; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Model/CallbackRequest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class CallbackRequest 17 | { 18 | public function __construct( 19 | private readonly string $expression, 20 | private readonly PathItem|Reference|null $definition, 21 | private readonly array $specificationExtensions = [], 22 | ) { 23 | } 24 | 25 | public function getExpression(): string 26 | { 27 | return $this->expression; 28 | } 29 | 30 | public function getDefinition(): Reference|PathItem|null 31 | { 32 | return $this->definition; 33 | } 34 | 35 | public function getSpecificationExtensions(): array 36 | { 37 | return $this->specificationExtensions; 38 | } 39 | 40 | public function toArray(): array 41 | { 42 | return array_filter([ 43 | 'expression' => $this->getExpression(), 44 | 'definition' => $this->getDefinition()?->toArray(), 45 | ] + $this->getSpecificationExtensions()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Model/ExternalDocumentation.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class ExternalDocumentation implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly string $url, 22 | private readonly ?string $description = null, 23 | private readonly array $specificationExtensions = [], 24 | ) { 25 | } 26 | 27 | public function getUrl(): string 28 | { 29 | return $this->url; 30 | } 31 | 32 | public function getDescription(): ?string 33 | { 34 | return $this->description; 35 | } 36 | 37 | public function getSpecificationExtensions(): array 38 | { 39 | return $this->specificationExtensions; 40 | } 41 | 42 | public function toArray(): array 43 | { 44 | return array_filter([ 45 | 'description' => $this->getDescription(), 46 | 'url' => $this->getUrl(), 47 | ] + $this->getSpecificationExtensions()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Configurator/Traits/HeadersTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Configurator\ParameterConfigurator; 13 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 14 | use Selency\OpenApi\Model\Parameter; 15 | use Selency\OpenApi\Model\Reference; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | trait HeadersTrait 22 | { 23 | /** 24 | * @var array 25 | */ 26 | private array $headers = []; 27 | 28 | public function header(string $name, ParameterConfigurator|ReferenceConfigurator|string $header): static 29 | { 30 | if (\is_string($header)) { 31 | $header = new ReferenceConfigurator('#/components/headers/'.ReferenceConfigurator::normalize($header)); 32 | } 33 | 34 | if ($header instanceof ReferenceConfigurator) { 35 | $this->headers[$name] = $header->build(); 36 | 37 | return $this; 38 | } 39 | 40 | $this->headers[$name] = $header->build(asHeader: true); 41 | 42 | return $this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Loader/SelfDescribingSchemaLoader.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Loader; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Configurator\ComponentsConfigurator; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | class SelfDescribingSchemaLoader implements ComponentsLoaderInterface 20 | { 21 | /** 22 | * @param iterable> $selfDescribingSchemas 23 | */ 24 | public function __construct(private iterable $selfDescribingSchemas = []) 25 | { 26 | } 27 | 28 | public function load(OpenApiBuilderInterface $openApi): ComponentsConfigurator 29 | { 30 | $components = $openApi->components(); 31 | 32 | foreach ($this->selfDescribingSchemas as $selfDescribingSchema) { 33 | $configurator = $openApi->schema()->type('object'); 34 | \call_user_func([$selfDescribingSchema, 'describeSchema'], $configurator, $openApi); 35 | 36 | $components->schema($selfDescribingSchema, $configurator); 37 | } 38 | 39 | return $components; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | $fileHeaderComment = <<<'EOF' 11 | (c) Selency 12 | 13 | For the full copyright and license information, please view the LICENSE 14 | file that was distributed with this source code. 15 | EOF; 16 | 17 | return (new PhpCsFixer\Config()) 18 | ->setRules([ 19 | '@PHP71Migration' => true, 20 | '@PHPUnit75Migration:risky' => true, 21 | '@Symfony' => true, 22 | '@Symfony:risky' => true, 23 | 'protected_to_private' => false, 24 | 'native_constant_invocation' => ['strict' => false], 25 | 'nullable_type_declaration_for_default_null_value' => ['use_nullable_type_declaration' => false], 26 | 'no_superfluous_phpdoc_tags' => ['remove_inheritdoc' => true], 27 | 'header_comment' => ['header' => $fileHeaderComment], 28 | 'modernize_strpos' => true, 29 | 'get_class_to_class_keyword' => true, 30 | ]) 31 | ->setRiskyAllowed(true) 32 | ->setFinder( 33 | (new PhpCsFixer\Finder()) 34 | ->in([__DIR__.'/src', __DIR__.'/tests']) 35 | ->append([__FILE__]) 36 | ->notPath('#/Fixtures/#') 37 | ) 38 | ->setCacheFile('.php-cs-fixer.cache') 39 | ; 40 | -------------------------------------------------------------------------------- /src/Model/Discriminator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Discriminator implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $mapping 22 | */ 23 | public function __construct( 24 | private readonly string $propertyName, 25 | private readonly ?array $mapping = null, 26 | private readonly array $specificationExtensions = [], 27 | ) { 28 | } 29 | 30 | public function getPropertyName(): string 31 | { 32 | return $this->propertyName; 33 | } 34 | 35 | public function getMapping(): ?array 36 | { 37 | return $this->mapping; 38 | } 39 | 40 | public function getSpecificationExtensions(): array 41 | { 42 | return $this->specificationExtensions; 43 | } 44 | 45 | public function toArray(): array 46 | { 47 | return array_filter([ 48 | 'propertyName' => $this->getPropertyName(), 49 | 'mapping' => $this->normalizeCollection($this->getMapping()), 50 | ] + $this->getSpecificationExtensions()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Model/Contact.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Contact implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly ?string $name = null, 22 | private readonly ?string $url = null, 23 | private readonly ?string $email = null, 24 | private readonly array $specificationExtensions = [], 25 | ) { 26 | } 27 | 28 | public function getName(): ?string 29 | { 30 | return $this->name; 31 | } 32 | 33 | public function getUrl(): ?string 34 | { 35 | return $this->url; 36 | } 37 | 38 | public function getEmail(): ?string 39 | { 40 | return $this->email; 41 | } 42 | 43 | public function getSpecificationExtensions(): array 44 | { 45 | return $this->specificationExtensions; 46 | } 47 | 48 | public function toArray(): array 49 | { 50 | return array_filter([ 51 | 'name' => $this->getName(), 52 | 'email' => $this->getEmail(), 53 | 'url' => $this->getUrl(), 54 | ] + $this->getSpecificationExtensions()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Model/License.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class License implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly string $name, 22 | private readonly ?string $identifier = null, 23 | private readonly ?string $url = null, 24 | private readonly array $specificationExtensions = [], 25 | ) { 26 | } 27 | 28 | public function getName(): string 29 | { 30 | return $this->name; 31 | } 32 | 33 | public function getIdentifier(): ?string 34 | { 35 | return $this->identifier; 36 | } 37 | 38 | public function getUrl(): ?string 39 | { 40 | return $this->url; 41 | } 42 | 43 | public function getSpecificationExtensions(): array 44 | { 45 | return $this->specificationExtensions; 46 | } 47 | 48 | public function toArray(): array 49 | { 50 | return array_filter([ 51 | 'name' => $this->getName(), 52 | 'url' => $this->getUrl(), 53 | 'identifier' => $this->getIdentifier(), // ?? 54 | ] + $this->getSpecificationExtensions()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Model/Reference.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Reference implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly string $ref, 22 | private readonly ?string $summary = null, 23 | private readonly ?string $description = null, 24 | private readonly array $specificationExtensions = [], 25 | ) { 26 | } 27 | 28 | public function getRef(): string 29 | { 30 | return $this->ref; 31 | } 32 | 33 | public function getSummary(): ?string 34 | { 35 | return $this->summary; 36 | } 37 | 38 | public function getDescription(): ?string 39 | { 40 | return $this->description; 41 | } 42 | 43 | public function getSpecificationExtensions(): array 44 | { 45 | return $this->specificationExtensions; 46 | } 47 | 48 | public function toArray(): array 49 | { 50 | return array_filter([ 51 | '$ref' => $this->getRef(), 52 | 'summary' => $this->getSummary(), // ?? 53 | 'description' => $this->getDescription(), // ?? 54 | ] + $this->getSpecificationExtensions()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Compiler/DocumentationCompiler.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Compiler; 11 | 12 | use Selency\OpenApi\Configurator\DocumentationConfigurator; 13 | use Selency\OpenApi\Documentation\DocumentationInterface; 14 | use Selency\OpenApi\Loader\ComponentsLoaderInterface; 15 | use Selency\OpenApi\Model\OpenApi; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | class DocumentationCompiler implements DocumentationCompilerInterface 22 | { 23 | /** 24 | * @param iterable $componentsLoaders 25 | */ 26 | public function __construct(private iterable $componentsLoaders = []) 27 | { 28 | } 29 | 30 | public function compile(DocumentationInterface $doc): OpenApi 31 | { 32 | // Instanciate root configurator 33 | $rootConfigurator = new DocumentationConfigurator(); 34 | 35 | // Load components for this doc 36 | foreach ($this->componentsLoaders as $loader) { 37 | $doc->loadComponents($rootConfigurator, $loader); 38 | } 39 | 40 | // Apply user documentation details 41 | $doc->configure($rootConfigurator); 42 | 43 | // Compile the documentation 44 | return $rootConfigurator->build($doc->getIdentifier(), $doc->getVersion()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Model/Responses.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Responses implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $byHttpStatusCode 22 | */ 23 | public function __construct( 24 | private readonly Response|Reference|null $default = null, 25 | private readonly ?array $byHttpStatusCode = null, 26 | private readonly array $specificationExtensions = [], 27 | ) { 28 | } 29 | 30 | public function getDefault(): Response|Reference|null 31 | { 32 | return $this->default; 33 | } 34 | 35 | /** 36 | * @return array|null 37 | */ 38 | public function getByHttpStatusCode(): ?array 39 | { 40 | return $this->byHttpStatusCode; 41 | } 42 | 43 | public function getSpecificationExtensions(): array 44 | { 45 | return $this->specificationExtensions; 46 | } 47 | 48 | public function toArray(): array 49 | { 50 | return array_filter([ 51 | 'default' => $this->getDefault()?->toArray(), 52 | ] + $this->normalizeCollection($this->getByHttpStatusCode()) + $this->getSpecificationExtensions()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Model/Tag.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Tag implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly string $name, 22 | private readonly ?string $description = null, 23 | private readonly ?ExternalDocumentation $externalDocs = null, 24 | private readonly array $specificationExtensions = [], 25 | ) { 26 | } 27 | 28 | public function getName(): string 29 | { 30 | return $this->name; 31 | } 32 | 33 | public function getDescription(): ?string 34 | { 35 | return $this->description; 36 | } 37 | 38 | public function getExternalDocs(): ?ExternalDocumentation 39 | { 40 | return $this->externalDocs; 41 | } 42 | 43 | public function getSpecificationExtensions(): array 44 | { 45 | return $this->specificationExtensions; 46 | } 47 | 48 | public function toArray(): array 49 | { 50 | return array_filter([ 51 | 'name' => $this->getName(), 52 | 'description' => $this->getDescription(), 53 | 'externalDocs' => $this->getExternalDocs()?->toArray(), 54 | ] + $this->getSpecificationExtensions()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Model/ServerVariable.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class ServerVariable implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param string[]|null $enum 22 | */ 23 | public function __construct( 24 | private readonly string $default, 25 | private readonly ?string $description = null, 26 | private readonly ?array $enum = null, 27 | private readonly array $specificationExtensions = [], 28 | ) { 29 | } 30 | 31 | public function getDefault(): string 32 | { 33 | return $this->default; 34 | } 35 | 36 | public function getDescription(): ?string 37 | { 38 | return $this->description; 39 | } 40 | 41 | /** 42 | * @return string[]|null 43 | */ 44 | public function getEnum(): ?array 45 | { 46 | return $this->enum; 47 | } 48 | 49 | public function getSpecificationExtensions(): array 50 | { 51 | return $this->specificationExtensions; 52 | } 53 | 54 | public function toArray(): array 55 | { 56 | return array_filter([ 57 | 'enum' => $this->getEnum(), 58 | 'default' => $this->getDefault(), 59 | 'description' => $this->getDescription(), 60 | ] + $this->getSpecificationExtensions()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Model/RequestBody.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class RequestBody implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array $content 22 | */ 23 | public function __construct( 24 | private readonly array $content, 25 | private readonly ?string $description = null, 26 | private readonly ?bool $required = null, 27 | private readonly array $specificationExtensions = [], 28 | ) { 29 | } 30 | 31 | /** 32 | * @return array 33 | */ 34 | public function getContent(): array 35 | { 36 | return $this->content; 37 | } 38 | 39 | public function getDescription(): ?string 40 | { 41 | return $this->description; 42 | } 43 | 44 | public function getRequired(): ?bool 45 | { 46 | return $this->required; 47 | } 48 | 49 | public function getSpecificationExtensions(): array 50 | { 51 | return $this->specificationExtensions; 52 | } 53 | 54 | public function toArray(): array 55 | { 56 | return array_filter([ 57 | 'description' => $this->getDescription(), 58 | 'content' => $this->normalizeCollection($this->getContent()), 59 | 'required' => $this->getRequired(), 60 | ] + $this->getSpecificationExtensions()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Model/Server.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Server implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $variables 22 | */ 23 | public function __construct( 24 | private readonly string $url, 25 | private readonly ?string $description = null, 26 | private readonly ?array $variables = null, 27 | private readonly array $specificationExtensions = [], 28 | ) { 29 | } 30 | 31 | public function getUrl(): string 32 | { 33 | return $this->url; 34 | } 35 | 36 | public function getDescription(): ?string 37 | { 38 | return $this->description; 39 | } 40 | 41 | /** 42 | * @return array|null 43 | */ 44 | public function getVariables(): ?array 45 | { 46 | return $this->variables; 47 | } 48 | 49 | public function getSpecificationExtensions(): array 50 | { 51 | return $this->specificationExtensions; 52 | } 53 | 54 | public function toArray(): array 55 | { 56 | return array_filter([ 57 | 'url' => $this->getUrl(), 58 | 'description' => $this->getDescription(), 59 | 'variables' => $this->normalizeCollection($this->getVariables()), 60 | ] + $this->getSpecificationExtensions()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Configurator/EncodingConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Encoding; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class EncodingConfigurator 19 | { 20 | use Traits\ExtensionsTrait; 21 | use Traits\HeadersTrait; 22 | 23 | private ?string $contentType = null; 24 | private ?string $style = null; 25 | private ?bool $explode = null; 26 | private ?bool $allowReserved = null; 27 | 28 | public function build(): Encoding 29 | { 30 | return new Encoding( 31 | $this->contentType, 32 | $this->headers, 33 | $this->style, 34 | $this->explode, 35 | $this->allowReserved, 36 | $this->specificationExtensions, 37 | ); 38 | } 39 | 40 | public function contentType(string $contentType): static 41 | { 42 | $this->contentType = $contentType; 43 | 44 | return $this; 45 | } 46 | 47 | public function style(string $style): static 48 | { 49 | $this->style = $style; 50 | 51 | return $this; 52 | } 53 | 54 | public function explode(bool $explode): static 55 | { 56 | $this->explode = $explode; 57 | 58 | return $this; 59 | } 60 | 61 | public function allowReserved(bool $allowReserved): static 62 | { 63 | $this->allowReserved = $allowReserved; 64 | 65 | return $this; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Model/Example.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Example implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly ?string $summary = null, 22 | private readonly ?string $description = null, 23 | private readonly mixed $value = null, 24 | private readonly ?string $externalValue = null, 25 | private readonly array $specificationExtensions = [], 26 | ) { 27 | } 28 | 29 | public function getSummary(): ?string 30 | { 31 | return $this->summary; 32 | } 33 | 34 | public function getDescription(): ?string 35 | { 36 | return $this->description; 37 | } 38 | 39 | public function getValue(): mixed 40 | { 41 | return $this->value; 42 | } 43 | 44 | public function getExternalValue(): ?string 45 | { 46 | return $this->externalValue; 47 | } 48 | 49 | public function getSpecificationExtensions(): array 50 | { 51 | return $this->specificationExtensions; 52 | } 53 | 54 | public function toArray(): array 55 | { 56 | return array_filter([ 57 | 'summary' => $this->getSummary(), 58 | 'description' => $this->getDescription(), 59 | 'value' => $this->normalizeCollection($this->getValue()), 60 | 'externalValue' => $this->getExternalValue(), 61 | ] + $this->getSpecificationExtensions()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Model/OauthFlows.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class OauthFlows implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly ?OauthFlow $implicit = null, 22 | private readonly ?OauthFlow $password = null, 23 | private readonly ?OauthFlow $clientCredentials = null, 24 | private readonly ?OauthFlow $authorizationCode = null, 25 | private readonly array $specificationExtensions = [], 26 | ) { 27 | } 28 | 29 | public function getImplicit(): ?OauthFlow 30 | { 31 | return $this->implicit; 32 | } 33 | 34 | public function getPassword(): ?OauthFlow 35 | { 36 | return $this->password; 37 | } 38 | 39 | public function getClientCredentials(): ?OauthFlow 40 | { 41 | return $this->clientCredentials; 42 | } 43 | 44 | public function getAuthorizationCode(): ?OauthFlow 45 | { 46 | return $this->authorizationCode; 47 | } 48 | 49 | public function getSpecificationExtensions(): array 50 | { 51 | return $this->specificationExtensions; 52 | } 53 | 54 | public function toArray(): array 55 | { 56 | return array_filter([ 57 | 'implicit' => $this->getImplicit()?->toArray(), 58 | 'password' => $this->getPassword()?->toArray(), 59 | 'clientCredentials' => $this->getClientCredentials()?->toArray(), 60 | 'authorizationCode' => $this->getAuthorizationCode()?->toArray(), 61 | ] + $this->getSpecificationExtensions()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Model/Xml.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Xml implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly ?string $name = null, 22 | private readonly ?string $namespace = null, 23 | private readonly ?string $prefix = null, 24 | private readonly ?bool $attribute = null, 25 | private readonly ?bool $wrapped = null, 26 | private readonly array $specificationExtensions = [], 27 | ) { 28 | } 29 | 30 | public function getName(): ?string 31 | { 32 | return $this->name; 33 | } 34 | 35 | public function getNamespace(): ?string 36 | { 37 | return $this->namespace; 38 | } 39 | 40 | public function getPrefix(): ?string 41 | { 42 | return $this->prefix; 43 | } 44 | 45 | public function isAttribute(): ?bool 46 | { 47 | return $this->attribute; 48 | } 49 | 50 | public function isWrapped(): ?bool 51 | { 52 | return $this->wrapped; 53 | } 54 | 55 | public function getSpecificationExtensions(): array 56 | { 57 | return $this->specificationExtensions; 58 | } 59 | 60 | public function toArray(): array 61 | { 62 | return array_filter([ 63 | 'name' => $this->getName(), 64 | 'namespace' => $this->getNamespace(), 65 | 'prefix' => $this->getPrefix(), 66 | 'attribute' => $this->isAttribute(), 67 | 'wrapped' => $this->isWrapped(), 68 | ] + $this->getSpecificationExtensions()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Configurator/QueryParametersConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Model\OpenApiTrait; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | class QueryParametersConfigurator 20 | { 21 | use OpenApiTrait; 22 | use Traits\ExtensionsTrait; 23 | use Traits\QueryParametersTrait; 24 | 25 | public function __construct(OpenApiBuilderInterface $openApiBuilder) 26 | { 27 | $this->openApiBuilder = $openApiBuilder; 28 | } 29 | 30 | public static function createFromDefinition(self|ReferenceConfigurator|string $definition, OpenApiBuilderInterface $openApiBuilder): self|ReferenceConfigurator 31 | { 32 | // Empty schema 33 | if (!$definition) { 34 | return new self($openApiBuilder); 35 | } 36 | 37 | // Direct configurator or reference 38 | if ($definition instanceof self || $definition instanceof ReferenceConfigurator) { 39 | return $definition; 40 | } 41 | 42 | // Parameter reference 43 | if (!method_exists($definition, 'describeQueryParameters')) { 44 | return new ReferenceConfigurator('#/components/parameters/'.ReferenceConfigurator::normalize($definition)); 45 | } 46 | 47 | // Describe 48 | $configurator = new self($openApiBuilder); 49 | \call_user_func([$definition, 'describeQueryParameters'], $configurator, $openApiBuilder); 50 | 51 | return $configurator; 52 | } 53 | 54 | public function getParameters(): array 55 | { 56 | return $this->queryParameters; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Model/OauthFlow.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class OauthFlow implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array $scopes 22 | */ 23 | public function __construct( 24 | private readonly string $authorizationUrl, 25 | private readonly string $tokenUrl, 26 | private readonly array $scopes = [], 27 | private readonly ?string $refreshUrl = null, 28 | private readonly array $specificationExtensions = [], 29 | ) { 30 | } 31 | 32 | public function getAuthorizationUrl(): string 33 | { 34 | return $this->authorizationUrl; 35 | } 36 | 37 | public function getTokenUrl(): string 38 | { 39 | return $this->tokenUrl; 40 | } 41 | 42 | /** 43 | * @return array 44 | */ 45 | public function getScopes(): array 46 | { 47 | return $this->scopes; 48 | } 49 | 50 | public function getRefreshUrl(): ?string 51 | { 52 | return $this->refreshUrl; 53 | } 54 | 55 | public function getSpecificationExtensions(): array 56 | { 57 | return $this->specificationExtensions; 58 | } 59 | 60 | public function toArray(): array 61 | { 62 | return array_filter([ 63 | 'authorizationUrl' => $this->getAuthorizationUrl(), 64 | 'tokenUrl' => $this->getTokenUrl(), 65 | 'refreshUrl' => $this->getRefreshUrl(), 66 | 'scopes' => $this->normalizeCollection($this->getScopes()), 67 | ] + $this->getSpecificationExtensions()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Configurator/LinkConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Link; 13 | use Selency\OpenApi\Model\Server; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | class LinkConfigurator 20 | { 21 | use Traits\DescriptionTrait; 22 | use Traits\ExtensionsTrait; 23 | use Traits\ParametersTrait; 24 | 25 | private ?string $operationRef = null; 26 | private ?string $operationId = null; 27 | private mixed $requestBody = null; 28 | private ?Server $server = null; 29 | 30 | public function build(): Link 31 | { 32 | return new Link( 33 | $this->operationRef, 34 | $this->operationId, 35 | $this->parameters ?: null, 36 | $this->requestBody, 37 | $this->description, 38 | $this->server, 39 | $this->specificationExtensions, 40 | ); 41 | } 42 | 43 | public function operationRef(?string $operationRef): static 44 | { 45 | $this->operationRef = $operationRef; 46 | 47 | return $this; 48 | } 49 | 50 | public function operationId(?string $operationId): static 51 | { 52 | $this->operationId = $operationId; 53 | 54 | return $this; 55 | } 56 | 57 | public function requestBody(mixed $requestBody): static 58 | { 59 | $this->requestBody = $requestBody; 60 | 61 | return $this; 62 | } 63 | 64 | public function server(ServerConfigurator|string $server): static 65 | { 66 | $this->server = \is_string($server) ? (new ServerConfigurator($server))->build() : $server->build(); 67 | 68 | return $this; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Configurator/Traits/QueryParametersTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator\Traits; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Configurator\QueryParameterConfigurator; 14 | use Selency\OpenApi\Configurator\QueryParametersConfigurator; 15 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 16 | use Selency\OpenApi\Model\Parameter; 17 | use Selency\OpenApi\Model\Reference; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | * @author Selency Team 22 | */ 23 | trait QueryParametersTrait 24 | { 25 | /** 26 | * @var array 27 | */ 28 | private array $queryParameters = []; 29 | private ?OpenApiBuilderInterface $openApiBuilder = null; 30 | 31 | public function queryParameter(QueryParameterConfigurator|ReferenceConfigurator|string $parameter): static 32 | { 33 | if (\is_string($parameter)) { 34 | $parameter = new ReferenceConfigurator('#/components/parameters/'.ReferenceConfigurator::normalize($parameter)); 35 | } 36 | 37 | $this->queryParameters[] = $parameter->build(); 38 | 39 | return $this; 40 | } 41 | 42 | public function queryParameters(QueryParametersConfigurator|string $parameters): static 43 | { 44 | $configurator = QueryParametersConfigurator::createFromDefinition($parameters, $this->openApiBuilder); 45 | 46 | if ($configurator instanceof ReferenceConfigurator) { 47 | $this->queryParameters[] = $configurator->build(); 48 | 49 | return $this; 50 | } 51 | 52 | $this->queryParameters = array_merge($this->queryParameters, $configurator->getParameters()); 53 | 54 | return $this; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yaml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | pull_request: ~ 5 | push: 6 | branches: 7 | - main 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 11 | cancel-in-progress: true 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | tests: 18 | name: Unit Tests 19 | runs-on: ubuntu-latest 20 | 21 | strategy: 22 | matrix: 23 | include: 24 | - php: '8.2' 25 | symfony: '6.2' 26 | mode: low-deps 27 | deprecations_helper: max[self]=0 28 | 29 | - php: '8.1' 30 | symfony: '6.2' 31 | mode: high-deps 32 | deprecations_helper: disabled=1 33 | 34 | fail-fast: false 35 | 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v3 39 | 40 | - name: Setup PHP 41 | uses: shivammathur/setup-php@v2 42 | with: 43 | php-version: "${{ matrix.php }}" 44 | coverage: none 45 | ini-values: date.timezone=UTC,memory_limit=-1,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 46 | 47 | - name: Install dependencies 48 | run: | 49 | composer require --no-update --dev symfony/phpunit-bridge=${{ matrix.symfony }} symfony/yaml=${{ matrix.symfony }} 50 | 51 | if [[ "${{ matrix.mode }}" = low-deps ]]; then 52 | composer update --prefer-dist --no-interaction --no-ansi --no-progress --prefer-lowest 53 | else 54 | composer update --prefer-dist --no-interaction --no-ansi --no-progress 55 | fi 56 | 57 | - name: Run tests 58 | run: SYMFONY_DEPRECATIONS_HELPER="${{ matrix.deprecations_helper }}" php vendor/bin/simple-phpunit 59 | -------------------------------------------------------------------------------- /src/Model/Response.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Response implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $headers 22 | * @param array|null $links 23 | */ 24 | public function __construct( 25 | private readonly string $description, 26 | private readonly ?array $headers = null, 27 | private readonly ?array $content = null, 28 | private readonly ?array $links = null, 29 | private readonly array $specificationExtensions = [], 30 | ) { 31 | } 32 | 33 | public function getDescription(): string 34 | { 35 | return $this->description; 36 | } 37 | 38 | /** 39 | * @return array|null 40 | */ 41 | public function getHeaders(): ?array 42 | { 43 | return $this->headers; 44 | } 45 | 46 | public function getContent(): ?array 47 | { 48 | return $this->content; 49 | } 50 | 51 | /** 52 | * @return array|null 53 | */ 54 | public function getLinks(): ?array 55 | { 56 | return $this->links; 57 | } 58 | 59 | public function getSpecificationExtensions(): array 60 | { 61 | return $this->specificationExtensions; 62 | } 63 | 64 | public function toArray(): array 65 | { 66 | return array_filter([ 67 | 'description' => $this->getDescription(), 68 | 'headers' => $this->normalizeCollection($this->getHeaders()), 69 | 'content' => $this->normalizeCollection($this->getContent()), 70 | 'links' => $this->normalizeCollection($this->getLinks()), 71 | ] + $this->getSpecificationExtensions()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Model/MediaType.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class MediaType implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $examples 22 | * @param array|null $encodings 23 | */ 24 | public function __construct( 25 | private readonly Schema|Reference|null $schema = null, 26 | private readonly mixed $example = null, 27 | private readonly ?array $examples = null, 28 | private readonly ?array $encodings = null, 29 | private readonly array $specificationExtensions = [], 30 | ) { 31 | } 32 | 33 | public function getSchema(): Schema|Reference|null 34 | { 35 | return $this->schema; 36 | } 37 | 38 | public function getExample(): mixed 39 | { 40 | return $this->example; 41 | } 42 | 43 | /** 44 | * @return array|null 45 | */ 46 | public function getExamples(): ?array 47 | { 48 | return $this->examples; 49 | } 50 | 51 | /** 52 | * @return array|null 53 | */ 54 | public function getEncodings(): ?array 55 | { 56 | return $this->encodings; 57 | } 58 | 59 | public function getSpecificationExtensions(): array 60 | { 61 | return $this->specificationExtensions; 62 | } 63 | 64 | public function toArray(): array 65 | { 66 | return array_filter([ 67 | 'schema' => $this->getSchema()?->toArray(), 68 | 'example' => $this->getExample(), 69 | 'examples' => $this->normalizeCollection($this->getExamples()), 70 | 'encoding' => $this->normalizeCollection($this->getEncodings()), 71 | ] + $this->getSpecificationExtensions()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Model/Encoding.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Encoding implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $headers 22 | */ 23 | public function __construct( 24 | private readonly ?string $contentType = null, 25 | private readonly ?array $headers = null, 26 | private readonly ?string $style = null, 27 | private readonly ?bool $explode = null, 28 | private readonly ?bool $allowReserved = null, 29 | private readonly array $specificationExtensions = [], 30 | ) { 31 | } 32 | 33 | public function getContentType(): ?string 34 | { 35 | return $this->contentType; 36 | } 37 | 38 | /** 39 | * @return array|null 40 | */ 41 | public function getHeaders(): ?array 42 | { 43 | return $this->headers; 44 | } 45 | 46 | public function getStyle(): ?string 47 | { 48 | return $this->style; 49 | } 50 | 51 | public function isExplode(): ?bool 52 | { 53 | return $this->explode; 54 | } 55 | 56 | public function allowReserved(): ?bool 57 | { 58 | return $this->allowReserved; 59 | } 60 | 61 | public function getSpecificationExtensions(): array 62 | { 63 | return $this->specificationExtensions; 64 | } 65 | 66 | public function toArray(): array 67 | { 68 | return array_filter([ 69 | 'contentType' => $this->getContentType(), 70 | 'headers' => $this->normalizeCollection($this->getHeaders()), 71 | 'style' => $this->getStyle(), 72 | 'explode' => $this->isExplode(), 73 | 'allowReserved' => $this->allowReserved(), 74 | ] + $this->getSpecificationExtensions()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Model/Link.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Link implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly ?string $operationRef = null, 22 | private readonly ?string $operationId = null, 23 | private readonly ?array $parameters = null, 24 | private readonly mixed $requestBody = null, 25 | private readonly ?string $description = null, 26 | private readonly ?Server $server = null, 27 | private readonly array $specificationExtensions = [], 28 | ) { 29 | } 30 | 31 | public function getOperationRef(): ?string 32 | { 33 | return $this->operationRef; 34 | } 35 | 36 | public function getOperationId(): ?string 37 | { 38 | return $this->operationId; 39 | } 40 | 41 | public function getParameters(): ?array 42 | { 43 | return $this->parameters; 44 | } 45 | 46 | public function getRequestBody(): mixed 47 | { 48 | return $this->requestBody; 49 | } 50 | 51 | public function getDescription(): ?string 52 | { 53 | return $this->description; 54 | } 55 | 56 | public function getServer(): ?Server 57 | { 58 | return $this->server; 59 | } 60 | 61 | public function getSpecificationExtensions(): array 62 | { 63 | return $this->specificationExtensions; 64 | } 65 | 66 | public function toArray(): array 67 | { 68 | return array_filter([ 69 | 'operationRef' => $this->getOperationRef(), 70 | 'operationId' => $this->getOperationId(), 71 | 'parameters' => $this->normalizeCollection($this->getParameters()), 72 | 'requestBody' => $this->getRequestBody(), 73 | 'description' => $this->getDescription(), 74 | 'server' => $this->getServer()?->toArray(), 75 | ] + $this->getSpecificationExtensions()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Model/Info.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Info implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly string $title, 22 | private readonly string $version, 23 | private readonly ?string $summary = null, 24 | private readonly ?string $description = null, 25 | private readonly ?string $termsOfService = null, 26 | private readonly ?Contact $contact = null, 27 | private readonly ?License $license = null, 28 | private readonly array $specificationExtensions = [], 29 | ) { 30 | } 31 | 32 | public function getTitle(): string 33 | { 34 | return $this->title; 35 | } 36 | 37 | public function getVersion(): string 38 | { 39 | return $this->version; 40 | } 41 | 42 | public function getSummary(): ?string 43 | { 44 | return $this->summary; 45 | } 46 | 47 | public function getDescription(): ?string 48 | { 49 | return $this->description; 50 | } 51 | 52 | public function getTermsOfService(): ?string 53 | { 54 | return $this->termsOfService; 55 | } 56 | 57 | public function getContact(): ?Contact 58 | { 59 | return $this->contact; 60 | } 61 | 62 | public function getLicense(): ?License 63 | { 64 | return $this->license; 65 | } 66 | 67 | public function getSpecificationExtensions(): array 68 | { 69 | return $this->specificationExtensions; 70 | } 71 | 72 | public function toArray(): array 73 | { 74 | return array_filter([ 75 | 'title' => $this->getTitle(), 76 | 'description' => $this->getDescription(), 77 | 'termsOfService' => $this->getTermsOfService(), 78 | 'contact' => $this->getContact()?->toArray(), 79 | 'license' => $this->getLicense()?->toArray(), 80 | 'version' => $this->getVersion(), 81 | 'summary' => $this->getSummary(), // ?? 82 | ] + $this->getSpecificationExtensions()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Configurator/SecuritySchemeConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\OauthFlows; 13 | use Selency\OpenApi\Model\SecurityScheme; 14 | use Selency\OpenApi\Model\SecuritySchemeIn; 15 | 16 | /** 17 | * @author Titouan Galopin 18 | * @author Selency Team 19 | */ 20 | class SecuritySchemeConfigurator 21 | { 22 | use Traits\DescriptionTrait; 23 | use Traits\ExtensionsTrait; 24 | use Traits\NameTrait; 25 | 26 | private string $type = ''; 27 | private ?SecuritySchemeIn $in = null; 28 | private ?string $scheme = null; 29 | private ?string $bearerFormat = null; 30 | private ?string $openIdConnectUrl = null; 31 | private ?OauthFlows $flows = null; 32 | 33 | public function build(): SecurityScheme 34 | { 35 | return new SecurityScheme( 36 | type: $this->type, 37 | name: $this->name, 38 | in: $this->in, 39 | scheme: $this->scheme, 40 | description: $this->description, 41 | bearerFormat: $this->bearerFormat, 42 | openIdConnectUrl: $this->openIdConnectUrl, 43 | flows: $this->flows, 44 | specificationExtensions: $this->specificationExtensions, 45 | ); 46 | } 47 | 48 | public function type(string $type): static 49 | { 50 | $this->type = $type; 51 | 52 | return $this; 53 | } 54 | 55 | public function in(SecuritySchemeIn $in): static 56 | { 57 | $this->in = $in; 58 | 59 | return $this; 60 | } 61 | 62 | public function scheme(string $scheme): static 63 | { 64 | $this->scheme = $scheme; 65 | 66 | return $this; 67 | } 68 | 69 | public function bearerFormat(string $bearerFormat): static 70 | { 71 | $this->bearerFormat = $bearerFormat; 72 | 73 | return $this; 74 | } 75 | 76 | public function openIdConnectUrl(string $openIdConnectUrl): static 77 | { 78 | $this->openIdConnectUrl = $openIdConnectUrl; 79 | 80 | return $this; 81 | } 82 | 83 | public function flows(OauthFlows $flows): static 84 | { 85 | $this->flows = $flows; 86 | 87 | return $this; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Configurator/InfoConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Contact; 13 | use Selency\OpenApi\Model\Info; 14 | use Selency\OpenApi\Model\License; 15 | 16 | /** 17 | * @author Titouan Galopin 18 | * @author Selency Team 19 | */ 20 | class InfoConfigurator 21 | { 22 | use Traits\DescriptionTrait; 23 | use Traits\ExtensionsTrait; 24 | use Traits\SummaryTrait; 25 | 26 | private string $title = ''; 27 | private string $version = ''; 28 | private ?string $termsOfService = null; 29 | private ?Contact $contact = null; 30 | private ?License $license = null; 31 | 32 | public function build(string $identifier = '', string $version = ''): Info 33 | { 34 | return new Info( 35 | title: $this->title ?: $identifier, 36 | version: $this->version ?: $version, 37 | summary: $this->summary, 38 | description: $this->description, 39 | termsOfService: $this->termsOfService, 40 | contact: $this->contact, 41 | license: $this->license, 42 | specificationExtensions: $this->specificationExtensions, 43 | ); 44 | } 45 | 46 | public function title(string $title): static 47 | { 48 | $this->title = $title; 49 | 50 | return $this; 51 | } 52 | 53 | public function version(string $version): static 54 | { 55 | $this->version = $version; 56 | 57 | return $this; 58 | } 59 | 60 | public function termsOfService(string $termsOfService): static 61 | { 62 | $this->termsOfService = $termsOfService; 63 | 64 | return $this; 65 | } 66 | 67 | public function contact(string $name = null, string $url = null, string $email = null, array $specificationExtensions = []): static 68 | { 69 | $this->contact = new Contact($name, $url, $email, $specificationExtensions); 70 | 71 | return $this; 72 | } 73 | 74 | public function license(string $name, string $identifier = null, string $url = null, array $specificationExtensions = []): static 75 | { 76 | $this->license = new License($name, $identifier, $url, $specificationExtensions); 77 | 78 | return $this; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Configurator/QueryParameterConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Parameter; 13 | use Selency\OpenApi\Model\ParameterIn; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | class QueryParameterConfigurator 20 | { 21 | use Traits\ContentTrait; 22 | use Traits\DeprecatedTrait; 23 | use Traits\DescriptionTrait; 24 | use Traits\ExamplesTrait; 25 | use Traits\ExtensionsTrait; 26 | use Traits\NameTrait; 27 | use Traits\SchemaTrait; 28 | 29 | private ?bool $required = null; 30 | private ?bool $allowEmptyValue = null; 31 | private ?string $style = null; 32 | private ?bool $explode = null; 33 | private ?bool $allowReserved = null; 34 | 35 | public function __construct(string $name) 36 | { 37 | $this->name = $name; 38 | } 39 | 40 | public function build(): Parameter 41 | { 42 | return new Parameter( 43 | $this->name, 44 | ParameterIn::QUERY, 45 | $this->description, 46 | $this->required, 47 | $this->deprecated, 48 | $this->allowEmptyValue, 49 | $this->style, 50 | $this->explode, 51 | $this->allowReserved, 52 | $this->schema, 53 | $this->example, 54 | $this->examples, 55 | $this->content ?: null, 56 | $this->specificationExtensions, 57 | ); 58 | } 59 | 60 | public function required(bool $required): static 61 | { 62 | $this->required = $required; 63 | 64 | return $this; 65 | } 66 | 67 | public function allowEmptyValue(bool $allowEmptyValue): static 68 | { 69 | $this->allowEmptyValue = $allowEmptyValue; 70 | 71 | return $this; 72 | } 73 | 74 | public function style(string $style): static 75 | { 76 | $this->style = $style; 77 | 78 | return $this; 79 | } 80 | 81 | public function explode(bool $explode): static 82 | { 83 | $this->explode = $explode; 84 | 85 | return $this; 86 | } 87 | 88 | public function allowReserved(bool $allowReserved): static 89 | { 90 | $this->allowReserved = $allowReserved; 91 | 92 | return $this; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Model/SecurityScheme.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class SecurityScheme implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | public function __construct( 21 | private readonly string $type, 22 | private readonly ?string $name = null, 23 | private readonly ?SecuritySchemeIn $in = null, 24 | private readonly ?string $scheme = null, 25 | private readonly ?string $description = null, 26 | private readonly ?string $bearerFormat = null, 27 | private readonly ?string $openIdConnectUrl = null, 28 | private readonly ?OauthFlows $flows = null, 29 | private readonly array $specificationExtensions = [], 30 | ) { 31 | } 32 | 33 | public function getType(): string 34 | { 35 | return $this->type; 36 | } 37 | 38 | public function getName(): ?string 39 | { 40 | return $this->name; 41 | } 42 | 43 | public function getIn(): ?SecuritySchemeIn 44 | { 45 | return $this->in; 46 | } 47 | 48 | public function getScheme(): ?string 49 | { 50 | return $this->scheme; 51 | } 52 | 53 | public function getDescription(): ?string 54 | { 55 | return $this->description; 56 | } 57 | 58 | public function getBearerFormat(): ?string 59 | { 60 | return $this->bearerFormat; 61 | } 62 | 63 | public function getOpenIdConnectUrl(): ?string 64 | { 65 | return $this->openIdConnectUrl; 66 | } 67 | 68 | public function getFlows(): ?OauthFlows 69 | { 70 | return $this->flows; 71 | } 72 | 73 | public function getSpecificationExtensions(): array 74 | { 75 | return $this->specificationExtensions; 76 | } 77 | 78 | public function toArray(): array 79 | { 80 | return array_filter([ 81 | 'type' => $this->getType(), 82 | 'description' => $this->getDescription(), 83 | // invalid in 3.1 84 | // 'name' => $this->getName(), 85 | // 'in' => $this->getIn()?->value, 86 | 'scheme' => $this->getScheme(), 87 | 'bearerFormat' => $this->getBearerFormat(), 88 | 'flows' => $this->getFlows()?->toArray(), 89 | 'openIdConnectUrl' => $this->getOpenIdConnectUrl(), 90 | ] + $this->getSpecificationExtensions()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Configurator/ParameterConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Parameter; 13 | use Selency\OpenApi\Model\ParameterIn; 14 | 15 | /** 16 | * @author Titouan Galopin 17 | * @author Selency Team 18 | */ 19 | class ParameterConfigurator 20 | { 21 | use Traits\ContentTrait; 22 | use Traits\DeprecatedTrait; 23 | use Traits\DescriptionTrait; 24 | use Traits\ExamplesTrait; 25 | use Traits\ExtensionsTrait; 26 | use Traits\NameTrait; 27 | use Traits\SchemaTrait; 28 | 29 | private ?ParameterIn $in = null; 30 | private ?bool $required = null; 31 | private ?bool $allowEmptyValue = null; 32 | private ?string $style = null; 33 | private ?bool $explode = null; 34 | private ?bool $allowReserved = null; 35 | 36 | public function __construct(string $name) 37 | { 38 | $this->name = $name; 39 | } 40 | 41 | public function build(bool $asHeader = false): Parameter 42 | { 43 | return new Parameter( 44 | $asHeader ? null : $this->name, 45 | $asHeader ? null : $this->in, 46 | $this->description, 47 | $this->required, 48 | $this->deprecated, 49 | $this->allowEmptyValue, 50 | $this->style, 51 | $this->explode, 52 | $this->allowReserved, 53 | $this->schema, 54 | $this->example, 55 | $this->examples, 56 | $this->content ?: null, 57 | $this->specificationExtensions, 58 | ); 59 | } 60 | 61 | public function in(ParameterIn $in): static 62 | { 63 | $this->in = $in; 64 | 65 | return $this; 66 | } 67 | 68 | public function required(bool $required): static 69 | { 70 | $this->required = $required; 71 | 72 | return $this; 73 | } 74 | 75 | public function allowEmptyValue(bool $allowEmptyValue): static 76 | { 77 | $this->allowEmptyValue = $allowEmptyValue; 78 | 79 | return $this; 80 | } 81 | 82 | public function style(string $style): static 83 | { 84 | $this->style = $style; 85 | 86 | return $this; 87 | } 88 | 89 | public function explode(bool $explode): static 90 | { 91 | $this->explode = $explode; 92 | 93 | return $this; 94 | } 95 | 96 | public function allowReserved(bool $allowReserved): static 97 | { 98 | $this->allowReserved = $allowReserved; 99 | 100 | return $this; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Builder/OpenApiBuilderInterface.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Builder; 11 | 12 | use Selency\OpenApi\Configurator\CallbackRequestConfigurator; 13 | use Selency\OpenApi\Configurator\ComponentsConfigurator; 14 | use Selency\OpenApi\Configurator\EncodingConfigurator; 15 | use Selency\OpenApi\Configurator\ExampleConfigurator; 16 | use Selency\OpenApi\Configurator\InfoConfigurator; 17 | use Selency\OpenApi\Configurator\LinkConfigurator; 18 | use Selency\OpenApi\Configurator\MediaTypeConfigurator; 19 | use Selency\OpenApi\Configurator\OperationConfigurator; 20 | use Selency\OpenApi\Configurator\ParameterConfigurator; 21 | use Selency\OpenApi\Configurator\PathItemConfigurator; 22 | use Selency\OpenApi\Configurator\QueryParameterConfigurator; 23 | use Selency\OpenApi\Configurator\QueryParametersConfigurator; 24 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 25 | use Selency\OpenApi\Configurator\RequestBodyConfigurator; 26 | use Selency\OpenApi\Configurator\ResponseConfigurator; 27 | use Selency\OpenApi\Configurator\ResponsesConfigurator; 28 | use Selency\OpenApi\Configurator\SchemaConfigurator; 29 | use Selency\OpenApi\Configurator\SecuritySchemeConfigurator; 30 | use Selency\OpenApi\Configurator\ServerConfigurator; 31 | use Selency\OpenApi\Configurator\TagConfigurator; 32 | 33 | /** 34 | * @author Titouan Galopin 35 | * @author Selency Team 36 | */ 37 | interface OpenApiBuilderInterface 38 | { 39 | public function schema(SchemaConfigurator|ReferenceConfigurator|string $definition = null): SchemaConfigurator|ReferenceConfigurator; 40 | 41 | public function callbackRequest(): CallbackRequestConfigurator; 42 | 43 | public function components(): ComponentsConfigurator; 44 | 45 | public function content(): MediaTypeConfigurator; 46 | 47 | public function encoding(): EncodingConfigurator; 48 | 49 | public function example(): ExampleConfigurator; 50 | 51 | public function info(): InfoConfigurator; 52 | 53 | public function link(): LinkConfigurator; 54 | 55 | public function mediaType(): MediaTypeConfigurator; 56 | 57 | public function operation(): OperationConfigurator; 58 | 59 | public function parameter(string $name): ParameterConfigurator; 60 | 61 | public function queryParameter(string $name): QueryParameterConfigurator; 62 | 63 | public function queryParameters(): QueryParametersConfigurator; 64 | 65 | public function pathItem(): PathItemConfigurator; 66 | 67 | public function reference(string $ref): ReferenceConfigurator; 68 | 69 | public function requestBody(): RequestBodyConfigurator; 70 | 71 | public function response(): ResponseConfigurator; 72 | 73 | public function responses(): ResponsesConfigurator; 74 | 75 | public function securityScheme(): SecuritySchemeConfigurator; 76 | 77 | public function server(string $url): ServerConfigurator; 78 | 79 | public function tag(): TagConfigurator; 80 | } 81 | -------------------------------------------------------------------------------- /src/Configurator/DocumentationConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Components; 13 | use Selency\OpenApi\Model\OpenApi; 14 | use Selency\OpenApi\Model\PathItem; 15 | use Selency\OpenApi\Model\Reference; 16 | use Selency\OpenApi\Model\SecurityRequirement; 17 | use Selency\OpenApi\Model\Tag; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | * @author Selency Team 22 | */ 23 | class DocumentationConfigurator 24 | { 25 | use Traits\ExtensionsTrait; 26 | use Traits\ExternalDocsTrait; 27 | use Traits\ServersTrait; 28 | 29 | private string $version = '3.1.0'; 30 | private ?string $jsonSchemaDialect = null; 31 | private ?InfoConfigurator $info = null; 32 | private ?Components $components = null; 33 | 34 | /** 35 | * @var Tag[] 36 | */ 37 | private array $tags = []; 38 | 39 | /** 40 | * @var array 41 | */ 42 | private array $paths = []; 43 | 44 | /** 45 | * @var array 46 | */ 47 | private array $webhooks = []; 48 | 49 | /** 50 | * @var SecurityRequirement[] 51 | */ 52 | private array $securityRequirements = []; 53 | 54 | public function build(string $identifier = '', string $version = ''): OpenApi 55 | { 56 | return new OpenApi( 57 | version: $this->version, 58 | info: ($this->info ?: new InfoConfigurator())->build($identifier, $version), 59 | servers: $this->servers ?: null, 60 | paths: $this->paths ?: null, 61 | webhooks: $this->webhooks ?: null, 62 | components: $this->components, 63 | security: $this->securityRequirements, 64 | tags: $this->tags ?: null, 65 | externalDocs: $this->externalDocs, 66 | jsonSchemaDialect: $this->jsonSchemaDialect, 67 | specificationExtensions: $this->specificationExtensions, 68 | ); 69 | } 70 | 71 | public function version(string $version): static 72 | { 73 | $this->version = $version; 74 | 75 | return $this; 76 | } 77 | 78 | public function jsonSchemaDialect(string $jsonSchemaDialect): static 79 | { 80 | $this->jsonSchemaDialect = $jsonSchemaDialect; 81 | 82 | return $this; 83 | } 84 | 85 | public function info(InfoConfigurator $info): static 86 | { 87 | $this->info = $info; 88 | 89 | return $this; 90 | } 91 | 92 | public function components(ComponentsConfigurator $components): static 93 | { 94 | $this->components = $components->build($this->components); 95 | 96 | return $this; 97 | } 98 | 99 | public function tag(TagConfigurator|string $tag): static 100 | { 101 | $this->tags[] = \is_string($tag) ? new Tag($tag) : $tag->build(); 102 | 103 | return $this; 104 | } 105 | 106 | public function path(string $path, PathItemConfigurator|ReferenceConfigurator $pathItem): static 107 | { 108 | $this->paths[$path] = $pathItem->build($this->paths[$path] ?? null); 109 | 110 | return $this; 111 | } 112 | 113 | public function webhook(string $name, PathItemConfigurator|ReferenceConfigurator $pathItem): static 114 | { 115 | $this->webhooks[$name] = $pathItem->build(); 116 | 117 | return $this; 118 | } 119 | 120 | public function securityRequirement(string $name, array $config = []): static 121 | { 122 | $this->securityRequirements[] = new SecurityRequirement($name, $config); 123 | 124 | return $this; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Configurator/PathItemConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Model\Operation; 14 | use Selency\OpenApi\Model\PathItem; 15 | 16 | /** 17 | * @author Titouan Galopin 18 | * @author Selency Team 19 | */ 20 | class PathItemConfigurator 21 | { 22 | use Traits\DescriptionTrait; 23 | use Traits\ExtensionsTrait; 24 | use Traits\ParametersTrait; 25 | use Traits\QueryParametersTrait; 26 | use Traits\ServersTrait; 27 | use Traits\SummaryTrait; 28 | 29 | private ?string $ref = null; 30 | private ?Operation $get = null; 31 | private ?Operation $post = null; 32 | private ?Operation $put = null; 33 | private ?Operation $patch = null; 34 | private ?Operation $delete = null; 35 | private ?Operation $head = null; 36 | private ?Operation $options = null; 37 | private ?Operation $trace = null; 38 | 39 | public function __construct(OpenApiBuilderInterface $openApiBuilder) 40 | { 41 | $this->openApiBuilder = $openApiBuilder; 42 | } 43 | 44 | public function build(PathItem $toMergeWith = null): PathItem 45 | { 46 | return new PathItem( 47 | ref: $this->ref ?: $toMergeWith?->getRef(), 48 | summary: $this->summary ?: $toMergeWith?->getSummary(), 49 | description: $this->description ?: $toMergeWith?->getDescription(), 50 | get: $this->get ?: $toMergeWith?->getGet(), 51 | put: $this->put ?: $toMergeWith?->getPut(), 52 | post: $this->post ?: $toMergeWith?->getPost(), 53 | patch: $this->patch ?: $toMergeWith?->getPatch(), 54 | delete: $this->delete ?: $toMergeWith?->getDelete(), 55 | head: $this->head ?: $toMergeWith?->getHead(), 56 | options: $this->options ?: $toMergeWith?->getOptions(), 57 | trace: $this->trace ?: $toMergeWith?->getTrace(), 58 | servers: $this->servers ?: $toMergeWith?->getServers() ?: null, 59 | parameters: array_merge($this->parameters, $this->queryParameters) ?: $toMergeWith?->getParameters() ?: null, 60 | specificationExtensions: $this->specificationExtensions ?: $toMergeWith?->getSpecificationExtensions() ?: [], 61 | ); 62 | } 63 | 64 | public function ref(string $ref): static 65 | { 66 | $this->ref = $ref; 67 | 68 | return $this; 69 | } 70 | 71 | public function get(OperationConfigurator $get): static 72 | { 73 | $this->get = $get->build(); 74 | 75 | return $this; 76 | } 77 | 78 | public function post(OperationConfigurator $post): static 79 | { 80 | $this->post = $post->build(); 81 | 82 | return $this; 83 | } 84 | 85 | public function put(OperationConfigurator $put): static 86 | { 87 | $this->put = $put->build(); 88 | 89 | return $this; 90 | } 91 | 92 | public function patch(OperationConfigurator $patch): static 93 | { 94 | $this->patch = $patch->build(); 95 | 96 | return $this; 97 | } 98 | 99 | public function delete(OperationConfigurator $delete): static 100 | { 101 | $this->delete = $delete->build(); 102 | 103 | return $this; 104 | } 105 | 106 | public function head(OperationConfigurator $head): static 107 | { 108 | $this->head = $head->build(); 109 | 110 | return $this; 111 | } 112 | 113 | public function options(OperationConfigurator $options): static 114 | { 115 | $this->options = $options->build(); 116 | 117 | return $this; 118 | } 119 | 120 | public function trace(OperationConfigurator $trace): static 121 | { 122 | $this->trace = $trace->build(); 123 | 124 | return $this; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Model/PathItem.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class PathItem implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param Server[]|null $servers 22 | * @param array|null $parameters 23 | */ 24 | public function __construct( 25 | private readonly ?string $ref = null, 26 | private readonly ?string $summary = null, 27 | private readonly ?string $description = null, 28 | private readonly ?Operation $get = null, 29 | private readonly ?Operation $put = null, 30 | private readonly ?Operation $post = null, 31 | private readonly ?Operation $patch = null, 32 | private readonly ?Operation $delete = null, 33 | private readonly ?Operation $head = null, 34 | private readonly ?Operation $options = null, 35 | private readonly ?Operation $trace = null, 36 | private readonly ?array $servers = null, 37 | private readonly ?array $parameters = null, 38 | private readonly array $specificationExtensions = [], 39 | ) { 40 | } 41 | 42 | public function getRef(): ?string 43 | { 44 | return $this->ref; 45 | } 46 | 47 | public function getSummary(): ?string 48 | { 49 | return $this->summary; 50 | } 51 | 52 | public function getDescription(): ?string 53 | { 54 | return $this->description; 55 | } 56 | 57 | public function getGet(): ?Operation 58 | { 59 | return $this->get; 60 | } 61 | 62 | public function getPut(): ?Operation 63 | { 64 | return $this->put; 65 | } 66 | 67 | public function getPost(): ?Operation 68 | { 69 | return $this->post; 70 | } 71 | 72 | public function getPatch(): ?Operation 73 | { 74 | return $this->patch; 75 | } 76 | 77 | public function getDelete(): ?Operation 78 | { 79 | return $this->delete; 80 | } 81 | 82 | public function getHead(): ?Operation 83 | { 84 | return $this->head; 85 | } 86 | 87 | public function getOptions(): ?Operation 88 | { 89 | return $this->options; 90 | } 91 | 92 | public function getTrace(): ?Operation 93 | { 94 | return $this->trace; 95 | } 96 | 97 | /** 98 | * @return Server[]|null 99 | */ 100 | public function getServers(): ?array 101 | { 102 | return $this->servers; 103 | } 104 | 105 | /** 106 | * @return array|null 107 | */ 108 | public function getParameters(): ?array 109 | { 110 | return $this->parameters; 111 | } 112 | 113 | public function getSpecificationExtensions(): array 114 | { 115 | return $this->specificationExtensions; 116 | } 117 | 118 | public function toArray(): array 119 | { 120 | return array_filter([ 121 | '$ref' => $this->getRef(), 122 | 'summary' => $this->getSummary(), 123 | 'description' => $this->getDescription(), 124 | 'get' => $this->getGet()?->toArray(), 125 | 'put' => $this->getPut()?->toArray(), 126 | 'post' => $this->getPost()?->toArray(), 127 | 'delete' => $this->getDelete()?->toArray(), 128 | 'options' => $this->getOptions()?->toArray(), 129 | 'head' => $this->getHead()?->toArray(), 130 | 'patch' => $this->getPatch()?->toArray(), 131 | 'trace' => $this->getTrace()?->toArray(), 132 | 'servers' => $this->normalizeCollection($this->getServers()), 133 | 'parameters' => $this->normalizeCollection($this->getParameters()), 134 | ] + $this->getSpecificationExtensions()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Model/OpenApi.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | use Selency\OpenApi\Configurator\InfoConfigurator; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class OpenApi implements OpenApiModel 19 | { 20 | use OpenApiTrait; 21 | 22 | /** 23 | * @param Server[]|null $servers 24 | * @param array|null $paths 25 | * @param array|null $webhooks 26 | * @param SecurityRequirement[]|null $security 27 | * @param Tag[]|null $tags 28 | */ 29 | public function __construct( 30 | private readonly string $version, 31 | private readonly Info $info, 32 | private readonly ?array $servers = null, 33 | private readonly ?array $paths = null, 34 | private readonly ?array $webhooks = null, 35 | private readonly ?Components $components = null, 36 | private readonly ?array $security = null, 37 | private readonly ?array $tags = null, 38 | private readonly ?ExternalDocumentation $externalDocs = null, 39 | private readonly ?string $jsonSchemaDialect = null, 40 | private readonly array $specificationExtensions = [], 41 | ) { 42 | } 43 | 44 | public function getVersion(): string 45 | { 46 | return $this->version; 47 | } 48 | 49 | public function getInfo(): Info 50 | { 51 | return $this->info; 52 | } 53 | 54 | /** 55 | * @return Server[]|null 56 | */ 57 | public function getServers(): ?array 58 | { 59 | return $this->servers; 60 | } 61 | 62 | /** 63 | * @return array|null 64 | */ 65 | public function getPaths(): ?array 66 | { 67 | return $this->paths; 68 | } 69 | 70 | /** 71 | * @return array|null 72 | */ 73 | public function getWebhooks(): ?array 74 | { 75 | return $this->webhooks; 76 | } 77 | 78 | public function getComponents(): ?Components 79 | { 80 | return $this->components; 81 | } 82 | 83 | /** 84 | * @return SecurityRequirement[]|null 85 | */ 86 | public function getSecurity(): ?array 87 | { 88 | return $this->security; 89 | } 90 | 91 | /** 92 | * @return Tag[]|null 93 | */ 94 | public function getTags(): ?array 95 | { 96 | return $this->tags; 97 | } 98 | 99 | public function getExternalDocs(): ?ExternalDocumentation 100 | { 101 | return $this->externalDocs; 102 | } 103 | 104 | public function getJsonSchemaDialect(): ?string 105 | { 106 | return $this->jsonSchemaDialect; 107 | } 108 | 109 | public function getSpecificationExtensions(): array 110 | { 111 | return $this->specificationExtensions; 112 | } 113 | 114 | public function toArray(): array 115 | { 116 | $exported = array_filter([ 117 | 'openapi' => $this->version, 118 | 'info' => $this->info->toArray() ?: (new InfoConfigurator())->build()->toArray(), 119 | 'servers' => $this->normalizeCollection($this->servers), 120 | 'paths' => $this->normalizeCollection($this->paths), 121 | 'components' => $this->components->toArray(), 122 | 'tags' => $this->normalizeCollection($this->tags), 123 | 'externalDocs' => $this->externalDocs?->toArray(), 124 | 'jsonSchemaDialect' => $this->jsonSchemaDialect, 125 | 'webhooks' => $this->normalizeCollection($this->webhooks), 126 | ] + $this->getSpecificationExtensions()); 127 | 128 | $exported += ['security' => $this->normalizeCollection($this->security)]; 129 | 130 | return $exported; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Model/Parameter.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Parameter implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $examples 22 | * @param array|null $content 23 | */ 24 | public function __construct( 25 | private readonly ?string $name = null, 26 | private readonly ?ParameterIn $in = null, 27 | private readonly ?string $description = null, 28 | private readonly ?bool $required = null, 29 | private readonly ?bool $deprecated = null, 30 | private readonly ?bool $allowEmptyValue = null, 31 | private readonly ?string $style = null, 32 | private readonly ?bool $explode = null, 33 | private readonly ?bool $allowReserved = null, 34 | private readonly ?Schema $schema = null, 35 | private readonly mixed $example = null, 36 | private readonly ?array $examples = null, 37 | private readonly ?array $content = null, 38 | private readonly array $specificationExtensions = [], 39 | ) { 40 | } 41 | 42 | public function getName(): ?string 43 | { 44 | return $this->name; 45 | } 46 | 47 | public function getIn(): ?ParameterIn 48 | { 49 | return $this->in; 50 | } 51 | 52 | public function getDescription(): ?string 53 | { 54 | return $this->description; 55 | } 56 | 57 | public function getRequired(): ?bool 58 | { 59 | return $this->required; 60 | } 61 | 62 | public function getDeprecated(): ?bool 63 | { 64 | return $this->deprecated; 65 | } 66 | 67 | public function getAllowEmptyValue(): ?bool 68 | { 69 | return $this->allowEmptyValue; 70 | } 71 | 72 | public function getStyle(): ?string 73 | { 74 | return $this->style; 75 | } 76 | 77 | public function getExplode(): ?bool 78 | { 79 | return $this->explode; 80 | } 81 | 82 | public function getAllowReserved(): ?bool 83 | { 84 | return $this->allowReserved; 85 | } 86 | 87 | public function getSchema(): ?Schema 88 | { 89 | return $this->schema; 90 | } 91 | 92 | public function getExample(): mixed 93 | { 94 | return $this->example; 95 | } 96 | 97 | /** 98 | * @return array|null 99 | */ 100 | public function getExamples(): ?array 101 | { 102 | return $this->examples; 103 | } 104 | 105 | /** 106 | * @return array|null 107 | */ 108 | public function getContent(): ?array 109 | { 110 | return $this->content; 111 | } 112 | 113 | public function getSpecificationExtensions(): array 114 | { 115 | return $this->specificationExtensions; 116 | } 117 | 118 | public function toArray(): array 119 | { 120 | return array_filter([ 121 | 'name' => $this->getName(), 122 | 'in' => $this->getIn()?->value, 123 | 'description' => $this->getDescription(), 124 | 'required' => $this->getRequired(), 125 | 'deprecated' => $this->getDeprecated(), 126 | 'allowEmptyValue' => $this->getAllowEmptyValue(), 127 | 'style' => $this->getStyle(), 128 | 'explode' => $this->getExplode(), 129 | 'allowReserved' => $this->getAllowReserved(), 130 | 'schema' => $this->getSchema()?->toArray(), 131 | 'example' => $this->getExample(), 132 | 'examples' => $this->normalizeCollection($this->getExamples()), 133 | 'content' => $this->normalizeCollection($this->getContent()), 134 | ] + $this->getSpecificationExtensions()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Configurator/ComponentsConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Components; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | * @author Selency Team 17 | */ 18 | class ComponentsConfigurator 19 | { 20 | use Traits\ExtensionsTrait; 21 | 22 | private array $schemas = []; 23 | private array $responses = []; 24 | private array $parameters = []; 25 | private array $examples = []; 26 | private array $requestBodies = []; 27 | private array $securitySchemes = []; 28 | private array $links = []; 29 | private array $callbacks = []; 30 | private array $pathItems = []; 31 | 32 | public function build(Components $toMergeWith = null): Components 33 | { 34 | if (!$toMergeWith) { 35 | $toMergeWith = new Components(); 36 | } 37 | 38 | return new Components( 39 | array_merge($toMergeWith->getSchemas() ?: [], $this->schemas) ?: null, 40 | array_merge($toMergeWith->getResponses() ?: [], $this->responses) ?: null, 41 | array_merge($toMergeWith->getParameters() ?: [], $this->parameters) ?: null, 42 | array_merge($toMergeWith->getExamples() ?: [], $this->examples) ?: null, 43 | array_merge($toMergeWith->getRequestBodies() ?: [], $this->requestBodies) ?: null, 44 | array_merge($toMergeWith->getSecuritySchemes() ?: [], $this->securitySchemes) ?: null, 45 | array_merge($toMergeWith->getLinks() ?: [], $this->links) ?: null, 46 | array_merge($toMergeWith->getCallbacks() ?: [], $this->callbacks) ?: null, 47 | array_merge($toMergeWith->getPathItems() ?: [], $this->pathItems) ?: null, 48 | array_merge($toMergeWith->getSpecificationExtensions() ?: [], $this->specificationExtensions) ?: [], 49 | ); 50 | } 51 | 52 | /** 53 | * @return $this 54 | */ 55 | public function schema(string $name, SchemaConfigurator|string $schema): static 56 | { 57 | $this->schemas[ReferenceConfigurator::normalize($name)] = SchemaConfigurator::createFromDefinition($schema)->build(); 58 | 59 | return $this; 60 | } 61 | 62 | public function response(string $name, ResponseConfigurator $response): static 63 | { 64 | $this->responses[ReferenceConfigurator::normalize($name)] = $response->build(); 65 | 66 | return $this; 67 | } 68 | 69 | public function parameter(string $name, ParameterConfigurator $parameter): static 70 | { 71 | $this->parameters[ReferenceConfigurator::normalize($name)] = $parameter->build(); 72 | 73 | return $this; 74 | } 75 | 76 | public function example(string $name, ExampleConfigurator $example): static 77 | { 78 | $this->examples[ReferenceConfigurator::normalize($name)] = $example->build(); 79 | 80 | return $this; 81 | } 82 | 83 | public function requestBody(string $name, RequestBodyConfigurator $requestBody): static 84 | { 85 | $this->requestBodies[ReferenceConfigurator::normalize($name)] = $requestBody->build(); 86 | 87 | return $this; 88 | } 89 | 90 | public function securityScheme(string $name, SecuritySchemeConfigurator $securityScheme): static 91 | { 92 | $this->securitySchemes[ReferenceConfigurator::normalize($name)] = $securityScheme->build(); 93 | 94 | return $this; 95 | } 96 | 97 | public function callback(string $name, CallbackRequestConfigurator $callback): static 98 | { 99 | $this->callbacks[ReferenceConfigurator::normalize($name)] = $callback->build(); 100 | 101 | return $this; 102 | } 103 | 104 | public function link(string $name, LinkConfigurator $link): static 105 | { 106 | $this->links[ReferenceConfigurator::normalize($name)] = $link->build(); 107 | 108 | return $this; 109 | } 110 | 111 | public function pathItem(string $name, PathItemConfigurator $pathItem): static 112 | { 113 | $reference = ReferenceConfigurator::normalize($name); 114 | $this->pathItems[$reference] = $pathItem->build($this->pathItems[$reference] ?? null); 115 | 116 | return $this; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Configurator/OperationConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Builder\OpenApiBuilderInterface; 13 | use Selency\OpenApi\Model\CallbackRequest; 14 | use Selency\OpenApi\Model\Operation; 15 | use Selency\OpenApi\Model\Reference; 16 | use Selency\OpenApi\Model\RequestBody; 17 | use Selency\OpenApi\Model\Responses; 18 | use Selency\OpenApi\Model\SecurityRequirement; 19 | 20 | /** 21 | * @author Titouan Galopin 22 | * @author Selency Team 23 | */ 24 | class OperationConfigurator 25 | { 26 | use Traits\DeprecatedTrait; 27 | use Traits\DescriptionTrait; 28 | use Traits\ExtensionsTrait; 29 | use Traits\ExternalDocsTrait; 30 | use Traits\ParametersTrait; 31 | use Traits\QueryParametersTrait; 32 | use Traits\ServersTrait; 33 | use Traits\SummaryTrait; 34 | 35 | private ?string $operationId = null; 36 | 37 | /** 38 | * @var string[] 39 | */ 40 | private array $tags = []; 41 | 42 | private RequestBody|Reference|null $requestBody = null; 43 | private Responses|null $responses = null; 44 | 45 | /** 46 | * @var array 47 | */ 48 | private array $callbacks = []; 49 | 50 | /** 51 | * @var SecurityRequirement[]|null 52 | */ 53 | private ?array $securityRequirements = null; 54 | 55 | public function __construct(OpenApiBuilderInterface $openApiBuilder) 56 | { 57 | $this->openApiBuilder = $openApiBuilder; 58 | } 59 | 60 | public function build(): Operation 61 | { 62 | return new Operation( 63 | operationId: $this->operationId, 64 | summary: $this->summary, 65 | description: $this->description, 66 | deprecated: $this->deprecated, 67 | tags: $this->tags ?: null, 68 | externalDocs: $this->externalDocs, 69 | parameters: array_merge($this->parameters, $this->queryParameters) ?: null, 70 | requestBody: $this->requestBody, 71 | responses: $this->responses, 72 | callbacks: $this->callbacks ?: null, 73 | security: $this->securityRequirements, 74 | servers: $this->servers ?: null, 75 | specificationExtensions: $this->specificationExtensions, 76 | ); 77 | } 78 | 79 | public function operationId(string $operationId): static 80 | { 81 | $this->operationId = $operationId; 82 | 83 | return $this; 84 | } 85 | 86 | public function tag(string $tag): static 87 | { 88 | $this->tags[] = $tag; 89 | 90 | return $this; 91 | } 92 | 93 | public function responses(ResponsesConfigurator $responses): static 94 | { 95 | $this->responses = $responses->build(); 96 | 97 | return $this; 98 | } 99 | 100 | public function requestBody(RequestBodyConfigurator|ReferenceConfigurator|string $requestBody): static 101 | { 102 | if (\is_string($requestBody)) { 103 | $requestBody = new ReferenceConfigurator('#/components/requestBodies/'.ReferenceConfigurator::normalize($requestBody)); 104 | } 105 | 106 | $this->requestBody = $requestBody->build(); 107 | 108 | return $this; 109 | } 110 | 111 | public function callback(string $name, CallbackRequestConfigurator|ReferenceConfigurator|string $callbackRequest): static 112 | { 113 | if (\is_string($callbackRequest)) { 114 | $callbackRequest = new ReferenceConfigurator('#/components/callbacks/'.ReferenceConfigurator::normalize($callbackRequest)); 115 | } 116 | 117 | $this->callbacks[$name] = $callbackRequest->build(); 118 | 119 | return $this; 120 | } 121 | 122 | public function securityRequirement(?string $name, array $config = []): static 123 | { 124 | // Allow not having security 125 | if (null === $name) { 126 | $this->securityRequirements[] = new SecurityRequirement(SecurityRequirement::NONE, []); 127 | 128 | return $this; 129 | } 130 | 131 | $this->securityRequirements[] = new SecurityRequirement($name, $config); 132 | 133 | return $this; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Model/Components.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Components implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $schemas 22 | * @param array|null $responses 23 | * @param array|null $parameters 24 | * @param array|null $examples 25 | * @param array|null $requestBodies 26 | * @param array|null $securitySchemes 27 | * @param array|null $links 28 | * @param array|null $callbacks 29 | * @param array|null $pathItems 30 | */ 31 | public function __construct( 32 | private readonly ?array $schemas = null, 33 | private readonly ?array $responses = null, 34 | private readonly ?array $parameters = null, 35 | private readonly ?array $examples = null, 36 | private readonly ?array $requestBodies = null, 37 | private readonly ?array $securitySchemes = null, 38 | private readonly ?array $links = null, 39 | private readonly ?array $callbacks = null, 40 | private readonly ?array $pathItems = null, 41 | private readonly array $specificationExtensions = [], 42 | ) { 43 | } 44 | 45 | /** 46 | * @return array|null 47 | */ 48 | public function getSchemas(): ?array 49 | { 50 | return $this->schemas; 51 | } 52 | 53 | /** 54 | * @return array|null 55 | */ 56 | public function getResponses(): ?array 57 | { 58 | return $this->responses; 59 | } 60 | 61 | /** 62 | * @return array|null 63 | */ 64 | public function getParameters(): ?array 65 | { 66 | return $this->parameters; 67 | } 68 | 69 | /** 70 | * @return array|null 71 | */ 72 | public function getExamples(): ?array 73 | { 74 | return $this->examples; 75 | } 76 | 77 | /** 78 | * @return array|null 79 | */ 80 | public function getRequestBodies(): ?array 81 | { 82 | return $this->requestBodies; 83 | } 84 | 85 | /** 86 | * @return array|null 87 | */ 88 | public function getSecuritySchemes(): ?array 89 | { 90 | return $this->securitySchemes; 91 | } 92 | 93 | /** 94 | * @return array|null 95 | */ 96 | public function getLinks(): ?array 97 | { 98 | return $this->links; 99 | } 100 | 101 | /** 102 | * @return array|null 103 | */ 104 | public function getCallbacks(): ?array 105 | { 106 | return $this->callbacks; 107 | } 108 | 109 | /** 110 | * @return array|null 111 | */ 112 | public function getPathItems(): ?array 113 | { 114 | return $this->pathItems; 115 | } 116 | 117 | public function getSpecificationExtensions(): array 118 | { 119 | return $this->specificationExtensions; 120 | } 121 | 122 | public function toArray(): array 123 | { 124 | return array_filter([ 125 | 'schemas' => $this->normalizeCollection($this->getSchemas()), 126 | 'responses' => $this->normalizeCollection($this->getResponses()), 127 | 'parameters' => $this->normalizeCollection($this->getParameters()), 128 | 'examples' => $this->normalizeCollection($this->getExamples()), 129 | 'requestBodies' => $this->normalizeCollection($this->getRequestBodies()), 130 | 'securitySchemes' => $this->normalizeCollection($this->getSecuritySchemes()), 131 | 'links' => $this->normalizeCollection($this->getLinks()), 132 | 'callbacks' => $this->normalizeCollection($this->getCallbacks()), 133 | 'pathItems' => $this->normalizeCollection($this->getPathItems()), // ?? 134 | ] + $this->getSpecificationExtensions()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Builder/OpenApiBuilder.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Builder; 11 | 12 | use Selency\OpenApi\Configurator\CallbackRequestConfigurator; 13 | use Selency\OpenApi\Configurator\ComponentsConfigurator; 14 | use Selency\OpenApi\Configurator\EncodingConfigurator; 15 | use Selency\OpenApi\Configurator\ExampleConfigurator; 16 | use Selency\OpenApi\Configurator\InfoConfigurator; 17 | use Selency\OpenApi\Configurator\LinkConfigurator; 18 | use Selency\OpenApi\Configurator\MediaTypeConfigurator; 19 | use Selency\OpenApi\Configurator\OperationConfigurator; 20 | use Selency\OpenApi\Configurator\ParameterConfigurator; 21 | use Selency\OpenApi\Configurator\PathItemConfigurator; 22 | use Selency\OpenApi\Configurator\QueryParameterConfigurator; 23 | use Selency\OpenApi\Configurator\QueryParametersConfigurator; 24 | use Selency\OpenApi\Configurator\ReferenceConfigurator; 25 | use Selency\OpenApi\Configurator\RequestBodyConfigurator; 26 | use Selency\OpenApi\Configurator\ResponseConfigurator; 27 | use Selency\OpenApi\Configurator\ResponsesConfigurator; 28 | use Selency\OpenApi\Configurator\SchemaConfigurator; 29 | use Selency\OpenApi\Configurator\SecuritySchemeConfigurator; 30 | use Selency\OpenApi\Configurator\ServerConfigurator; 31 | use Selency\OpenApi\Configurator\TagConfigurator; 32 | 33 | /** 34 | * @author Titouan Galopin 35 | * @author Selency Team 36 | */ 37 | class OpenApiBuilder implements OpenApiBuilderInterface 38 | { 39 | public function schema(SchemaConfigurator|ReferenceConfigurator|string $definition = null): SchemaConfigurator|ReferenceConfigurator 40 | { 41 | return SchemaConfigurator::createFromDefinition($definition); 42 | } 43 | 44 | public function callbackRequest(): CallbackRequestConfigurator 45 | { 46 | return new CallbackRequestConfigurator(); 47 | } 48 | 49 | public function components(): ComponentsConfigurator 50 | { 51 | return new ComponentsConfigurator(); 52 | } 53 | 54 | public function content(): MediaTypeConfigurator 55 | { 56 | return new MediaTypeConfigurator(); 57 | } 58 | 59 | public function encoding(): EncodingConfigurator 60 | { 61 | return new EncodingConfigurator(); 62 | } 63 | 64 | public function example(): ExampleConfigurator 65 | { 66 | return new ExampleConfigurator(); 67 | } 68 | 69 | public function info(): InfoConfigurator 70 | { 71 | return new InfoConfigurator(); 72 | } 73 | 74 | public function link(): LinkConfigurator 75 | { 76 | return new LinkConfigurator(); 77 | } 78 | 79 | public function mediaType(): MediaTypeConfigurator 80 | { 81 | return new MediaTypeConfigurator(); 82 | } 83 | 84 | public function operation(): OperationConfigurator 85 | { 86 | return new OperationConfigurator($this); 87 | } 88 | 89 | public function parameter(string $name): ParameterConfigurator 90 | { 91 | return new ParameterConfigurator($name); 92 | } 93 | 94 | public function queryParameter(string $name): QueryParameterConfigurator 95 | { 96 | return new QueryParameterConfigurator($name); 97 | } 98 | 99 | public function queryParameters(): QueryParametersConfigurator 100 | { 101 | return new QueryParametersConfigurator($this); 102 | } 103 | 104 | public function pathItem(): PathItemConfigurator 105 | { 106 | return new PathItemConfigurator($this); 107 | } 108 | 109 | public function reference(string $ref): ReferenceConfigurator 110 | { 111 | return new ReferenceConfigurator($ref); 112 | } 113 | 114 | public function requestBody(): RequestBodyConfigurator 115 | { 116 | return new RequestBodyConfigurator(); 117 | } 118 | 119 | public function response(): ResponseConfigurator 120 | { 121 | return new ResponseConfigurator(); 122 | } 123 | 124 | public function responses(): ResponsesConfigurator 125 | { 126 | return new ResponsesConfigurator(); 127 | } 128 | 129 | public function securityScheme(): SecuritySchemeConfigurator 130 | { 131 | return new SecuritySchemeConfigurator(); 132 | } 133 | 134 | public function server(string $url): ServerConfigurator 135 | { 136 | return new ServerConfigurator($url); 137 | } 138 | 139 | public function tag(): TagConfigurator 140 | { 141 | return new TagConfigurator(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Model/Operation.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Operation implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param string[]|null $tags 22 | * @param array|null $parameters 23 | * @param array|null $callbacks 24 | * @param SecurityRequirement[]|null $security 25 | * @param Server[]|null $servers 26 | */ 27 | public function __construct( 28 | private readonly ?string $operationId = null, 29 | private readonly ?string $summary = null, 30 | private readonly ?string $description = null, 31 | private readonly ?bool $deprecated = null, 32 | private readonly ?array $tags = null, 33 | private readonly ?ExternalDocumentation $externalDocs = null, 34 | private readonly ?array $parameters = null, 35 | private readonly RequestBody|Reference|null $requestBody = null, 36 | private readonly ?Responses $responses = null, 37 | private readonly ?array $callbacks = null, 38 | private readonly ?array $security = null, 39 | private readonly ?array $servers = null, 40 | private readonly array $specificationExtensions = [], 41 | ) { 42 | } 43 | 44 | public function getOperationId(): ?string 45 | { 46 | return $this->operationId; 47 | } 48 | 49 | public function getSummary(): ?string 50 | { 51 | return $this->summary; 52 | } 53 | 54 | public function getDescription(): ?string 55 | { 56 | return $this->description; 57 | } 58 | 59 | public function getDeprecated(): ?bool 60 | { 61 | return $this->deprecated; 62 | } 63 | 64 | /** 65 | * @return string[]|null 66 | */ 67 | public function getTags(): ?array 68 | { 69 | return $this->tags; 70 | } 71 | 72 | public function getExternalDocs(): ?ExternalDocumentation 73 | { 74 | return $this->externalDocs; 75 | } 76 | 77 | /** 78 | * @return array|null 79 | */ 80 | public function getParameters(): ?array 81 | { 82 | return $this->parameters; 83 | } 84 | 85 | public function getRequestBody(): RequestBody|Reference|null 86 | { 87 | return $this->requestBody; 88 | } 89 | 90 | public function getResponses(): ?Responses 91 | { 92 | return $this->responses; 93 | } 94 | 95 | /** 96 | * @return array|null 97 | */ 98 | public function getCallbacks(): ?array 99 | { 100 | return $this->callbacks; 101 | } 102 | 103 | /** 104 | * @return SecurityRequirement[]|null 105 | */ 106 | public function getSecurity(): ?array 107 | { 108 | return $this->security; 109 | } 110 | 111 | /** 112 | * @return Server[]|null 113 | */ 114 | public function getServers(): ?array 115 | { 116 | return $this->servers; 117 | } 118 | 119 | public function getSpecificationExtensions(): array 120 | { 121 | return $this->specificationExtensions; 122 | } 123 | 124 | public function toArray(): array 125 | { 126 | $exported = array_filter([ 127 | 'tags' => $this->normalizeCollection($this->getTags()), 128 | 'summary' => $this->getSummary(), 129 | 'description' => $this->getDescription(), 130 | 'externalDocs' => $this->externalDocs?->toArray(), 131 | 'operationId' => $this->getOperationId(), 132 | 'parameters' => $this->normalizeCollection($this->getParameters()), 133 | 'requestBody' => $this->getRequestBody()?->toArray(), 134 | 'responses' => $this->getResponses()?->toArray(), 135 | 'callbacks' => $this->normalizeCollection($this->getCallbacks()), 136 | 'deprecated' => $this->getDeprecated(), 137 | 'servers' => $this->normalizeCollection($this->getServers()), 138 | ] + $this->getSpecificationExtensions()); 139 | 140 | if (null !== $this->security) { 141 | $exportedSecurity = $this->normalizeCollection($this->getSecurity()); 142 | if ($exportedSecurity === [['__NO_SECURITY' => []]]) { 143 | $exportedSecurity = []; 144 | } 145 | 146 | $exported += ['security' => $exportedSecurity]; 147 | } 148 | 149 | return $exported; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Model/Schema.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Model; 11 | 12 | /** 13 | * @author Titouan Galopin 14 | * @author Selency Team 15 | */ 16 | class Schema implements OpenApiModel 17 | { 18 | use OpenApiTrait; 19 | 20 | /** 21 | * @param array|null $properties 22 | */ 23 | public function __construct( 24 | private readonly ?string $title = null, 25 | private readonly ?int $multipleOf = null, 26 | private readonly ?int $maximum = null, 27 | private readonly ?bool $exclusiveMaximum = null, 28 | private readonly ?int $minimum = null, 29 | private readonly ?bool $exclusiveMinimum = null, 30 | private readonly ?int $maxLength = null, 31 | private readonly ?int $minLength = null, 32 | private readonly ?string $pattern = null, 33 | private readonly ?int $maxItems = null, 34 | private readonly ?int $minItems = null, 35 | private readonly ?bool $uniqueItems = null, 36 | private readonly ?int $maxProperties = null, 37 | private readonly ?int $minProperties = null, 38 | private readonly ?array $required = null, 39 | private readonly ?array $enum = null, 40 | private readonly ?array $type = null, 41 | private readonly ?array $allOf = null, 42 | private readonly ?array $oneOf = null, 43 | private readonly ?array $anyOf = null, 44 | private readonly self|Reference|null $not = null, 45 | private readonly self|Reference|null $items = null, 46 | private readonly ?array $properties = null, 47 | private readonly ?string $description = null, 48 | private readonly ?string $format = null, 49 | private readonly mixed $default = null, 50 | private readonly ?bool $readOnly = null, 51 | private readonly ?bool $writeOnly = null, 52 | private readonly mixed $example = null, 53 | private readonly ?bool $deprecated = null, 54 | private readonly ?Discriminator $discriminator = null, 55 | private readonly ?Xml $xml = null, 56 | private readonly ?ExternalDocumentation $externalDocs = null, 57 | private readonly self|Reference|false|null $additionalProperties = null, 58 | private readonly array $specificationExtensions = [], 59 | ) { 60 | } 61 | 62 | public function getTitle(): ?string 63 | { 64 | return $this->title; 65 | } 66 | 67 | public function getMultipleOf(): ?int 68 | { 69 | return $this->multipleOf; 70 | } 71 | 72 | public function getMaximum(): ?int 73 | { 74 | return $this->maximum; 75 | } 76 | 77 | public function isExclusiveMaximum(): ?bool 78 | { 79 | return $this->exclusiveMaximum; 80 | } 81 | 82 | public function getMinimum(): ?int 83 | { 84 | return $this->minimum; 85 | } 86 | 87 | public function isExclusiveMinimum(): ?bool 88 | { 89 | return $this->exclusiveMinimum; 90 | } 91 | 92 | public function getMaxLength(): ?int 93 | { 94 | return $this->maxLength; 95 | } 96 | 97 | public function getMinLength(): ?int 98 | { 99 | return $this->minLength; 100 | } 101 | 102 | public function getPattern(): ?string 103 | { 104 | return $this->pattern; 105 | } 106 | 107 | public function getMaxItems(): ?int 108 | { 109 | return $this->maxItems; 110 | } 111 | 112 | public function getMinItems(): ?int 113 | { 114 | return $this->minItems; 115 | } 116 | 117 | public function isUniqueItems(): ?bool 118 | { 119 | return $this->uniqueItems; 120 | } 121 | 122 | public function getMaxProperties(): ?int 123 | { 124 | return $this->maxProperties; 125 | } 126 | 127 | public function getMinProperties(): ?int 128 | { 129 | return $this->minProperties; 130 | } 131 | 132 | public function getRequired(): ?array 133 | { 134 | return $this->required; 135 | } 136 | 137 | public function getEnum(): ?array 138 | { 139 | return $this->enum; 140 | } 141 | 142 | public function getType(): ?array 143 | { 144 | return $this->type; 145 | } 146 | 147 | public function getAllOf(): ?array 148 | { 149 | return $this->allOf; 150 | } 151 | 152 | public function getOneOf(): ?array 153 | { 154 | return $this->oneOf; 155 | } 156 | 157 | public function getAnyOf(): ?array 158 | { 159 | return $this->anyOf; 160 | } 161 | 162 | public function getNot(): self|Reference|null 163 | { 164 | return $this->not; 165 | } 166 | 167 | public function getItems(): self|Reference|null 168 | { 169 | return $this->items; 170 | } 171 | 172 | public function getProperties(): ?array 173 | { 174 | return $this->properties; 175 | } 176 | 177 | public function getDescription(): ?string 178 | { 179 | return $this->description; 180 | } 181 | 182 | public function getFormat(): ?string 183 | { 184 | return $this->format; 185 | } 186 | 187 | public function getDefault(): mixed 188 | { 189 | return $this->default; 190 | } 191 | 192 | public function isNullable(): ?bool 193 | { 194 | return null === $this->type ? null : \in_array('null', $this->type, true); 195 | } 196 | 197 | public function isReadOnly(): ?bool 198 | { 199 | return $this->readOnly; 200 | } 201 | 202 | public function isWriteOnly(): ?bool 203 | { 204 | return $this->writeOnly; 205 | } 206 | 207 | public function getExample(): mixed 208 | { 209 | return $this->example; 210 | } 211 | 212 | public function isDeprecated(): ?bool 213 | { 214 | return $this->deprecated; 215 | } 216 | 217 | public function getDiscriminator(): ?Discriminator 218 | { 219 | return $this->discriminator; 220 | } 221 | 222 | public function getXml(): ?Xml 223 | { 224 | return $this->xml; 225 | } 226 | 227 | public function getExternalDocs(): ?ExternalDocumentation 228 | { 229 | return $this->externalDocs; 230 | } 231 | 232 | public function getAdditionalProperties(): self|bool|Reference|null 233 | { 234 | return $this->additionalProperties; 235 | } 236 | 237 | public function getSpecificationExtensions(): array 238 | { 239 | return $this->specificationExtensions; 240 | } 241 | 242 | public function toArray(): array 243 | { 244 | $data = [ 245 | 'title' => $this->getTitle(), 246 | 'multipleOf' => $this->getMultipleOf(), 247 | 'maximum' => $this->getMaximum(), 248 | 'exclusiveMaximum' => $this->isExclusiveMaximum(), 249 | 'minimum' => $this->getMinimum(), 250 | 'exclusiveMinimum' => $this->isExclusiveMinimum(), 251 | 'maxLength' => $this->getMaxLength(), 252 | 'minLength' => $this->getMinLength(), 253 | 'pattern' => $this->getPattern(), 254 | 'maxItems' => $this->getMaxItems(), 255 | 'minItems' => $this->getMinItems(), 256 | 'uniqueItems' => $this->isUniqueItems(), 257 | 'maxProperties' => $this->getMaxProperties(), 258 | 'minProperties' => $this->getMinProperties(), 259 | 'required' => $this->normalizeCollection($this->getRequired()), 260 | 'enum' => $this->normalizeCollection($this->getEnum()), 261 | 'type' => $this->getType(), 262 | 'allOf' => $this->normalizeCollection($this->getAllOf()), 263 | 'oneOf' => $this->normalizeCollection($this->getOneOf()), 264 | 'anyOf' => $this->normalizeCollection($this->getAnyOf()), 265 | 'not' => $this->getNot()?->toArray(), 266 | 'items' => $this->getItems()?->toArray(), 267 | 'properties' => $this->normalizeCollection($this->getProperties()), 268 | 'description' => $this->getDescription(), 269 | 'format' => $this->getFormat(), 270 | 'default' => $this->getDefault(), 271 | 'discriminator' => $this->getDiscriminator()?->toArray(), 272 | 'readOnly' => $this->isReadOnly(), 273 | 'writeOnly' => $this->isWriteOnly(), 274 | 'xml' => $this->getXml()?->toArray(), 275 | 'externalDocs' => $this->getExternalDocs()?->toArray(), 276 | 'example' => $this->getExample(), 277 | 'deprecated' => $this->isDeprecated(), 278 | ]; 279 | 280 | if (null !== $this->additionalProperties) { 281 | if (false === $this->additionalProperties) { 282 | $data['additionalProperties'] = false; 283 | } else { 284 | $data['additionalProperties'] = $this->additionalProperties->toArray(); 285 | } 286 | } 287 | 288 | return array_filter($data + $this->getSpecificationExtensions()); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Selency OpenAPI 2 | 3 | The OpenApi component provides a user-friendly, object-oriented API to build 4 | OpenAPI specifications using PHP. 5 | 6 | ## Sponsor 7 | 8 | [Selency](https://www.selency.fr) is a team of 80 people, each more committed than the last to 9 | promoting second-hand goods and doing something positive for our planet. 10 | Open-Source is perfectly aligned with our values, leading us to contribute 11 | back to the Symfony ecosystem through a series of components. 12 | 13 | We're always looking for talented people, [join us](https://www.welcometothejungle.com/fr/companies/selency)! 14 | 15 | ## Usage 16 | 17 | ``` 18 | composer require selency/openapi 19 | ``` 20 | 21 | Selency OpenApi implements the OpenApi standard in its version 3.1. It allows to build 22 | documentations using PHP objects, giving more flexibility and reusability to OpenApi 23 | definitions. All features of OpenApi 3.1 are supported. 24 | 25 | ### Writing documentation 26 | 27 | The OpenApi component provides object-oriented PHP tools to build the documentation 28 | definition: 29 | 30 | ```php 31 | // openapi/Documentation.php 32 | 33 | use Selency\OpenApi\Documentation\AbstractDocumentation; 34 | 35 | class Documentation extends AbstractDocumentation 36 | { 37 | public function getIdentifier(): string 38 | { 39 | return 'myapi'; 40 | } 41 | 42 | public function getVersion(): string 43 | { 44 | return '1.3.4'; 45 | } 46 | 47 | public function configure(DocumentationConfigurator $doc): void 48 | { 49 | $doc->info($this->openApi->info() 50 | ->title('Monolith API') 51 | ->description(file_get_contents(__DIR__.'/Resources/info_description.md')) 52 | ->contact(name: 'API support', url: 'https://symfony.com', email: 'contact@symfony.com') 53 | ->specificationExtension('x-logo', [ 54 | 'url' => 'https://symfony.com/logos/symfony_black_02.png', 55 | 'altText' => 'Symfony logo', 56 | ]) 57 | ->license('MIT') 58 | ); 59 | 60 | $doc->externalDocs(url: 'https://github.com/symfony/openapi', description: 'OpenApi component'); 61 | 62 | $doc->server($this->openApi->server('https://api.symfony.local')->description('Local')) 63 | ->server($this->openApi->server('https://api.symfony-staging.com')->description('Staging')) 64 | ->server($this->openApi->server('https://api.symfony.com')->description('Prod')); 65 | 66 | $doc->securityRequirement(self::REF_SECURITY_USER_JWT); 67 | 68 | $doc->path('/health', $this->openApi->pathItem() 69 | ->get($this->openApi->operation() 70 | ->tag('Health') 71 | ->operationId('app.health.check') 72 | ->summary('Health check') 73 | ->description('Check the API is up and available.') 74 | ->securityRequirement(null) 75 | ->responses($this->openApi->responses() 76 | ->response('200', $this->openApi->response() 77 | ->description('When the API is up and available.') 78 | ->content('application/json', $this->openApi->schema() 79 | ->property('name', $this->openApi->schema()->type('string')->description('Name for this API')->example('Selency API')) 80 | ->property('env', $this->openApi->schema()->type('string')->description('Current environment of this instance of the API')->example('prod')) 81 | ) 82 | ) 83 | ->response('500', $this->openApi->response()->description('When the API is unavailable due to a backend problem.')) 84 | ) 85 | ) 86 | ); 87 | 88 | // ... 89 | } 90 | } 91 | 92 | // Build a read-only model representing the documentation 93 | $compiler = new DocumentationCompiler(); 94 | $openApiDefinition = $compiler->compile($doc); 95 | 96 | // Compile it as YAML or JSON for usage in other tools 97 | $openApiYaml = (new Dumper\YamlDumper())->dump($openApiDefinition); 98 | $openApiJson = (new Dumper\JsonDumper())->dump($openApiDefinition); 99 | ``` 100 | 101 | ### Splitting documentations in multiple files 102 | 103 | The component provides a concept of Partial documentations to allow splitting the documentation 104 | in multiple files for readability: 105 | 106 | ```php 107 | // HealthDocumentation.php 108 | use Selency\OpenApi\Documentation\PartialDocumentationInterface; 109 | 110 | #[AutoconfigureTag('app.partial_documentation')] 111 | class HealthDocumentation implements PartialDocumentationInterface 112 | { 113 | public function __construct(private OpenApiBuilderInterface $openApi) 114 | { 115 | } 116 | 117 | public function configure(DocumentationConfigurator $doc): void 118 | { 119 | $doc->path('/health', $this->openApi->pathItem() 120 | ->get($this->openApi->operation() 121 | ->tag('Health') 122 | ->operationId('app.health.check') 123 | ->summary('Health check') 124 | ->description('Check the API is up and available. Mostly used by the infrastructure to check for readiness.') 125 | ->securityRequirement(null) 126 | ->responses($this->openApi->responses() 127 | ->response('200', $this->openApi->response() 128 | ->description('When the API is up and available.') 129 | ->content('application/json', $this->openApi->schema() 130 | ->property('name', $this->openApi->schema()->type('string')->description('Name for this API')->example('Selency API')) 131 | ->property('env', $this->openApi->schema()->type('string')->description('Current environment of this instance of the API')->example('prod')) 132 | ) 133 | ) 134 | ->response('500', $this->openApi->response()->description('When the API is unavailable due to a backend problem.')) 135 | ) 136 | ) 137 | ); 138 | } 139 | } 140 | 141 | 142 | // Documentation.php 143 | class Documentation extends AbstractDocumentation 144 | { 145 | private iterable $partialsDocs; 146 | 147 | public function __construct( 148 | private Builder\OpenApiBuilder $openApi, 149 | #[TaggedIterator(tag: 'app.partial_documentation')] iterable $partialsDocs, 150 | ) { 151 | $this->partialsDocs = $partialsDocs; 152 | } 153 | 154 | public function getIdentifier(): string 155 | { 156 | return 'myapi'; 157 | } 158 | 159 | public function getVersion(): string 160 | { 161 | return '1.3.4'; 162 | } 163 | 164 | public function configure(DocumentationConfigurator $doc): void 165 | { 166 | $doc->info($this->openApi->info() 167 | ->title('Monolith API') 168 | ->description(file_get_contents(__DIR__.'/Resources/info_description.md')) 169 | ->contact(name: 'API support', url: 'https://symfony.com', email: 'contact@symfony.com') 170 | ->specificationExtension('x-logo', [ 171 | 'url' => 'https://symfony.com/logos/symfony_black_02.png', 172 | 'altText' => 'Symfony logo', 173 | ]) 174 | ->license('MIT') 175 | ); 176 | 177 | // ... 178 | 179 | // Apply partial documentations 180 | foreach ($this->partialsDocs as $partialsDoc) { 181 | $partialsDoc->configure($doc); 182 | } 183 | } 184 | } 185 | ``` 186 | 187 | ### Store documentation close to your application code 188 | 189 | The OpenApi component provides two interfaces to help maintaining documentation by storing it close 190 | to your code: 191 | 192 | * `SelfDescribingSchemaInterface` can be implemented by classes describing a schema (request, response, payloads, ...) ; 193 | * `SelfDescribingQueryParametersInterface` can be implemented by classes describing a list of query parameters ; 194 | 195 | These interfaces are especially useful when using objects to handle inputs and outputs of your API: 196 | 197 | ```php 198 | class AuthRegisterPayload implements SelfDescribingSchemaInterface 199 | { 200 | #[Assert\Email(mode: Email::VALIDATION_MODE_STRICT)] 201 | #[Assert\NotBlank] 202 | public $email; 203 | 204 | #[Assert\Type(type: 'string')] 205 | #[Assert\NotBlank] 206 | public $firstName; 207 | 208 | #[Assert\Type(type: 'string')] 209 | #[Assert\NotBlank] 210 | public $lastName; 211 | 212 | #[Assert\Type(type: 'string')] 213 | #[Assert\NotBlank] 214 | public $password; 215 | 216 | public static function describeSchema(SchemaConfigurator $schema, OpenApiBuilderInterface $openApi): void 217 | { 218 | $schema 219 | ->title('AuthRegister') 220 | ->required(['email', 'firstName', 'lastName', 'password']) 221 | ->property('email', $openApi->schema() 222 | ->type('string') 223 | ->description('User\'s email') 224 | ->example('john.doe@domain.com') 225 | ) 226 | ->property('firstName', $openApi->schema() 227 | ->type('string') 228 | ->description('User\'s first name') 229 | ->example('John') 230 | ) 231 | ->property('lastName', $openApi->schema() 232 | ->type('string') 233 | ->description('User\'s last name') 234 | ->example('Doe') 235 | ) 236 | ->property('password', $openApi->schema() 237 | ->type('string') 238 | ->description('User\'s plaintext password') 239 | ) 240 | ; 241 | } 242 | } 243 | ``` 244 | 245 | You can then load these self-describing schemas/query params classes by providing the 246 | dedicated loader during compilation: 247 | 248 | ```php 249 | // Build a read-only model representing the documentation 250 | $compiler = new DocumentationCompiler([ 251 | new Selency\OpenApi\Loader\SelfDescribingSchemaLoader([ 252 | AuthRegisterPayload::class, 253 | ]) 254 | ]); 255 | 256 | $openApiDefinition = $compiler->compile($doc); 257 | ``` 258 | 259 | > **Note**: in a Symfony application, SelfDescribingSchemaInterface and 260 | > SelfDescribingQueryParametersInterface class are automatically added to the compiler, 261 | > you don't need to do anything. 262 | 263 | And use them in your definitions: 264 | 265 | ```php 266 | class Documentation extends AbstractDocumentation 267 | { 268 | // ... 269 | 270 | public function configure(DocumentationConfigurator $doc): void 271 | { 272 | // ... 273 | 274 | $doc->path('/auth/register', $this->openApi->pathItem() 275 | ->post($this->openApi->operation() 276 | ->tag('Auth') 277 | ->operationId('app.auth.register') 278 | ->summary('Auth registration') 279 | ->description('Register as a user.') 280 | ->securityRequirement(null) 281 | ->requestBody($this->openApi->requestBody() 282 | ->content('application/json', AuthRegisterPayload::class) 283 | ) 284 | ->responses($this->openApi->responses() 285 | ->response('200', $this->openApi->response() 286 | ->content('application/json', AuthRegisterOutput::class) 287 | ) 288 | ) 289 | ) 290 | ); 291 | 292 | // ... 293 | } 294 | } 295 | ``` 296 | -------------------------------------------------------------------------------- /src/Configurator/SchemaConfigurator.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | namespace Selency\OpenApi\Configurator; 11 | 12 | use Selency\OpenApi\Model\Discriminator; 13 | use Selency\OpenApi\Model\Reference; 14 | use Selency\OpenApi\Model\Schema; 15 | use Selency\OpenApi\Model\Xml; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * @author Selency Team 20 | */ 21 | class SchemaConfigurator 22 | { 23 | use Traits\DeprecatedTrait; 24 | use Traits\DescriptionTrait; 25 | use Traits\ExtensionsTrait; 26 | use Traits\ExternalDocsTrait; 27 | 28 | private const PHP_TO_OPENAPI_TYPE = [ 29 | 'null' => 'null', 30 | 'string' => 'string', 31 | 'number' => 'number', 32 | 'float' => 'number', 33 | 'double' => 'number', 34 | 'integer' => 'integer', 35 | 'int' => 'integer', 36 | 'boolean' => 'boolean', 37 | 'bool' => 'boolean', 38 | 'array' => 'array', 39 | 'object' => 'object', 40 | ]; 41 | 42 | private ?string $title = null; 43 | private ?int $multipleOf = null; 44 | private ?int $maximum = null; 45 | private ?bool $exclusiveMaximum = null; 46 | private ?int $minimum = null; 47 | private ?bool $exclusiveMinimum = null; 48 | private ?int $maxLength = null; 49 | private ?int $minLength = null; 50 | private ?string $pattern = null; 51 | private ?int $maxItems = null; 52 | private ?int $minItems = null; 53 | private ?bool $uniqueItems = null; 54 | private ?int $maxProperties = null; 55 | private ?int $minProperties = null; 56 | private ?array $required = null; 57 | private ?array $enum = null; 58 | private ?array $type = null; 59 | private Schema|Reference|null $not = null; 60 | private Schema|Reference|null $items = null; 61 | private ?string $format = null; 62 | private mixed $default = null; 63 | private ?bool $readOnly = null; 64 | private ?bool $writeOnly = null; 65 | private mixed $example = null; 66 | private ?Discriminator $discriminator = null; 67 | private ?Xml $xml = null; 68 | private Schema|Reference|false|null $additionalProperties = null; 69 | 70 | /** 71 | * @var array|null 72 | */ 73 | private ?array $properties = null; 74 | 75 | /** 76 | * @var array|null 77 | */ 78 | private ?array $allOf = null; 79 | 80 | /** 81 | * @var array|null 82 | */ 83 | private ?array $oneOf = null; 84 | 85 | /** 86 | * @var array|null 87 | */ 88 | private ?array $anyOf = null; 89 | 90 | public static function createFromDefinition(self|ReferenceConfigurator|string|array $definition = null): self|ReferenceConfigurator 91 | { 92 | // Empty schema 93 | if (!$definition) { 94 | return new self(); 95 | } 96 | 97 | // Direct schema or reference 98 | if ($definition instanceof self || $definition instanceof ReferenceConfigurator) { 99 | return $definition; 100 | } 101 | 102 | // Array: types list 103 | if (\is_array($definition)) { 104 | return (new self())->type($definition); 105 | } 106 | 107 | $typeDefinition = '?' === $definition[0] ? mb_substr($definition, 1) : $definition; 108 | 109 | // Native type 110 | if (isset(self::PHP_TO_OPENAPI_TYPE[$typeDefinition])) { 111 | return (new self())->type($definition); 112 | } 113 | 114 | // Schema reference 115 | $reference = new ReferenceConfigurator('#/components/schemas/'.ReferenceConfigurator::normalize($typeDefinition)); 116 | 117 | return '?' === $definition[0] ? (new self())->oneOf([$reference, 'null']) : $reference; 118 | } 119 | 120 | public function build(): Schema 121 | { 122 | return new Schema( 123 | title: $this->title, 124 | multipleOf: $this->multipleOf, 125 | maximum: $this->maximum, 126 | exclusiveMaximum: $this->exclusiveMaximum, 127 | minimum: $this->minimum, 128 | exclusiveMinimum: $this->exclusiveMinimum, 129 | maxLength: $this->maxLength, 130 | minLength: $this->minLength, 131 | pattern: $this->pattern, 132 | maxItems: $this->maxItems, 133 | minItems: $this->minItems, 134 | uniqueItems: $this->uniqueItems, 135 | maxProperties: $this->maxProperties, 136 | minProperties: $this->minProperties, 137 | required: $this->required, 138 | enum: $this->enum, 139 | type: $this->type, 140 | allOf: $this->allOf, 141 | oneOf: $this->oneOf, 142 | anyOf: $this->anyOf, 143 | not: $this->not, 144 | items: $this->items, 145 | properties: $this->properties, 146 | description: $this->description, 147 | format: $this->format, 148 | default: $this->default, 149 | readOnly: $this->readOnly, 150 | writeOnly: $this->writeOnly, 151 | example: $this->example, 152 | deprecated: $this->deprecated, 153 | discriminator: $this->discriminator, 154 | xml: $this->xml, 155 | externalDocs: $this->externalDocs, 156 | additionalProperties: $this->additionalProperties, 157 | specificationExtensions: $this->specificationExtensions, 158 | ); 159 | } 160 | 161 | public function property(string $name, self|ReferenceConfigurator|string $definition): static 162 | { 163 | $this->properties[$name] = self::createFromDefinition($definition)->build(); 164 | 165 | return $this; 166 | } 167 | 168 | public function title(string $title): static 169 | { 170 | $this->title = $title; 171 | 172 | return $this; 173 | } 174 | 175 | public function multipleOf(int $multipleOf): static 176 | { 177 | $this->multipleOf = $multipleOf; 178 | 179 | return $this; 180 | } 181 | 182 | public function maximum(int $maximum): static 183 | { 184 | $this->maximum = $maximum; 185 | 186 | return $this; 187 | } 188 | 189 | public function exclusiveMaximum(bool $exclusiveMaximum): static 190 | { 191 | $this->exclusiveMaximum = $exclusiveMaximum; 192 | 193 | return $this; 194 | } 195 | 196 | public function minimum(int $minimum): static 197 | { 198 | $this->minimum = $minimum; 199 | 200 | return $this; 201 | } 202 | 203 | public function exclusiveMinimum(bool $exclusiveMinimum): static 204 | { 205 | $this->exclusiveMinimum = $exclusiveMinimum; 206 | 207 | return $this; 208 | } 209 | 210 | public function maxLength(int $maxLength): static 211 | { 212 | $this->maxLength = $maxLength; 213 | 214 | return $this; 215 | } 216 | 217 | public function minLength(int $minLength): static 218 | { 219 | $this->minLength = $minLength; 220 | 221 | return $this; 222 | } 223 | 224 | public function pattern(string $pattern): static 225 | { 226 | $this->pattern = $pattern; 227 | 228 | return $this; 229 | } 230 | 231 | public function maxItems(int $maxItems): static 232 | { 233 | $this->maxItems = $maxItems; 234 | 235 | return $this; 236 | } 237 | 238 | public function minItems(int $minItems): static 239 | { 240 | $this->minItems = $minItems; 241 | 242 | return $this; 243 | } 244 | 245 | public function uniqueItems(bool $uniqueItems): static 246 | { 247 | $this->uniqueItems = $uniqueItems; 248 | 249 | return $this; 250 | } 251 | 252 | public function maxProperties(int $maxProperties): static 253 | { 254 | $this->maxProperties = $maxProperties; 255 | 256 | return $this; 257 | } 258 | 259 | public function minProperties(int $minProperties): static 260 | { 261 | $this->minProperties = $minProperties; 262 | 263 | return $this; 264 | } 265 | 266 | public function required(array $required): static 267 | { 268 | $this->required = $required; 269 | 270 | return $this; 271 | } 272 | 273 | /** 274 | * @template T of \UnitEnum 275 | * 276 | * @param array|class-string $enum 277 | * 278 | * @return $this 279 | */ 280 | public function enum(array|string $enum): static 281 | { 282 | if (\is_string($enum)) { 283 | if (!enum_exists($enum)) { 284 | throw new \InvalidArgumentException('Parameter #1 $enum of method '.__CLASS__.'::'.__METHOD__.' expects an array or an enum, string given.'); 285 | } 286 | 287 | $enum = $enum::cases(); 288 | } 289 | 290 | $this->enum = $enum; 291 | 292 | return $this; 293 | } 294 | 295 | public function type(array|string $typeDefinitions): static 296 | { 297 | // Only 298 | if (\is_string($typeDefinitions)) { 299 | if ('?' === $typeDefinitions[0]) { 300 | $typeDefinitions = array_unique([mb_substr($typeDefinitions, 1), 'null']); 301 | } else { 302 | $typeDefinitions = [$typeDefinitions]; 303 | } 304 | } 305 | 306 | $types = []; 307 | foreach ($typeDefinitions as $definition) { 308 | $definition = mb_strtolower($definition); 309 | 310 | if (!isset(self::PHP_TO_OPENAPI_TYPE[$definition])) { 311 | throw new \InvalidArgumentException('Type '.$definition.' is not a valid OpenApi type.'); 312 | } 313 | 314 | $types[] = self::PHP_TO_OPENAPI_TYPE[$definition]; 315 | } 316 | 317 | $this->type = $types; 318 | 319 | return $this; 320 | } 321 | 322 | /** 323 | * @param array $allOf 324 | */ 325 | public function allOf(array $allOf): static 326 | { 327 | $this->allOf = []; 328 | foreach ($allOf as $definition) { 329 | $this->allOf[] = self::createFromDefinition($definition)->build(); 330 | } 331 | 332 | return $this; 333 | } 334 | 335 | /** 336 | * @param array $oneOf 337 | */ 338 | public function oneOf(array $oneOf): static 339 | { 340 | $this->oneOf = []; 341 | foreach ($oneOf as $definition) { 342 | $this->oneOf[] = self::createFromDefinition($definition)->build(); 343 | } 344 | 345 | return $this; 346 | } 347 | 348 | /** 349 | * @param array $anyOf 350 | */ 351 | public function anyOf(array $anyOf): static 352 | { 353 | $this->anyOf = []; 354 | foreach ($anyOf as $definition) { 355 | $this->anyOf[] = self::createFromDefinition($definition)->build(); 356 | } 357 | 358 | return $this; 359 | } 360 | 361 | public function not(self|ReferenceConfigurator|string $not): static 362 | { 363 | $this->not = self::createFromDefinition($not)->build(); 364 | 365 | return $this; 366 | } 367 | 368 | public function items(self|ReferenceConfigurator|string $items): static 369 | { 370 | $this->items = self::createFromDefinition($items)->build(); 371 | 372 | return $this; 373 | } 374 | 375 | public function format(string $format): static 376 | { 377 | $this->format = $format; 378 | 379 | return $this; 380 | } 381 | 382 | public function default(mixed $default): static 383 | { 384 | $this->default = $default; 385 | 386 | return $this; 387 | } 388 | 389 | public function nullable(bool $nullable): static 390 | { 391 | if ($nullable) { 392 | $this->type = array_unique(array_merge($this->type ?: [], ['null'])); 393 | } else { 394 | $this->type = array_filter($this->type ?: [], static fn (string $t) => 'null' !== $t); 395 | } 396 | 397 | return $this; 398 | } 399 | 400 | public function readOnly(bool $readOnly): static 401 | { 402 | $this->readOnly = $readOnly; 403 | 404 | return $this; 405 | } 406 | 407 | public function writeOnly(bool $writeOnly): static 408 | { 409 | $this->writeOnly = $writeOnly; 410 | 411 | return $this; 412 | } 413 | 414 | public function example(mixed $example): static 415 | { 416 | $this->example = $example; 417 | 418 | return $this; 419 | } 420 | 421 | public function discriminator(string $propertyName, array $mapping = null, array $specificationExtensions = []): static 422 | { 423 | $this->discriminator = new Discriminator($propertyName, $mapping, $specificationExtensions); 424 | 425 | return $this; 426 | } 427 | 428 | public function xml( 429 | string $name = null, 430 | string $namespace = null, 431 | string $prefix = null, 432 | bool $attribute = null, 433 | bool $wrapped = null, 434 | array $specificationExtensions = [], 435 | ): static { 436 | $this->xml = new Xml($name, $namespace, $prefix, $attribute, $wrapped, $specificationExtensions); 437 | 438 | return $this; 439 | } 440 | 441 | public function additionalProperties(self|ReferenceConfigurator|string|false $additionalProperties): static 442 | { 443 | if (false === $additionalProperties) { 444 | $this->additionalProperties = false; 445 | } else { 446 | $this->additionalProperties = self::createFromDefinition($additionalProperties)->build(); 447 | } 448 | 449 | return $this; 450 | } 451 | } 452 | --------------------------------------------------------------------------------