welcome back to dyb-tech
This commit is contained in:
+83
@@ -0,0 +1,83 @@
|
||||
## Contributing
|
||||
|
||||
Contributions of any kind are welcome.
|
||||
|
||||
Feel free to submit [Github Issues](https://github.com/zircote/swagger-php/issues)
|
||||
or [pull requests](https://github.com/zircote/swagger-php/pulls).
|
||||
|
||||
|
||||
## Quick Guide
|
||||
|
||||
The documentation site has [some details](https://zircote.github.io/swagger-php/guide/under-the-hood.html#documentation) about internals.
|
||||
|
||||
### How-To
|
||||
|
||||
* [Fork](https://help.github.com/articles/fork-a-repo/) the repo.
|
||||
* [Checkout](https://git-scm.com/docs/git-checkout) the branch you want to make changes on.
|
||||
* Typically, this will be `master`. Note that most of the time, `master` represents the next release of swagger-php, so Pull Requests that break backwards compatibility might be postponed.
|
||||
* Install dependencies: `composer install`.
|
||||
* Create a new branch, e.g. `feature-foo` or `bugfix-bar`.
|
||||
* Make changes.
|
||||
* If you are adding functionality or fixing a bug - add a test!
|
||||
|
||||
Prefer adding new test cases over modifying existing ones.
|
||||
* Update documentation: `composer docs:gen`.
|
||||
* Run static analysis using PHPStan/Psalm: `composer analyse`.
|
||||
* Check if tests pass: `composer test`.
|
||||
* Fix code style issues: `composer cs`.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation website is build from the [docs](docs/) folder with [vitepress](https://vitepress.vuejs.org).
|
||||
This process involves converting the existing markdown (`.md`) files into static HTML pages and publishing them.
|
||||
|
||||
Some reference content is based on the existing code, so changes to annotations, attributes and processors will require to re-generate those markdown files: `composer docs:gen`.
|
||||
|
||||
The actual published content is managed in the [gh-pages](https://github.com/zircote/swagger-php/tree/gh-pages) branch and driven by a [publish action](https://github.com/zircote/swagger-php/actions/workflows/gh-pages.yml).
|
||||
|
||||
|
||||
## Useful commands
|
||||
|
||||
### To run both unit tests and linting execute
|
||||
```shell
|
||||
composer test
|
||||
```
|
||||
|
||||
### To run static-analysis execute
|
||||
```shell
|
||||
composer analyse
|
||||
```
|
||||
|
||||
### Running unit tests only
|
||||
```shell
|
||||
./bin/phpunit
|
||||
```
|
||||
|
||||
### Regenerate reference markup docs
|
||||
```shell
|
||||
composer docs:gen
|
||||
```
|
||||
|
||||
### Running linting only
|
||||
```shell
|
||||
composer lint
|
||||
```
|
||||
|
||||
### To make `php-cs-fixer` fix linting errors
|
||||
```shell
|
||||
composer cs
|
||||
```
|
||||
|
||||
### Run dev server for local development of `gh-pages`
|
||||
```shell
|
||||
composer docs:dev
|
||||
```
|
||||
|
||||
|
||||
## Project's Standards
|
||||
|
||||
* [PSR-1: Basic Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
|
||||
* [PSR-2: Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
|
||||
* [PSR-4: Autoloading Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)
|
||||
* [PSR-5: PHPDoc (draft)](https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md)
|
||||
Vendored
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
swagger-php
|
||||
Copyright 2022 The swagger-php project
|
||||
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
[](https://github.com/zircote/swagger-php/actions?query=workflow:build)
|
||||
[](https://packagist.org/packages/zircote/swagger-php)
|
||||
[](LICENSE)
|
||||
|
||||
# swagger-php
|
||||
|
||||
Generate interactive [OpenAPI](https://www.openapis.org) documentation for your RESTful API using
|
||||
[doctrine annotations](https://www.doctrine-project.org/projects/annotations.html) (optional as of version 4.8; if required the `doctrine/annotations` library must be installed in addition to swagger.php).
|
||||
or [PHP attributes](https://www.php.net/manual/en/language.attributes.overview.php).
|
||||
|
||||
For a full list of supported annotations, please have look at the [`OpenApi\Annotations` namespace](src/Annotations) or the [documentation website](https://zircote.github.io/swagger-php/guide/annotations.html).
|
||||
|
||||
## Features
|
||||
|
||||
- Compatible with the OpenAPI **3.0** and **3.1** specification.
|
||||
- Extracts information from code & existing phpdoc annotations.
|
||||
- Command-line interface available.
|
||||
- [Documentation site](https://zircote.github.io/swagger-php/) with a getting started guide.
|
||||
- Exceptional error reporting (with hints, context)
|
||||
- As of PHP 8.1 all annotations are also available as PHP attributes
|
||||
|
||||
## OpenAPI version support
|
||||
|
||||
`swagger-php` allows to generate specs either for **OpenAPI 3.0.0** or **OpenAPI 3.1.0**.
|
||||
By default the spec will be in version `3.0.0`. The command line option `--version` may be used to change this
|
||||
to `3.1.0`.
|
||||
|
||||
Programmatically, the method `Generator::setVersion()` can be used to change the version.
|
||||
|
||||
## Requirements
|
||||
|
||||
`swagger-php` requires at least PHP 7.2 for annotations and PHP 8.1 for using attributes.
|
||||
|
||||
## Installation (with [Composer](https://getcomposer.org))
|
||||
|
||||
```shell
|
||||
composer require zircote/swagger-php
|
||||
```
|
||||
|
||||
For cli usage from anywhere install swagger-php globally and make sure to place the `~/.composer/vendor/bin` directory in your PATH so the `openapi` executable can be located by your system.
|
||||
|
||||
```shell
|
||||
composer global require zircote/swagger-php
|
||||
```
|
||||
|
||||
### doctrine/annotations
|
||||
As of version `4.8` the [doctrine annotations](https://www.doctrine-project.org/projects/annotations.html) library **is optional** and **no longer installed by default**.
|
||||
|
||||
To use PHPDoc annotations this needs to be installed on top of `swagger-php`:
|
||||
```shell
|
||||
composer require doctrine/annotations
|
||||
```
|
||||
|
||||
If your code uses PHPDoc annotations you will need to install this as well:
|
||||
|
||||
```shell
|
||||
composer require doctrine/annotations
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Add annotations to your php files.
|
||||
|
||||
```php
|
||||
/**
|
||||
* @OA\Info(title="My First API", version="0.1")
|
||||
*/
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/resource.json",
|
||||
* @OA\Response(response="200", description="An example resource")
|
||||
* )
|
||||
*/
|
||||
```
|
||||
|
||||
Visit the [Documentation website](https://zircote.github.io/swagger-php/) for the [Getting started guide](https://zircote.github.io/swagger-php/guide) or look at the [Examples directory](Examples/) for more examples.
|
||||
|
||||
### Usage from php
|
||||
|
||||
Generate always-up-to-date documentation.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require("vendor/autoload.php");
|
||||
$openapi = \OpenApi\Generator::scan(['/path/to/project']);
|
||||
header('Content-Type: application/x-yaml');
|
||||
echo $openapi->toYaml();
|
||||
```
|
||||
Documentation of how to use the `Generator` class can be found in the [Generator reference](https://zircote.github.io/swagger-php/reference/generator).
|
||||
|
||||
### Usage from the Command Line Interface
|
||||
|
||||
The `openapi` command line interface can be used to generate the documentation to a static yaml/json file.
|
||||
|
||||
```shell
|
||||
./vendor/bin/openapi --help
|
||||
```
|
||||
Starting with version 4 the default analyser used on the command line is the new `ReflectionAnalyser`.
|
||||
|
||||
Using the `--legacy` flag (`-l`) the legacy `TokenAnalyser` can still be used.
|
||||
|
||||
### Usage from the Deserializer
|
||||
|
||||
Generate the OpenApi annotation object from a json string, which makes it easier to manipulate objects programmatically.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use OpenApi\Serializer;
|
||||
|
||||
$serializer = new Serializer();
|
||||
$openapi = $serializer->deserialize($jsonString, 'OpenApi\Annotations\OpenApi');
|
||||
echo $openapi->toJson();
|
||||
```
|
||||
|
||||
## [Contributing](CONTRIBUTING.md)
|
||||
|
||||
## More on OpenApi & Swagger
|
||||
|
||||
- https://swagger.io
|
||||
- https://www.openapis.org
|
||||
- [OpenApi Documentation](https://swagger.io/docs/)
|
||||
- [OpenApi Specification](http://swagger.io/specification/)
|
||||
- [Related projects](docs/related-projects.md)
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use OpenApi\Analysers\AttributeAnnotationFactory;
|
||||
use OpenApi\Analysers\DocBlockAnnotationFactory;
|
||||
use OpenApi\Analysers\ReflectionAnalyser;
|
||||
use OpenApi\Analysers\TokenAnalyser;
|
||||
use OpenApi\Annotations\OpenApi;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Util;
|
||||
use OpenApi\Loggers\ConsoleLogger;
|
||||
|
||||
if (class_exists(Generator::class) === false) {
|
||||
if (file_exists(__DIR__.'/../vendor/autoload.php')) { // cloned / dev environment?
|
||||
require_once(__DIR__.'/../vendor/autoload.php');
|
||||
} else {
|
||||
require_once(realpath(__DIR__.'/../../../').'/autoload.php');
|
||||
}
|
||||
}
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// Possible options and their default values.
|
||||
$options = [
|
||||
'config' => [],
|
||||
'legacy' => false,
|
||||
'output' => false,
|
||||
'format' => 'auto',
|
||||
'exclude' => [],
|
||||
'pattern' => '*.php',
|
||||
'bootstrap' => [],
|
||||
'help' => false,
|
||||
'debug' => false,
|
||||
'processor' => [],
|
||||
'version' => null,
|
||||
];
|
||||
$aliases = [
|
||||
'c' => 'config',
|
||||
'l' => 'legacy',
|
||||
'o' => 'output',
|
||||
'e' => 'exclude',
|
||||
'n' => 'pattern',
|
||||
'b' => 'bootstrap',
|
||||
'h' => 'help',
|
||||
'd' => 'debug',
|
||||
'p' => 'processor',
|
||||
'f' => 'format'
|
||||
];
|
||||
$needsArgument = [
|
||||
'config',
|
||||
'output',
|
||||
'format',
|
||||
'exclude',
|
||||
'pattern',
|
||||
'bootstrap',
|
||||
'processor',
|
||||
'version',
|
||||
];
|
||||
$paths = [];
|
||||
$error = false;
|
||||
|
||||
try {
|
||||
// Parse cli arguments
|
||||
for ($i = 1; $i < $argc; $i++) {
|
||||
$arg = $argv[$i];
|
||||
if (substr($arg, 0, 2) === '--') { // longopt
|
||||
$option = substr($arg, 2);
|
||||
} elseif ($arg[0] === '-') { // shortopt
|
||||
if (array_key_exists(substr($arg, 1), $aliases)) {
|
||||
$option = $aliases[$arg[1]];
|
||||
} else {
|
||||
throw new Exception('Unknown option: "' . $arg . '"');
|
||||
}
|
||||
} else {
|
||||
$paths[] = $arg;
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($option, $options) === false) {
|
||||
throw new Exception('Unknown option: "' . $arg . '"');
|
||||
}
|
||||
if (in_array($option, $needsArgument)) {
|
||||
if (empty($argv[$i + 1]) || $argv[$i + 1][0] === '-') {
|
||||
throw new Exception('Missing argument for "' . $arg . '"');
|
||||
}
|
||||
if (is_array($options[$option])) {
|
||||
$options[$option][] = $argv[$i + 1];
|
||||
} else {
|
||||
$options[$option] = $argv[$i + 1];
|
||||
}
|
||||
$i++;
|
||||
} else {
|
||||
$options[$option] = true;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
$logger = new ConsoleLogger($options['debug']);
|
||||
|
||||
if (!$error && $options['bootstrap']) {
|
||||
foreach ($options['bootstrap'] as $bootstrap) {
|
||||
$filenames = glob($bootstrap);
|
||||
if (false === $filenames) {
|
||||
$error = 'Invalid `--bootstrap` value: "' . $bootstrap . '"';
|
||||
break;
|
||||
}
|
||||
foreach ($filenames as $filename) {
|
||||
if ($options['debug']) {
|
||||
$logger->debug('Bootstrapping: ' . $filename);
|
||||
}
|
||||
require_once($filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($paths) === 0) {
|
||||
$error = 'Specify at least one path.';
|
||||
}
|
||||
|
||||
if ($options['help'] === false && $error) {
|
||||
$logger->error('', ['prefix' => '']);
|
||||
$logger->error($error);
|
||||
// Show help
|
||||
$options['help'] = true;
|
||||
}
|
||||
$defaultVersion = OpenApi::DEFAULT_VERSION;
|
||||
if ($options['help']) {
|
||||
$help = <<<EOF
|
||||
|
||||
Usage: openapi [--option value] [/path/to/project ...]
|
||||
|
||||
Options:
|
||||
--config (-c) Generator config
|
||||
ex: -c operationId.hash=false
|
||||
--legacy (-l) Use legacy TokenAnalyser; default is the new ReflectionAnalyser
|
||||
--output (-o) Path to store the generated documentation.
|
||||
ex: --output openapi.yaml
|
||||
--exclude (-e) Exclude path(s).
|
||||
ex: --exclude vendor,library/Zend
|
||||
--pattern (-n) Pattern of files to scan.
|
||||
ex: --pattern "*.php" or --pattern "/\.(phps|php)$/"
|
||||
--bootstrap (-b) Bootstrap php file(s) for defining constants, etc.
|
||||
ex: --bootstrap config/constants.php
|
||||
--processor (-p) Register an additional processor.
|
||||
--format (-f) Force yaml or json.
|
||||
--debug (-d) Show additional error information.
|
||||
--version The OpenAPI version; defaults to {$defaultVersion}.
|
||||
--help (-h) Display this help message.
|
||||
|
||||
|
||||
EOF;
|
||||
$logger->info($help);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$errorTypes = [
|
||||
E_ERROR => 'Error',
|
||||
E_WARNING => 'Warning',
|
||||
E_PARSE => 'Parser error',
|
||||
E_NOTICE => 'Notice',
|
||||
E_STRICT => 'Strict',
|
||||
E_DEPRECATED => 'Deprecated',
|
||||
E_CORE_ERROR => 'Error(Core)',
|
||||
E_CORE_WARNING => 'Warning(Core)',
|
||||
E_COMPILE_ERROR => 'Error(compile)',
|
||||
E_COMPILE_WARNING => 'Warning(Compile)',
|
||||
E_RECOVERABLE_ERROR => 'Error(Recoverable)',
|
||||
E_USER_ERROR => 'Error',
|
||||
E_USER_WARNING => 'Warning',
|
||||
E_USER_NOTICE => 'Notice',
|
||||
E_USER_DEPRECATED => 'Deprecated',
|
||||
];
|
||||
set_error_handler(function ($errno, $errstr, $file, $line) use ($errorTypes, $options, $logger) {
|
||||
if (!(error_reporting() & $errno)) {
|
||||
// This error code is not included in error_reporting
|
||||
return;
|
||||
}
|
||||
$type = array_key_exists($errno, $errorTypes) ? $errorTypes[$errno] : 'Error';
|
||||
if ($type === 'Deprecated') {
|
||||
$logger->info($errstr, ['prefix' => $type . ': ']);
|
||||
} else {
|
||||
$logger->error($errstr, ['prefix' => $type . ': ']);
|
||||
}
|
||||
|
||||
if ($options['debug']) {
|
||||
$logger->info(' in '.$file.' on line '.$line);
|
||||
}
|
||||
if (substr($type, 0, 5) === 'Error') {
|
||||
exit($errno);
|
||||
}
|
||||
});
|
||||
|
||||
set_exception_handler(function ($exception) use ($logger) {
|
||||
$logger->error($exception);
|
||||
exit($exception->getCode() ?: 1);
|
||||
});
|
||||
|
||||
$exclude = null;
|
||||
if ($options['exclude']) {
|
||||
$exclude = $options['exclude'];
|
||||
if (strpos($exclude[0], ',') !== false) {
|
||||
$exploded = explode(',', $exclude[0]);
|
||||
$logger->error('Comma-separated exclude paths are deprecated, use multiple --exclude statements: --exclude '.$exploded[0].' --exclude '.$exploded[1]);
|
||||
$exclude[0] = array_shift($exploded);
|
||||
$exclude = array_merge($exclude, $exploded);
|
||||
}
|
||||
}
|
||||
|
||||
$pattern = "*.php";
|
||||
if ($options['pattern']) {
|
||||
$pattern = $options['pattern'];
|
||||
}
|
||||
|
||||
$generator = new Generator($logger);
|
||||
foreach ($options["processor"] as $processor) {
|
||||
$class = '\OpenApi\Processors\\'.$processor;
|
||||
if (class_exists($class)) {
|
||||
$processor = new $class();
|
||||
} elseif (class_exists($processor)) {
|
||||
$processor = new $processor();
|
||||
}
|
||||
$generator->addProcessor($processor);
|
||||
}
|
||||
|
||||
$analyser = $options['legacy']
|
||||
? new TokenAnalyser()
|
||||
: new ReflectionAnalyser([new DocBlockAnnotationFactory(), new AttributeAnnotationFactory()]);
|
||||
|
||||
$openapi = $generator
|
||||
->setVersion($options['version'])
|
||||
->setConfig($options['config'])
|
||||
->setAnalyser($analyser)
|
||||
->generate(Util::finder($paths, $exclude, $pattern));
|
||||
|
||||
if ($options['output'] === false) {
|
||||
if (strtolower($options['format']) === 'json') {
|
||||
echo $openapi->toJson();
|
||||
} else {
|
||||
echo $openapi->toYaml();
|
||||
}
|
||||
echo "\n";
|
||||
} else {
|
||||
if (is_dir($options['output'])) {
|
||||
$options['output'] .= '/openapi.yaml';
|
||||
}
|
||||
$openapi->saveAs($options['output'], $options['format']);
|
||||
}
|
||||
exit($logger->loggedMessageAboveNotice() ? 1 : 0);
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"name": "zircote/swagger-php",
|
||||
"type": "library",
|
||||
"license": "Apache-2.0",
|
||||
"bin": [
|
||||
"bin/openapi"
|
||||
],
|
||||
"description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations",
|
||||
"keywords": [
|
||||
"json",
|
||||
"rest",
|
||||
"api",
|
||||
"service discovery"
|
||||
],
|
||||
"homepage": "https://github.com/zircote/swagger-php/",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Robert Allen",
|
||||
"email": "zircote@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Bob Fanger",
|
||||
"email": "bfanger@gmail.com",
|
||||
"homepage": "https://bfanger.nl"
|
||||
},
|
||||
{
|
||||
"name": "Martin Rademacher",
|
||||
"email": "mano@radebatz.net",
|
||||
"homepage": "https://radebatz.net"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"bin-dir": "bin",
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.x-dev"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"ext-json": "*",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
||||
"symfony/deprecation-contracts": "^2 || ^3",
|
||||
"symfony/finder": ">=2.2",
|
||||
"symfony/yaml": ">=3.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OpenApi\\": "src"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/package-versions-deprecated": "^1.11",
|
||||
"doctrine/annotations": "^1.7 || ^2.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.17 || ^3.47.1",
|
||||
"phpstan/phpstan": "^1.6",
|
||||
"phpunit/phpunit": ">=8",
|
||||
"vimeo/psalm": "^4.23"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/annotations": "^1.7 || ^2.0"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"exclude-from-classmap": [
|
||||
"/tests/Fixtures"
|
||||
],
|
||||
"psr-4": {
|
||||
"OpenApi\\Tools\\": "tools/src/",
|
||||
"OpenApi\\Tests\\": "tests/",
|
||||
"AnotherNamespace\\": "tests/Fixtures/AnotherNamespace"
|
||||
}
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"cs": "Fix all codestyle issues",
|
||||
"lint": "Test codestyle",
|
||||
"test": "Run all non-legacy and codestyle tests",
|
||||
"testlegacy": "Run tests using the legacy TokenAnalyser",
|
||||
"testall": "Run all tests (test + testlegacy)",
|
||||
"analyse": "Run static analysis (phpstan/psalm)",
|
||||
"spectral": "Run spectral lint over all .yaml files in the Examples folder",
|
||||
"docs:gen": "Rebuild reference documentation",
|
||||
"docs:dev": "Run dev server for local development of gh-pages",
|
||||
"docs:build": "Re-build static gh-pages"
|
||||
},
|
||||
"scripts": {
|
||||
"cs": "export XDEBUG_MODE=off && php-cs-fixer fix --allow-risky=yes",
|
||||
"lint": "@cs --dry-run",
|
||||
"test": [
|
||||
"export XDEBUG_MODE=off && phpunit",
|
||||
"@lint"
|
||||
],
|
||||
"testlegacy": "export XDEBUG_MODE=off && export PHPUNIT_ANALYSER=legacy && phpunit",
|
||||
"testall": [
|
||||
"@test",
|
||||
"@testlegacy"
|
||||
],
|
||||
"analyse": [
|
||||
"export XDEBUG_MODE=off && phpstan analyse --memory-limit=2G",
|
||||
"export XDEBUG_MODE=off && psalm"
|
||||
],
|
||||
"spectral": "for ff in `find Examples -name '*.yaml'`; do spectral lint $ff; done",
|
||||
"docs:gen": [
|
||||
"@php tools/refgen.php",
|
||||
"@php tools/procgen.php"
|
||||
],
|
||||
"docs:dev": "cd docs && npm run dev",
|
||||
"docs:build": [
|
||||
"@docs:gen",
|
||||
"cd docs && npm run build"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
|
||||
interface AnalyserInterface
|
||||
{
|
||||
public function setGenerator(Generator $generator): void;
|
||||
|
||||
public function fromFile(string $filename, Context $context): Analysis;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
|
||||
interface AnnotationFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Checks if this factory is supported by the current runtime.
|
||||
*/
|
||||
public function isSupported(): bool;
|
||||
|
||||
public function setGenerator(Generator $generator): void;
|
||||
|
||||
/**
|
||||
* @return array<OA\AbstractAnnotation> top level annotations
|
||||
*/
|
||||
public function build(\Reflector $reflector, Context $context): array;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
|
||||
class AttributeAnnotationFactory implements AnnotationFactoryInterface
|
||||
{
|
||||
/** @var Generator|null */
|
||||
protected $generator;
|
||||
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return \PHP_VERSION_ID >= 80100;
|
||||
}
|
||||
|
||||
public function setGenerator(Generator $generator): void
|
||||
{
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
public function build(\Reflector $reflector, Context $context): array
|
||||
{
|
||||
if (!$this->isSupported() || !method_exists($reflector, 'getAttributes')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($reflector instanceof \ReflectionProperty && method_exists($reflector, 'isPromoted') && $reflector->isPromoted()) {
|
||||
// handled via __construct() parameter
|
||||
return [];
|
||||
}
|
||||
|
||||
// no proper way to inject
|
||||
Generator::$context = $context;
|
||||
|
||||
/** @var OA\AbstractAnnotation[] $annotations */
|
||||
$annotations = [];
|
||||
try {
|
||||
foreach ($reflector->getAttributes() as $attribute) {
|
||||
if (class_exists($attribute->getName())) {
|
||||
$instance = $attribute->newInstance();
|
||||
if ($instance instanceof OA\AbstractAnnotation) {
|
||||
$annotations[] = $instance;
|
||||
}
|
||||
} else {
|
||||
$context->logger->debug(sprintf('Could not instantiate attribute "%s", because class not found.', $attribute->getName()));
|
||||
}
|
||||
}
|
||||
|
||||
if ($reflector instanceof \ReflectionMethod) {
|
||||
// also look at parameter attributes
|
||||
foreach ($reflector->getParameters() as $rp) {
|
||||
foreach ([OA\Property::class, OA\Parameter::class, OA\RequestBody::class] as $attributeName) {
|
||||
foreach ($rp->getAttributes($attributeName, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
||||
/** @var OA\Property|OA\Parameter|OA\RequestBody $instance */
|
||||
$instance = $attribute->newInstance();
|
||||
$type = (($rnt = $rp->getType()) && $rnt instanceof \ReflectionNamedType) ? $rnt->getName() : Generator::UNDEFINED;
|
||||
$nullable = $rnt ? $rnt->allowsNull() : true;
|
||||
|
||||
if ($instance instanceof OA\RequestBody) {
|
||||
$instance->required = !$nullable;
|
||||
} elseif ($instance instanceof OA\Property) {
|
||||
if (Generator::isDefault($instance->property)) {
|
||||
$instance->property = $rp->getName();
|
||||
}
|
||||
if (Generator::isDefault($instance->type)) {
|
||||
$instance->type = $type;
|
||||
}
|
||||
$instance->nullable = $nullable ?: Generator::UNDEFINED;
|
||||
|
||||
if ($rp->isPromoted()) {
|
||||
// promoted parameter - docblock is available via class/property
|
||||
if ($comment = $rp->getDeclaringClass()->getProperty($rp->getName())->getDocComment()) {
|
||||
$instance->_context->comment = $comment;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!$instance->name || Generator::isDefault($instance->name)) {
|
||||
$instance->name = $rp->getName();
|
||||
}
|
||||
$instance->required = !$nullable;
|
||||
$context = new Context(['nested' => $this], $context);
|
||||
$context->comment = null;
|
||||
$instance->merge([new OA\Schema(['type' => $type, '_context' => $context])]);
|
||||
}
|
||||
$annotations[] = $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (($rrt = $reflector->getReturnType()) && $rrt instanceof \ReflectionNamedType) {
|
||||
foreach ($annotations as $annotation) {
|
||||
if ($annotation instanceof OA\Property && Generator::isDefault($annotation->type)) {
|
||||
// pick up simple return types
|
||||
$annotation->type = $rrt->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Generator::$context = null;
|
||||
}
|
||||
|
||||
$annotations = array_values(array_filter($annotations, function ($a) {
|
||||
return $a instanceof OA\AbstractAnnotation;
|
||||
}));
|
||||
|
||||
// merge backwards into parents...
|
||||
$isParent = function (OA\AbstractAnnotation $annotation, OA\AbstractAnnotation $possibleParent): bool {
|
||||
// regular annotation hierarchy
|
||||
$explicitParent = null !== $possibleParent->matchNested($annotation) && !$annotation instanceof OA\Attachable;
|
||||
|
||||
$isParentAllowed = false;
|
||||
// support Attachable subclasses
|
||||
if ($isAttachable = $annotation instanceof OA\Attachable) {
|
||||
if (!$isParentAllowed = (null === $annotation->allowedParents())) {
|
||||
// check for allowed parents
|
||||
foreach ($annotation->allowedParents() as $allowedParent) {
|
||||
if ($possibleParent instanceof $allowedParent) {
|
||||
$isParentAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Property can be nested...
|
||||
return $annotation->getRoot() != $possibleParent->getRoot()
|
||||
&& ($explicitParent || ($isAttachable && $isParentAllowed));
|
||||
};
|
||||
|
||||
$annotationsWithoutParent = [];
|
||||
foreach ($annotations as $index => $annotation) {
|
||||
$mergedIntoParent = false;
|
||||
|
||||
for ($ii = 0; $ii < count($annotations); ++$ii) {
|
||||
if ($ii === $index) {
|
||||
continue;
|
||||
}
|
||||
$possibleParent = $annotations[$ii];
|
||||
if ($isParent($annotation, $possibleParent)) {
|
||||
$mergedIntoParent = true; //
|
||||
$possibleParent->merge([$annotation]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mergedIntoParent) {
|
||||
$annotationsWithoutParent[] = $annotation;
|
||||
}
|
||||
}
|
||||
|
||||
return $annotationsWithoutParent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
|
||||
/**
|
||||
* Scans for classes/interfaces/traits.
|
||||
*
|
||||
* Relies on a `composer --optimized` run in order to utilize
|
||||
* the generated class map.
|
||||
*/
|
||||
class ComposerAutoloaderScanner
|
||||
{
|
||||
/**
|
||||
* Collect all classes/interfaces/traits known by composer.
|
||||
*
|
||||
* @param array<string> $namespaces
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function scan(array $namespaces): array
|
||||
{
|
||||
$units = [];
|
||||
if ($autoloader = $this->getComposerAutoloader()) {
|
||||
foreach (array_keys($autoloader->getClassMap()) as $unit) {
|
||||
foreach ($namespaces as $namespace) {
|
||||
if (0 === strpos($unit, $namespace)) {
|
||||
$units[] = $unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $units;
|
||||
}
|
||||
|
||||
public static function getComposerAutoloader(): ?ClassLoader
|
||||
{
|
||||
foreach (spl_autoload_functions() as $fkt) {
|
||||
if (is_array($fkt) && $fkt[0] instanceof ClassLoader) {
|
||||
return $fkt[0];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
|
||||
class DocBlockAnnotationFactory implements AnnotationFactoryInterface
|
||||
{
|
||||
/** @var DocBlockParser|null */
|
||||
protected $docBlockParser = null;
|
||||
|
||||
/** @var Generator|null */
|
||||
protected $generator = null;
|
||||
|
||||
public function __construct(?DocBlockParser $docBlockParser = null)
|
||||
{
|
||||
$this->docBlockParser = $docBlockParser ?: new DocBlockParser();
|
||||
}
|
||||
|
||||
public function isSupported(): bool
|
||||
{
|
||||
return DocBlockParser::isEnabled();
|
||||
}
|
||||
|
||||
public function setGenerator(Generator $generator): void
|
||||
{
|
||||
$this->generator = $generator;
|
||||
|
||||
$this->docBlockParser->setAliases($generator->getAliases());
|
||||
}
|
||||
|
||||
public function build(\Reflector $reflector, Context $context): array
|
||||
{
|
||||
$aliases = $this->generator ? $this->generator->getAliases() : [];
|
||||
|
||||
if (method_exists($reflector, 'getShortName') && method_exists($reflector, 'getName')) {
|
||||
$aliases[strtolower($reflector->getShortName())] = $reflector->getName();
|
||||
}
|
||||
|
||||
if ($context->with('scanned')) {
|
||||
$details = $context->scanned;
|
||||
foreach ($details['uses'] as $alias => $name) {
|
||||
$aliasKey = strtolower($alias);
|
||||
if ($name != $alias && !array_key_exists($aliasKey, $aliases)) {
|
||||
// real aliases only
|
||||
$aliases[strtolower($alias)] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->docBlockParser->setAliases($aliases);
|
||||
|
||||
if (method_exists($reflector, 'getDocComment') && ($comment = $reflector->getDocComment())) {
|
||||
return $this->docBlockParser->fromComment($comment, $context);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use Doctrine\Common\Annotations\DocParser;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Extract swagger-php annotations from a [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) using Doctrine's DocParser.
|
||||
*/
|
||||
class DocBlockParser
|
||||
{
|
||||
/**
|
||||
* @var DocParser
|
||||
*/
|
||||
protected $docParser;
|
||||
|
||||
/**
|
||||
* @param array<string, class-string> $aliases
|
||||
*/
|
||||
public function __construct(array $aliases = [])
|
||||
{
|
||||
if (DocBlockParser::isEnabled()) {
|
||||
$docParser = new DocParser();
|
||||
$docParser->setIgnoreNotImportedAnnotations(true);
|
||||
$docParser->setImports($aliases);
|
||||
$this->docParser = $docParser;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can process annotations.
|
||||
*/
|
||||
public static function isEnabled(): bool
|
||||
{
|
||||
return class_exists('Doctrine\\Common\\Annotations\\DocParser');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, class-string> $aliases
|
||||
*/
|
||||
public function setAliases(array $aliases): void
|
||||
{
|
||||
$this->docParser->setImports($aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use doctrine to parse the comment block and return the detected annotations.
|
||||
*
|
||||
* @param string $comment a T_DOC_COMMENT
|
||||
* @param Context $context
|
||||
*
|
||||
* @return array<OA\AbstractAnnotation>
|
||||
*/
|
||||
public function fromComment(string $comment, Context $context): array
|
||||
{
|
||||
$context->comment = $comment;
|
||||
|
||||
try {
|
||||
Generator::$context = $context;
|
||||
if ($context->is('annotations') === false) {
|
||||
$context->annotations = [];
|
||||
}
|
||||
|
||||
return $this->docParser->parse($comment, $context->getDebugLocation());
|
||||
} catch (\Exception $e) {
|
||||
if (preg_match('/^(.+) at position ([0-9]+) in ' . preg_quote((string) $context, '/') . '\.$/', $e->getMessage(), $matches)) {
|
||||
$errorMessage = $matches[1];
|
||||
$errorPos = (int) $matches[2];
|
||||
$atPos = strpos($comment, '@');
|
||||
$context->line -= substr_count($comment, "\n", $atPos + $errorPos) + 1;
|
||||
$lines = explode("\n", substr($comment, $atPos, $errorPos));
|
||||
$context->character = strlen(array_pop($lines)) + 1; // position starts at 0 character starts at 1
|
||||
$context->logger->error($errorMessage . ' in ' . $context, ['exception' => $e]);
|
||||
} else {
|
||||
$context->logger->error(
|
||||
$e->getMessage() . ($context->filename ? ('; file=' . $context->filename) : ''),
|
||||
['exception' => $e]
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
} finally {
|
||||
Generator::$context = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* OpenApi analyser using reflection.
|
||||
*
|
||||
* Can read either PHP `DocBlock`s or `Attribute`s.
|
||||
*
|
||||
* Due to the nature of reflection this requires all related classes
|
||||
* to be auto-loadable.
|
||||
*/
|
||||
class ReflectionAnalyser implements AnalyserInterface
|
||||
{
|
||||
/** @var AnnotationFactoryInterface[] */
|
||||
protected $annotationFactories;
|
||||
|
||||
/** @var Generator|null */
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* @param array<AnnotationFactoryInterface> $annotationFactories
|
||||
*/
|
||||
public function __construct(array $annotationFactories = [])
|
||||
{
|
||||
$this->annotationFactories = [];
|
||||
foreach ($annotationFactories as $annotationFactory) {
|
||||
if ($annotationFactory->isSupported()) {
|
||||
$this->annotationFactories[] = $annotationFactory;
|
||||
}
|
||||
}
|
||||
if (!$this->annotationFactories) {
|
||||
throw new \RuntimeException('No suitable annotation factory found. At least one of "Doctrine Annotations" or PHP 8.1 are required');
|
||||
}
|
||||
}
|
||||
|
||||
public function setGenerator(Generator $generator): void
|
||||
{
|
||||
$this->generator = $generator;
|
||||
|
||||
foreach ($this->annotationFactories as $annotationFactory) {
|
||||
$annotationFactory->setGenerator($generator);
|
||||
}
|
||||
}
|
||||
|
||||
public function fromFile(string $filename, Context $context): Analysis
|
||||
{
|
||||
$scanner = new TokenScanner();
|
||||
$fileDetails = $scanner->scanFile($filename);
|
||||
|
||||
$analysis = new Analysis([], $context);
|
||||
foreach ($fileDetails as $fqdn => $details) {
|
||||
$this->analyzeFqdn($fqdn, $analysis, $details);
|
||||
}
|
||||
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
public function fromFqdn(string $fqdn, Analysis $analysis): Analysis
|
||||
{
|
||||
$fqdn = ltrim($fqdn, '\\');
|
||||
|
||||
$rc = new \ReflectionClass($fqdn);
|
||||
if (!$filename = $rc->getFileName()) {
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
$scanner = new TokenScanner();
|
||||
$fileDetails = $scanner->scanFile($filename);
|
||||
|
||||
$this->analyzeFqdn($fqdn, $analysis, $fileDetails[$fqdn]);
|
||||
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
protected function analyzeFqdn(string $fqdn, Analysis $analysis, array $details): Analysis
|
||||
{
|
||||
if (!class_exists($fqdn) && !interface_exists($fqdn) && !trait_exists($fqdn) && (!function_exists('enum_exists') || !enum_exists($fqdn))) {
|
||||
$analysis->context->logger->warning('Skipping unknown ' . $fqdn);
|
||||
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
$rc = new \ReflectionClass($fqdn);
|
||||
$contextType = $rc->isInterface() ? 'interface' : ($rc->isTrait() ? 'trait' : ((method_exists($rc, 'isEnum') && $rc->isEnum()) ? 'enum' : 'class'));
|
||||
$context = new Context([
|
||||
$contextType => $rc->getShortName(),
|
||||
'namespace' => $rc->getNamespaceName() ?: null,
|
||||
'uses' => $details['uses'],
|
||||
'comment' => $rc->getDocComment() ?: null,
|
||||
'filename' => $rc->getFileName() ?: null,
|
||||
'line' => $rc->getStartLine(),
|
||||
'annotations' => [],
|
||||
'scanned' => $details,
|
||||
], $analysis->context);
|
||||
|
||||
$definition = [
|
||||
$contextType => $rc->getShortName(),
|
||||
'extends' => null,
|
||||
'implements' => [],
|
||||
'traits' => [],
|
||||
'properties' => [],
|
||||
'methods' => [],
|
||||
'context' => $context,
|
||||
];
|
||||
$normaliseClass = function (string $name): string {
|
||||
return '\\' . ltrim($name, '\\');
|
||||
};
|
||||
if ($parentClass = $rc->getParentClass()) {
|
||||
$definition['extends'] = $normaliseClass($parentClass->getName());
|
||||
}
|
||||
$definition[$contextType == 'class' ? 'implements' : 'extends'] = array_map($normaliseClass, $details['interfaces']);
|
||||
$definition['traits'] = array_map($normaliseClass, $details['traits']);
|
||||
|
||||
foreach ($this->annotationFactories as $annotationFactory) {
|
||||
$analysis->addAnnotations($annotationFactory->build($rc, $context), $context);
|
||||
}
|
||||
|
||||
foreach ($rc->getMethods() as $method) {
|
||||
if (in_array($method->name, $details['methods'])) {
|
||||
$definition['methods'][$method->getName()] = $ctx = new Context([
|
||||
'method' => $method->getName(),
|
||||
'comment' => $method->getDocComment() ?: null,
|
||||
'filename' => $method->getFileName() ?: null,
|
||||
'line' => $method->getStartLine(),
|
||||
'annotations' => [],
|
||||
], $context);
|
||||
foreach ($this->annotationFactories as $annotationFactory) {
|
||||
$analysis->addAnnotations($annotationFactory->build($method, $ctx), $ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rc->getProperties() as $property) {
|
||||
if (in_array($property->name, $details['properties'])) {
|
||||
$definition['properties'][$property->getName()] = $ctx = new Context([
|
||||
'property' => $property->getName(),
|
||||
'comment' => $property->getDocComment() ?: null,
|
||||
'annotations' => [],
|
||||
], $context);
|
||||
if ($property->isStatic()) {
|
||||
$ctx->static = true;
|
||||
}
|
||||
if (\PHP_VERSION_ID >= 70400 && ($type = $property->getType())) {
|
||||
$ctx->nullable = $type->allowsNull();
|
||||
if ($type instanceof \ReflectionNamedType) {
|
||||
$ctx->type = $type->getName();
|
||||
// Context::fullyQualifiedName(...) expects this
|
||||
if (class_exists($absFqn = '\\' . $ctx->type)) {
|
||||
$ctx->type = $absFqn;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($this->annotationFactories as $annotationFactory) {
|
||||
$analysis->addAnnotations($annotationFactory->build($property, $ctx), $ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rc->getReflectionConstants() as $constant) {
|
||||
foreach ($this->annotationFactories as $annotationFactory) {
|
||||
$definition['constants'][$constant->getName()] = $ctx = new Context([
|
||||
'constant' => $constant->getName(),
|
||||
'comment' => $constant->getDocComment() ?: null,
|
||||
'annotations' => [],
|
||||
], $context);
|
||||
foreach ($annotationFactory->build($constant, $ctx) as $annotation) {
|
||||
if ($annotation instanceof OA\Property) {
|
||||
if (Generator::isDefault($annotation->property)) {
|
||||
$annotation->property = $constant->getName();
|
||||
}
|
||||
if (Generator::isDefault($annotation->const)) {
|
||||
$annotation->const = $constant->getValue();
|
||||
}
|
||||
$analysis->addAnnotation($annotation, $ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$addDefinition = 'add' . ucfirst($contextType) . 'Definition';
|
||||
$analysis->{$addDefinition}($definition);
|
||||
|
||||
return $analysis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Extracts swagger-php annotations from php code using static analysis.
|
||||
*/
|
||||
class TokenAnalyser implements AnalyserInterface
|
||||
{
|
||||
/** @var Generator|null */
|
||||
protected $generator;
|
||||
|
||||
public function setGenerator(Generator $generator): void
|
||||
{
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and process all doc-comments from a file.
|
||||
*
|
||||
* @param string $filename path to a php file
|
||||
*/
|
||||
public function fromFile(string $filename, Context $context): Analysis
|
||||
{
|
||||
if (function_exists('opcache_get_status') && function_exists('opcache_get_configuration')) {
|
||||
if (empty($GLOBALS['openapi_opcache_warning'])) {
|
||||
$GLOBALS['openapi_opcache_warning'] = true;
|
||||
$status = opcache_get_status();
|
||||
$config = opcache_get_configuration();
|
||||
if (is_array($status) && $status['opcache_enabled'] && $config['directives']['opcache.save_comments'] == false) {
|
||||
$context->logger->error("php.ini \"opcache.save_comments = 0\" interferes with extracting annotations.\n[LINK] https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.save-comments");
|
||||
}
|
||||
}
|
||||
}
|
||||
$tokens = token_get_all(file_get_contents($filename));
|
||||
|
||||
return $this->fromTokens($tokens, new Context(['filename' => $filename], $context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and process all doc-comments from the contents.
|
||||
*
|
||||
* @param string $code PHP code. (including <?php tags)
|
||||
* @param Context $context the original location of the contents
|
||||
*/
|
||||
public function fromCode(string $code, Context $context): Analysis
|
||||
{
|
||||
$tokens = token_get_all($code);
|
||||
|
||||
return $this->fromTokens($tokens, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared implementation for parseFile() & parseContents().
|
||||
*
|
||||
* @param array $tokens The result of a token_get_all()
|
||||
*/
|
||||
protected function fromTokens(array $tokens, Context $parseContext): Analysis
|
||||
{
|
||||
$generator = $this->generator ?: new Generator();
|
||||
$analysis = new Analysis([], $parseContext);
|
||||
$docBlockParser = new DocBlockParser($generator->getAliases());
|
||||
|
||||
reset($tokens);
|
||||
$token = '';
|
||||
|
||||
$aliases = $generator->getAliases();
|
||||
|
||||
$parseContext->uses = [];
|
||||
// default to parse context to start with
|
||||
$schemaContext = $parseContext;
|
||||
|
||||
$classDefinition = false;
|
||||
$interfaceDefinition = false;
|
||||
$traitDefinition = false;
|
||||
$enumDefinition = false;
|
||||
$comment = false;
|
||||
|
||||
$line = 0;
|
||||
$lineOffset = $parseContext->line ?: 0;
|
||||
|
||||
while ($token !== false) {
|
||||
$previousToken = $token;
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if (is_array($token) === false) {
|
||||
// Ignore tokens like "{", "}", etc
|
||||
continue;
|
||||
}
|
||||
|
||||
if (defined('T_ATTRIBUTE') && $token[0] === T_ATTRIBUTE) {
|
||||
// consume
|
||||
$this->parseAttribute($tokens, $token, $parseContext);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token[0] === T_DOC_COMMENT) {
|
||||
if ($comment) {
|
||||
// 2 Doc-comments in succession?
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, new Context(['line' => $line], $schemaContext));
|
||||
}
|
||||
$comment = $token[1];
|
||||
$line = $token[2] + $lineOffset;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($token[0], [T_ABSTRACT, T_FINAL])) {
|
||||
// skip
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
}
|
||||
|
||||
if ($token[0] === T_CLASS) {
|
||||
// Doc-comment before a class?
|
||||
if (is_array($previousToken) && $previousToken[0] === T_DOUBLE_COLON) {
|
||||
// php 5.5 class name resolution (i.e. ClassName::class)
|
||||
continue;
|
||||
}
|
||||
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if (is_string($token) && ($token === '(' || $token === '{')) {
|
||||
// php7 anonymous classes (i.e. new class() { public function foo() {} };)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($token) && ($token[1] === 'extends' || $token[1] === 'implements')) {
|
||||
// php7 anonymous classes with extends (i.e. new class() extends { public function foo() {} };)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($token)) {
|
||||
// PHP 8 named argument
|
||||
continue;
|
||||
}
|
||||
|
||||
$interfaceDefinition = false;
|
||||
$traitDefinition = false;
|
||||
$enumDefinition = false;
|
||||
|
||||
$schemaContext = new Context(['class' => $token[1], 'line' => $token[2]], $parseContext);
|
||||
if ($classDefinition) {
|
||||
$analysis->addClassDefinition($classDefinition);
|
||||
}
|
||||
$classDefinition = [
|
||||
'class' => $token[1],
|
||||
'extends' => null,
|
||||
'properties' => [],
|
||||
'methods' => [],
|
||||
'context' => $schemaContext,
|
||||
];
|
||||
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if ($token[0] === T_EXTENDS) {
|
||||
$schemaContext->extends = $this->parseNamespace($tokens, $token, $parseContext);
|
||||
$classDefinition['extends'] = $schemaContext->fullyQualifiedName($schemaContext->extends);
|
||||
}
|
||||
|
||||
if ($token[0] === T_IMPLEMENTS) {
|
||||
$schemaContext->implements = $this->parseNamespaceList($tokens, $token, $parseContext);
|
||||
$classDefinition['implements'] = array_map([$schemaContext, 'fullyQualifiedName'], $schemaContext->implements);
|
||||
}
|
||||
|
||||
if ($comment) {
|
||||
$schemaContext->line = $line;
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext);
|
||||
$comment = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo detect end-of-class and reset $schemaContext
|
||||
}
|
||||
|
||||
if ($token[0] === T_INTERFACE) { // Doc-comment before an interface?
|
||||
$classDefinition = false;
|
||||
$traitDefinition = false;
|
||||
$enumDefinition = false;
|
||||
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if (!is_array($token)) {
|
||||
// PHP 8 named argument
|
||||
continue;
|
||||
}
|
||||
|
||||
$schemaContext = new Context(['interface' => $token[1], 'line' => $token[2]], $parseContext);
|
||||
if ($interfaceDefinition) {
|
||||
$analysis->addInterfaceDefinition($interfaceDefinition);
|
||||
}
|
||||
$interfaceDefinition = [
|
||||
'interface' => $token[1],
|
||||
'extends' => null,
|
||||
'properties' => [],
|
||||
'methods' => [],
|
||||
'context' => $schemaContext,
|
||||
];
|
||||
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if ($token[0] === T_EXTENDS) {
|
||||
$schemaContext->extends = $this->parseNamespaceList($tokens, $token, $parseContext);
|
||||
$interfaceDefinition['extends'] = array_map([$schemaContext, 'fullyQualifiedName'], $schemaContext->extends);
|
||||
}
|
||||
|
||||
if ($comment) {
|
||||
$schemaContext->line = $line;
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext);
|
||||
$comment = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo detect end-of-interface and reset $schemaContext
|
||||
}
|
||||
|
||||
if ($token[0] === T_TRAIT) {
|
||||
$classDefinition = false;
|
||||
$interfaceDefinition = false;
|
||||
$enumDefinition = false;
|
||||
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if (!is_array($token)) {
|
||||
// PHP 8 named argument
|
||||
continue;
|
||||
}
|
||||
|
||||
$schemaContext = new Context(['trait' => $token[1], 'line' => $token[2]], $parseContext);
|
||||
if ($traitDefinition) {
|
||||
$analysis->addTraitDefinition($traitDefinition);
|
||||
}
|
||||
$traitDefinition = [
|
||||
'trait' => $token[1],
|
||||
'properties' => [],
|
||||
'methods' => [],
|
||||
'context' => $schemaContext,
|
||||
];
|
||||
|
||||
if ($comment) {
|
||||
$schemaContext->line = $line;
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext);
|
||||
$comment = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo detect end-of-trait and reset $schemaContext
|
||||
}
|
||||
|
||||
if (defined('T_ENUM') && $token[0] === T_ENUM) {
|
||||
$classDefinition = false;
|
||||
$interfaceDefinition = false;
|
||||
$traitDefinition = false;
|
||||
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if (!is_array($token)) {
|
||||
// PHP 8 named argument
|
||||
continue;
|
||||
}
|
||||
|
||||
$schemaContext = new Context(['enum' => $token[1], 'line' => $token[2]], $parseContext);
|
||||
if ($enumDefinition) {
|
||||
$analysis->addEnumDefinition($enumDefinition);
|
||||
}
|
||||
$enumDefinition = [
|
||||
'enum' => $token[1],
|
||||
'properties' => [],
|
||||
'methods' => [],
|
||||
'context' => $schemaContext,
|
||||
];
|
||||
|
||||
if ($comment) {
|
||||
$schemaContext->line = $line;
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $schemaContext);
|
||||
$comment = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo detect end-of-trait and reset $schemaContext
|
||||
}
|
||||
|
||||
if ($token[0] === T_STATIC) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_VARIABLE) {
|
||||
// static property
|
||||
$propertyContext = new Context(
|
||||
[
|
||||
'property' => substr($token[1], 1),
|
||||
'static' => true,
|
||||
'line' => $line,
|
||||
],
|
||||
$schemaContext
|
||||
);
|
||||
|
||||
if ($classDefinition) {
|
||||
$classDefinition['properties'][$propertyContext->property] = $propertyContext;
|
||||
}
|
||||
if ($traitDefinition) {
|
||||
$traitDefinition['properties'][$propertyContext->property] = $propertyContext;
|
||||
}
|
||||
if ($comment) {
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $propertyContext);
|
||||
$comment = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($token[0], [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR])) { // Scope
|
||||
[$type, $nullable, $token] = $this->parseTypeAndNextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_VARIABLE) {
|
||||
// instance property
|
||||
$propertyContext = new Context(
|
||||
[
|
||||
'property' => substr($token[1], 1),
|
||||
'type' => $type,
|
||||
'nullable' => $nullable,
|
||||
'line' => $line,
|
||||
],
|
||||
$schemaContext
|
||||
);
|
||||
|
||||
if ($classDefinition) {
|
||||
$classDefinition['properties'][$propertyContext->property] = $propertyContext;
|
||||
}
|
||||
if ($interfaceDefinition) {
|
||||
$interfaceDefinition['properties'][$propertyContext->property] = $propertyContext;
|
||||
}
|
||||
if ($traitDefinition) {
|
||||
$traitDefinition['properties'][$propertyContext->property] = $propertyContext;
|
||||
}
|
||||
if ($comment) {
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $propertyContext);
|
||||
$comment = false;
|
||||
}
|
||||
} elseif ($token[0] === T_FUNCTION) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_STRING) {
|
||||
$methodContext = new Context(
|
||||
[
|
||||
'method' => $token[1],
|
||||
'line' => $line,
|
||||
],
|
||||
$schemaContext
|
||||
);
|
||||
|
||||
if ($classDefinition) {
|
||||
$classDefinition['methods'][$token[1]] = $methodContext;
|
||||
}
|
||||
if ($interfaceDefinition) {
|
||||
$interfaceDefinition['methods'][$token[1]] = $methodContext;
|
||||
}
|
||||
if ($traitDefinition) {
|
||||
$traitDefinition['methods'][$token[1]] = $methodContext;
|
||||
}
|
||||
if ($comment) {
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $methodContext);
|
||||
$comment = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} elseif ($token[0] === T_FUNCTION) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if ($token[0] === T_STRING) {
|
||||
$methodContext = new Context(
|
||||
[
|
||||
'method' => $token[1],
|
||||
'line' => $line,
|
||||
],
|
||||
$schemaContext
|
||||
);
|
||||
|
||||
if ($classDefinition) {
|
||||
$classDefinition['methods'][$token[1]] = $methodContext;
|
||||
}
|
||||
if ($interfaceDefinition) {
|
||||
$interfaceDefinition['methods'][$token[1]] = $methodContext;
|
||||
}
|
||||
if ($traitDefinition) {
|
||||
$traitDefinition['methods'][$token[1]] = $methodContext;
|
||||
}
|
||||
if ($comment) {
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, $methodContext);
|
||||
$comment = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($token[0], [T_NAMESPACE, T_USE]) === false) {
|
||||
// Skip "use" & "namespace" to prevent "never imported" warnings)
|
||||
if ($comment) {
|
||||
// Not a doc-comment for a class, property or method?
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, new Context(['line' => $line], $schemaContext));
|
||||
$comment = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($token[0] === T_NAMESPACE) {
|
||||
$parseContext->namespace = $this->parseNamespace($tokens, $token, $parseContext);
|
||||
$aliases['__NAMESPACE__'] = $parseContext->namespace;
|
||||
$docBlockParser->setAliases($aliases);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token[0] === T_USE) {
|
||||
$statements = $this->parseUseStatement($tokens, $token, $parseContext);
|
||||
foreach ($statements as $alias => $target) {
|
||||
if ($classDefinition) {
|
||||
// class traits
|
||||
$classDefinition['traits'][] = $schemaContext->fullyQualifiedName($target);
|
||||
} elseif ($traitDefinition) {
|
||||
// trait traits
|
||||
$traitDefinition['traits'][] = $schemaContext->fullyQualifiedName($target);
|
||||
} else {
|
||||
// not a trait use
|
||||
$parseContext->uses[$alias] = $target;
|
||||
|
||||
$namespaces = $generator->getNamespaces();
|
||||
if (null === $namespaces) {
|
||||
$aliases[strtolower($alias)] = $target;
|
||||
} else {
|
||||
foreach ($namespaces as $namespace) {
|
||||
if (strcasecmp(substr($target . '\\', 0, strlen($namespace)), $namespace) === 0) {
|
||||
$aliases[strtolower($alias)] = $target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$docBlockParser->setAliases($aliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup final comment and definition
|
||||
if ($comment) {
|
||||
$this->analyseComment($analysis, $docBlockParser, $comment, new Context(['line' => $line], $schemaContext));
|
||||
}
|
||||
if ($classDefinition) {
|
||||
$analysis->addClassDefinition($classDefinition);
|
||||
}
|
||||
if ($interfaceDefinition) {
|
||||
$analysis->addInterfaceDefinition($interfaceDefinition);
|
||||
}
|
||||
if ($traitDefinition) {
|
||||
$analysis->addTraitDefinition($traitDefinition);
|
||||
}
|
||||
if ($enumDefinition) {
|
||||
$analysis->addEnumDefinition($enumDefinition);
|
||||
}
|
||||
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse comment and add annotations to analysis.
|
||||
*/
|
||||
private function analyseComment(Analysis $analysis, DocBlockParser $docBlockParser, string $comment, Context $context): void
|
||||
{
|
||||
$analysis->addAnnotations($docBlockParser->fromComment($comment, $context), $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* The next non-whitespace, non-comment token.
|
||||
*
|
||||
*
|
||||
* @return array|string The next token (or false)
|
||||
*/
|
||||
private function nextToken(array &$tokens, Context $context)
|
||||
{
|
||||
while (true) {
|
||||
$token = next($tokens);
|
||||
if (is_array($token)) {
|
||||
if ($token[0] === T_WHITESPACE) {
|
||||
continue;
|
||||
}
|
||||
if ($token[0] === T_COMMENT) {
|
||||
$pos = strpos($token[1], '@OA\\');
|
||||
if ($pos) {
|
||||
$line = $context->line ? $context->line + $token[2] : $token[2];
|
||||
$commentContext = new Context(['line' => $line], $context);
|
||||
$context->logger->warning('Annotations are only parsed inside `/**` DocBlocks, skipping ' . $commentContext);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
|
||||
private function parseAttribute(array &$tokens, &$token, Context $parseContext): void
|
||||
{
|
||||
$nesting = 1;
|
||||
while ($token !== false) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if (!is_array($token) && '[' === $token) {
|
||||
++$nesting;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($token) && ']' === $token) {
|
||||
--$nesting;
|
||||
if (!$nesting) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function php8NamespaceToken(): array
|
||||
{
|
||||
return defined('T_NAME_QUALIFIED') ? [T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse namespaced string.
|
||||
*
|
||||
* @param array|string $token
|
||||
*/
|
||||
private function parseNamespace(array &$tokens, &$token, Context $parseContext): string
|
||||
{
|
||||
$namespace = '';
|
||||
$nsToken = array_merge([T_STRING, T_NS_SEPARATOR], $this->php8NamespaceToken());
|
||||
while ($token !== false) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
if (!in_array($token[0], $nsToken)) {
|
||||
break;
|
||||
}
|
||||
$namespace .= $token[1];
|
||||
}
|
||||
|
||||
return $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse comma separated list of namespaced strings.
|
||||
*
|
||||
* @param array|string $token
|
||||
*/
|
||||
private function parseNamespaceList(array &$tokens, &$token, Context $parseContext): array
|
||||
{
|
||||
$namespaces = [];
|
||||
while ($namespace = $this->parseNamespace($tokens, $token, $parseContext)) {
|
||||
$namespaces[] = $namespace;
|
||||
if ($token != ',') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a use statement.
|
||||
*
|
||||
* @param (int|mixed)[]|string $token
|
||||
*/
|
||||
private function parseUseStatement(array &$tokens, &$token, Context $parseContext): array
|
||||
{
|
||||
$normalizeAlias = function ($alias): string {
|
||||
$alias = ltrim($alias, '\\');
|
||||
$elements = explode('\\', $alias);
|
||||
|
||||
return array_pop($elements);
|
||||
};
|
||||
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$statements = [];
|
||||
$explicitAlias = false;
|
||||
$nsToken = array_merge([T_STRING, T_NS_SEPARATOR], $this->php8NamespaceToken());
|
||||
while ($token !== false) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
$isNameToken = in_array($token[0], $nsToken);
|
||||
if (!$explicitAlias && $isNameToken) {
|
||||
$class .= $token[1];
|
||||
$alias = $token[1];
|
||||
} elseif ($explicitAlias && $isNameToken) {
|
||||
$alias .= $token[1];
|
||||
} elseif ($token[0] === T_AS) {
|
||||
$explicitAlias = true;
|
||||
$alias = '';
|
||||
} elseif ($token === ',') {
|
||||
$statements[$normalizeAlias($alias)] = $class;
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$explicitAlias = false;
|
||||
} elseif ($token === ';') {
|
||||
$statements[$normalizeAlias($alias)] = $class;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse type of variable (if it exists).
|
||||
*/
|
||||
private function parseTypeAndNextToken(array &$tokens, Context $parseContext): array
|
||||
{
|
||||
$type = Generator::UNDEFINED;
|
||||
$nullable = false;
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
|
||||
if ($token[0] === T_STATIC) {
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
}
|
||||
|
||||
if ($token === '?') { // nullable type
|
||||
$nullable = true;
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
}
|
||||
|
||||
$qualifiedToken = array_merge([T_NS_SEPARATOR, T_STRING, T_ARRAY], $this->php8NamespaceToken());
|
||||
$typeToken = array_merge([T_STRING], $this->php8NamespaceToken());
|
||||
// drill down namespace segments to basename property type declaration
|
||||
while (in_array($token[0], $qualifiedToken)) {
|
||||
if (in_array($token[0], $typeToken)) {
|
||||
$type = $token[1];
|
||||
}
|
||||
$token = $this->nextToken($tokens, $parseContext);
|
||||
}
|
||||
|
||||
return [$type, $nullable, $token];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Analysers;
|
||||
|
||||
/**
|
||||
* High level, PHP token based, scanner.
|
||||
*/
|
||||
class TokenScanner
|
||||
{
|
||||
/**
|
||||
* Scan file for all classes, interfaces and traits.
|
||||
*
|
||||
* @return string[][] File details
|
||||
*/
|
||||
public function scanFile(string $filename): array
|
||||
{
|
||||
return $this->scanTokens(token_get_all(file_get_contents($filename)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan file for all classes, interfaces and traits.
|
||||
*
|
||||
* @return array<string, array<string, mixed>> File details
|
||||
*/
|
||||
protected function scanTokens(array $tokens): array
|
||||
{
|
||||
$units = [];
|
||||
$uses = [];
|
||||
$isInterface = false;
|
||||
$isAbstractFunction = false;
|
||||
$namespace = '';
|
||||
$currentName = null;
|
||||
$unitLevel = 0;
|
||||
$lastToken = null;
|
||||
$stack = [];
|
||||
|
||||
$initUnit = function ($uses): array {
|
||||
return [
|
||||
'uses' => $uses,
|
||||
'interfaces' => [],
|
||||
'traits' => [],
|
||||
'enums' => [],
|
||||
'methods' => [],
|
||||
'properties' => [],
|
||||
];
|
||||
};
|
||||
|
||||
while (false !== ($token = $this->nextToken($tokens))) {
|
||||
// named arguments
|
||||
$nextToken = $this->nextToken($tokens);
|
||||
if (($token !== '}' && $nextToken === ':') || $nextToken === false) {
|
||||
continue;
|
||||
}
|
||||
do {
|
||||
$prevToken = prev($tokens);
|
||||
} while ($token !== $prevToken);
|
||||
|
||||
if (!is_array($token)) {
|
||||
switch ($token) {
|
||||
case '{':
|
||||
$stack[] = $token;
|
||||
break;
|
||||
case '}':
|
||||
array_pop($stack);
|
||||
if (count($stack) == $unitLevel) {
|
||||
$currentName = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($token[0]) {
|
||||
case T_ABSTRACT:
|
||||
if (count($stack)) {
|
||||
$isAbstractFunction = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_CURLY_OPEN:
|
||||
case T_DOLLAR_OPEN_CURLY_BRACES:
|
||||
$stack[] = $token[1];
|
||||
break;
|
||||
|
||||
case T_NAMESPACE:
|
||||
$namespace = $this->nextWord($tokens);
|
||||
break;
|
||||
|
||||
case T_USE:
|
||||
if (!$stack) {
|
||||
$uses = array_merge($uses, $this->parseFQNStatement($tokens, $token));
|
||||
} elseif ($currentName) {
|
||||
$traits = $this->resolveFQN($this->parseFQNStatement($tokens, $token), $namespace, $uses);
|
||||
$units[$currentName]['traits'] = array_merge($units[$currentName]['traits'], $traits);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_CLASS:
|
||||
if ($currentName) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($lastToken && is_array($lastToken) && $lastToken[0] === T_DOUBLE_COLON) {
|
||||
// ::class
|
||||
break;
|
||||
}
|
||||
|
||||
// class name
|
||||
$token = $this->nextToken($tokens);
|
||||
|
||||
// unless ...
|
||||
if (is_string($token) && ($token === '(' || $token === '{')) {
|
||||
// new class[()] { ... }
|
||||
if ('{' == $token) {
|
||||
prev($tokens);
|
||||
}
|
||||
break;
|
||||
} elseif (is_array($token) && in_array($token[1], ['extends', 'implements'])) {
|
||||
// new class[()] extends { ... }
|
||||
break;
|
||||
}
|
||||
|
||||
$isInterface = false;
|
||||
$currentName = $namespace . '\\' . $token[1];
|
||||
$unitLevel = count($stack);
|
||||
$units[$currentName] = $initUnit($uses);
|
||||
break;
|
||||
|
||||
case T_INTERFACE:
|
||||
if ($currentName) {
|
||||
break;
|
||||
}
|
||||
|
||||
$isInterface = true;
|
||||
$token = $this->nextToken($tokens);
|
||||
$currentName = $namespace . '\\' . $token[1];
|
||||
$unitLevel = count($stack);
|
||||
$units[$currentName] = $initUnit($uses);
|
||||
break;
|
||||
|
||||
case T_EXTENDS:
|
||||
$fqns = $this->parseFQNStatement($tokens, $token);
|
||||
if ($isInterface && $currentName) {
|
||||
$units[$currentName]['interfaces'] = $this->resolveFQN($fqns, $namespace, $uses);
|
||||
}
|
||||
if (!is_array($token) || T_IMPLEMENTS !== $token[0]) {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
case T_IMPLEMENTS:
|
||||
$fqns = $this->parseFQNStatement($tokens, $token);
|
||||
if ($currentName) {
|
||||
$units[$currentName]['interfaces'] = $this->resolveFQN($fqns, $namespace, $uses);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_FUNCTION:
|
||||
$token = $this->nextToken($tokens);
|
||||
if ((!is_array($token) && '&' == $token)
|
||||
|| (defined('T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') && T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG == $token[0])) {
|
||||
$token = $this->nextToken($tokens);
|
||||
}
|
||||
|
||||
if (($unitLevel + 1) == count($stack) && $currentName) {
|
||||
$units[$currentName]['methods'][] = $token[1];
|
||||
if (!$isInterface && !$isAbstractFunction) {
|
||||
// more nesting
|
||||
$units[$currentName]['properties'] = array_merge(
|
||||
$units[$currentName]['properties'],
|
||||
$this->parsePromotedProperties($tokens)
|
||||
);
|
||||
$this->skipTo($tokens, '{', true);
|
||||
} else {
|
||||
// no function body
|
||||
$this->skipTo($tokens, ';');
|
||||
$isAbstractFunction = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case T_VARIABLE:
|
||||
if (($unitLevel + 1) == count($stack) && $currentName) {
|
||||
$units[$currentName]['properties'][] = substr($token[1], 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// handle trait here too to avoid duplication
|
||||
if (T_TRAIT === $token[0] || (defined('T_ENUM') && T_ENUM === $token[0])) {
|
||||
if ($currentName) {
|
||||
break;
|
||||
}
|
||||
|
||||
$isInterface = false;
|
||||
$token = $this->nextToken($tokens);
|
||||
$currentName = $namespace . '\\' . $token[1];
|
||||
$unitLevel = count($stack);
|
||||
$this->skipTo($tokens, '{', true);
|
||||
$units[$currentName] = $initUnit($uses);
|
||||
}
|
||||
break;
|
||||
}
|
||||
$lastToken = $token;
|
||||
}
|
||||
|
||||
return $units;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next token that is not whitespace or comment.
|
||||
*
|
||||
* @return string|array|false
|
||||
*/
|
||||
protected function nextToken(array &$tokens)
|
||||
{
|
||||
$token = true;
|
||||
while ($token) {
|
||||
$token = next($tokens);
|
||||
if (is_array($token)) {
|
||||
if (in_array($token[0], [T_WHITESPACE, T_COMMENT])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function resolveFQN(array $names, string $namespace, array $uses): array
|
||||
{
|
||||
$resolve = function ($name) use ($namespace, $uses) {
|
||||
if ('\\' == $name[0]) {
|
||||
return substr($name, 1);
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $uses)) {
|
||||
return $uses[$name];
|
||||
}
|
||||
|
||||
return $namespace . '\\' . $name;
|
||||
};
|
||||
|
||||
return array_values(array_map($resolve, $names));
|
||||
}
|
||||
|
||||
protected function skipTo(array &$tokens, string $char, bool $prev = false): void
|
||||
{
|
||||
while (false !== ($token = next($tokens))) {
|
||||
if (is_string($token) && $token == $char) {
|
||||
if ($prev) {
|
||||
prev($tokens);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read next word.
|
||||
*
|
||||
* Skips leading whitespace.
|
||||
*/
|
||||
protected function nextWord(array &$tokens): string
|
||||
{
|
||||
$word = '';
|
||||
while (false !== ($token = next($tokens))) {
|
||||
if (is_array($token)) {
|
||||
if ($token[0] === T_WHITESPACE) {
|
||||
if ($word) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$word .= $token[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a use statement.
|
||||
*/
|
||||
protected function parseFQNStatement(array &$tokens, array &$token): array
|
||||
{
|
||||
$normalizeAlias = function ($alias): string {
|
||||
$alias = ltrim($alias, '\\');
|
||||
$elements = explode('\\', $alias);
|
||||
|
||||
return array_pop($elements);
|
||||
};
|
||||
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$statements = [];
|
||||
$explicitAlias = false;
|
||||
$php8NSToken = defined('T_NAME_QUALIFIED') ? [T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED] : [];
|
||||
$nsToken = array_merge([T_STRING, T_NS_SEPARATOR], $php8NSToken);
|
||||
while ($token !== false) {
|
||||
$token = $this->nextToken($tokens);
|
||||
$isNameToken = in_array($token[0], $nsToken);
|
||||
if (!$explicitAlias && $isNameToken) {
|
||||
$class .= $token[1];
|
||||
$alias = $token[1];
|
||||
} elseif ($explicitAlias && $isNameToken) {
|
||||
$alias .= $token[1];
|
||||
} elseif ($token[0] === T_AS) {
|
||||
$explicitAlias = true;
|
||||
$alias = '';
|
||||
} elseif ($token[0] === T_IMPLEMENTS) {
|
||||
$statements[$normalizeAlias($alias)] = $class;
|
||||
break;
|
||||
} elseif ($token === ',') {
|
||||
$statements[$normalizeAlias($alias)] = $class;
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$explicitAlias = false;
|
||||
} elseif ($token === ';') {
|
||||
$statements[$normalizeAlias($alias)] = $class;
|
||||
break;
|
||||
} elseif ($token === '{') {
|
||||
$statements[$normalizeAlias($alias)] = $class;
|
||||
prev($tokens);
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
protected function parsePromotedProperties(array &$tokens): array
|
||||
{
|
||||
$properties = [];
|
||||
|
||||
$this->skipTo($tokens, '(');
|
||||
$round = 1;
|
||||
$promoted = false;
|
||||
while (false !== ($token = $this->nextToken($tokens))) {
|
||||
if (is_string($token)) {
|
||||
switch ($token) {
|
||||
case '(':
|
||||
++$round;
|
||||
break;
|
||||
case ')':
|
||||
--$round;
|
||||
if (0 == $round) {
|
||||
return $properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_array($token)) {
|
||||
switch ($token[0]) {
|
||||
case T_PUBLIC:
|
||||
case T_PROTECTED:
|
||||
case T_PRIVATE:
|
||||
$promoted = true;
|
||||
break;
|
||||
case T_VARIABLE:
|
||||
if ($promoted) {
|
||||
$properties[] = ltrim($token[1], '$');
|
||||
$promoted = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Processors\ProcessorInterface;
|
||||
|
||||
/**
|
||||
* Result of the analyser.
|
||||
*
|
||||
* Pretends to be an array of annotations, but also contains detected classes
|
||||
* and helper functions for the processors.
|
||||
*/
|
||||
class Analysis
|
||||
{
|
||||
/**
|
||||
* @var \SplObjectStorage
|
||||
*/
|
||||
public $annotations;
|
||||
|
||||
/**
|
||||
* Class definitions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $classes = [];
|
||||
|
||||
/**
|
||||
* Interface definitions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $interfaces = [];
|
||||
|
||||
/**
|
||||
* Trait definitions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $traits = [];
|
||||
|
||||
/**
|
||||
* Enum definitions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $enums = [];
|
||||
|
||||
/**
|
||||
* The target OpenApi annotation.
|
||||
*
|
||||
* @var OA\OpenApi|null
|
||||
*/
|
||||
public $openapi = null;
|
||||
|
||||
/**
|
||||
* @var Context|null
|
||||
*/
|
||||
public $context = null;
|
||||
|
||||
public function __construct(array $annotations = [], ?Context $context = null)
|
||||
{
|
||||
$this->annotations = new \SplObjectStorage();
|
||||
$this->context = $context;
|
||||
|
||||
$this->addAnnotations($annotations, $context);
|
||||
}
|
||||
|
||||
public function addAnnotation(object $annotation, Context $context): void
|
||||
{
|
||||
if ($this->annotations->contains($annotation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($annotation instanceof OA\OpenApi) {
|
||||
$this->openapi = $this->openapi ?: $annotation;
|
||||
} else {
|
||||
if ($context->is('annotations') === false) {
|
||||
$context->annotations = [];
|
||||
}
|
||||
|
||||
if (in_array($annotation, $context->annotations, true) === false) {
|
||||
$context->annotations[] = $annotation;
|
||||
}
|
||||
}
|
||||
$this->annotations->attach($annotation, $context);
|
||||
$blacklist = property_exists($annotation, '_blacklist') ? $annotation::$_blacklist : [];
|
||||
foreach ($annotation as $property => $value) {
|
||||
if (in_array($property, $blacklist)) {
|
||||
if ($property === '_unmerged') {
|
||||
foreach ($value as $item) {
|
||||
$this->addAnnotation($item, $context);
|
||||
}
|
||||
}
|
||||
} elseif (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
if ($item instanceof OA\AbstractAnnotation) {
|
||||
$this->addAnnotation($item, $context);
|
||||
}
|
||||
}
|
||||
} elseif ($value instanceof OA\AbstractAnnotation) {
|
||||
$this->addAnnotation($value, $context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addAnnotations(array $annotations, Context $context): void
|
||||
{
|
||||
foreach ($annotations as $annotation) {
|
||||
$this->addAnnotation($annotation, $context);
|
||||
}
|
||||
}
|
||||
|
||||
public function addClassDefinition(array $definition): void
|
||||
{
|
||||
$class = $definition['context']->fullyQualifiedName($definition['class']);
|
||||
$this->classes[$class] = $definition;
|
||||
}
|
||||
|
||||
public function addInterfaceDefinition(array $definition): void
|
||||
{
|
||||
$interface = $definition['context']->fullyQualifiedName($definition['interface']);
|
||||
$this->interfaces[$interface] = $definition;
|
||||
}
|
||||
|
||||
public function addTraitDefinition(array $definition): void
|
||||
{
|
||||
$trait = $definition['context']->fullyQualifiedName($definition['trait']);
|
||||
$this->traits[$trait] = $definition;
|
||||
}
|
||||
|
||||
public function addEnumDefinition(array $definition): void
|
||||
{
|
||||
$enum = $definition['context']->fullyQualifiedName($definition['enum']);
|
||||
$this->enums[$enum] = $definition;
|
||||
}
|
||||
|
||||
public function addAnalysis(Analysis $analysis): void
|
||||
{
|
||||
foreach ($analysis->annotations as $annotation) {
|
||||
$this->addAnnotation($annotation, $analysis->annotations[$annotation]);
|
||||
}
|
||||
$this->classes = array_merge($this->classes, $analysis->classes);
|
||||
$this->interfaces = array_merge($this->interfaces, $analysis->interfaces);
|
||||
$this->traits = array_merge($this->traits, $analysis->traits);
|
||||
$this->enums = array_merge($this->enums, $analysis->enums);
|
||||
if ($this->openapi === null && $analysis->openapi !== null) {
|
||||
$this->openapi = $analysis->openapi;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all subclasses of the given parent class.
|
||||
*
|
||||
* @param string $parent the parent class
|
||||
*
|
||||
* @return array map of class => definition pairs of sub-classes
|
||||
*/
|
||||
public function getSubClasses(string $parent): array
|
||||
{
|
||||
$definitions = [];
|
||||
foreach ($this->classes as $class => $classDefinition) {
|
||||
if ($classDefinition['extends'] === $parent) {
|
||||
$definitions[$class] = $classDefinition;
|
||||
$definitions = array_merge($definitions, $this->getSubClasses($class));
|
||||
}
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all super classes for the given class.
|
||||
*
|
||||
* @param string $class the class name
|
||||
* @param bool $direct flag to find only the actual class parents
|
||||
*
|
||||
* @return array map of class => definition pairs of parent classes
|
||||
*/
|
||||
public function getSuperClasses(string $class, bool $direct = false): array
|
||||
{
|
||||
$classDefinition = $this->classes[$class] ?? null;
|
||||
if (!$classDefinition || empty($classDefinition['extends'])) {
|
||||
// unknown class, or no inheritance
|
||||
return [];
|
||||
}
|
||||
|
||||
$extends = $classDefinition['extends'];
|
||||
$extendsDefinition = $this->classes[$extends] ?? null;
|
||||
if (!$extendsDefinition) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$parentDetails = [$extends => $extendsDefinition];
|
||||
|
||||
if ($direct) {
|
||||
return $parentDetails;
|
||||
}
|
||||
|
||||
return array_merge($parentDetails, $this->getSuperClasses($extends));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of interfaces used by the given class or by classes which it extends.
|
||||
*
|
||||
* @param string $class the class name
|
||||
* @param bool $direct flag to find only the actual class interfaces
|
||||
*
|
||||
* @return array map of class => definition pairs of interfaces
|
||||
*/
|
||||
public function getInterfacesOfClass(string $class, bool $direct = false): array
|
||||
{
|
||||
$classes = $direct ? [] : array_keys($this->getSuperClasses($class));
|
||||
// add self
|
||||
$classes[] = $class;
|
||||
|
||||
$definitions = [];
|
||||
foreach ($classes as $clazz) {
|
||||
if (isset($this->classes[$clazz])) {
|
||||
$definition = $this->classes[$clazz];
|
||||
if (isset($definition['implements'])) {
|
||||
foreach ($definition['implements'] as $interface) {
|
||||
if (array_key_exists($interface, $this->interfaces)) {
|
||||
$definitions[$interface] = $this->interfaces[$interface];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$direct) {
|
||||
// expand recursively for interfaces extending other interfaces
|
||||
$collect = function ($interfaces, $cb) use (&$definitions): void {
|
||||
foreach ($interfaces as $interface) {
|
||||
if (isset($this->interfaces[$interface]['extends'])) {
|
||||
$cb($this->interfaces[$interface]['extends'], $cb);
|
||||
foreach ($this->interfaces[$interface]['extends'] as $fqdn) {
|
||||
$definitions[$fqdn] = $this->interfaces[$fqdn];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$collect(array_keys($definitions), $collect);
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of traits used by the given class/trait or by classes which it extends.
|
||||
*
|
||||
* @param string $source the source name
|
||||
* @param bool $direct flag to find only the actual class traits
|
||||
*
|
||||
* @return array map of class => definition pairs of traits
|
||||
*/
|
||||
public function getTraitsOfClass(string $source, bool $direct = false): array
|
||||
{
|
||||
$sources = $direct ? [] : array_keys($this->getSuperClasses($source));
|
||||
// add self
|
||||
$sources[] = $source;
|
||||
|
||||
$definitions = [];
|
||||
foreach ($sources as $sourze) {
|
||||
if (isset($this->classes[$sourze]) || isset($this->traits[$sourze])) {
|
||||
$definition = $this->classes[$sourze] ?? $this->traits[$sourze];
|
||||
if (isset($definition['traits'])) {
|
||||
foreach ($definition['traits'] as $trait) {
|
||||
if (array_key_exists($trait, $this->traits)) {
|
||||
$definitions[$trait] = $this->traits[$trait];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$direct) {
|
||||
// expand recursively for traits using other traits
|
||||
$collect = function ($traits, $cb) use (&$definitions): void {
|
||||
foreach ($traits as $trait) {
|
||||
if (isset($this->traits[$trait]['traits'])) {
|
||||
$cb($this->traits[$trait]['traits'], $cb);
|
||||
foreach ($this->traits[$trait]['traits'] as $fqdn) {
|
||||
$definitions[$fqdn] = $this->traits[$fqdn];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$collect(array_keys($definitions), $collect);
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string|array<class-string> $classes one or more class names
|
||||
* @param bool $strict in non-strict mode child classes are also detected
|
||||
*
|
||||
* @return OA\AbstractAnnotation[]
|
||||
*/
|
||||
public function getAnnotationsOfType($classes, bool $strict = false): array
|
||||
{
|
||||
$unique = new \SplObjectStorage();
|
||||
$annotations = [];
|
||||
|
||||
foreach ((array) $classes as $class) {
|
||||
/** @var OA\AbstractAnnotation $annotation */
|
||||
foreach ($this->annotations as $annotation) {
|
||||
if ($annotation instanceof $class && (!$strict || ($annotation->isRoot($class) && !$unique->contains($annotation)))) {
|
||||
$unique->attach($annotation);
|
||||
$annotations[] = $annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fqdn the source class/interface/trait
|
||||
*/
|
||||
public function getSchemaForSource(string $fqdn): ?OA\Schema
|
||||
{
|
||||
$fqdn = '\\' . ltrim($fqdn, '\\');
|
||||
|
||||
foreach ([$this->classes, $this->interfaces, $this->traits, $this->enums] as $definitions) {
|
||||
if (array_key_exists($fqdn, $definitions)) {
|
||||
$definition = $definitions[$fqdn];
|
||||
if (is_iterable($definition['context']->annotations)) {
|
||||
foreach (array_reverse($definition['context']->annotations) as $annotation) {
|
||||
if ($annotation instanceof OA\Schema && $annotation->isRoot(OA\Schema::class) && !$annotation->_context->is('generated')) {
|
||||
return $annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getContext(object $annotation): ?Context
|
||||
{
|
||||
if ($annotation instanceof OA\AbstractAnnotation) {
|
||||
return $annotation->_context;
|
||||
}
|
||||
if ($this->annotations->contains($annotation) === false) {
|
||||
throw new \Exception('Annotation not found');
|
||||
}
|
||||
$context = $this->annotations[$annotation];
|
||||
if ($context instanceof Context) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
// Weird, did you use the addAnnotation/addAnnotations methods?
|
||||
throw new \Exception('Annotation has no context');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an analysis with only the annotations that are merged into the OpenAPI annotation.
|
||||
*/
|
||||
public function merged(): Analysis
|
||||
{
|
||||
if ($this->openapi === null) {
|
||||
throw new \Exception('No openapi target set. Run the MergeIntoOpenApi processor');
|
||||
}
|
||||
$unmerged = $this->openapi->_unmerged;
|
||||
$this->openapi->_unmerged = [];
|
||||
$analysis = new Analysis([$this->openapi], $this->context);
|
||||
$this->openapi->_unmerged = $unmerged;
|
||||
|
||||
return $analysis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis with only the annotations that not merged.
|
||||
*/
|
||||
public function unmerged(): Analysis
|
||||
{
|
||||
return $this->split()->unmerged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the annotation into two analysis.
|
||||
* One with annotations that are merged and one with annotations that are not merged.
|
||||
*
|
||||
* @return \stdClass {merged: Analysis, unmerged: Analysis}
|
||||
*/
|
||||
public function split()
|
||||
{
|
||||
$result = new \stdClass();
|
||||
$result->merged = $this->merged();
|
||||
$result->unmerged = new Analysis([], $this->context);
|
||||
foreach ($this->annotations as $annotation) {
|
||||
if ($result->merged->annotations->contains($annotation) === false) {
|
||||
$result->unmerged->annotations->attach($annotation, $this->annotations[$annotation]);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the processor(s).
|
||||
*
|
||||
* @param callable|ProcessorInterface|array<ProcessorInterface|callable> $processors One or more processors
|
||||
*/
|
||||
public function process($processors = null): void
|
||||
{
|
||||
if (is_array($processors) === false && is_callable($processors) || $processors instanceof ProcessorInterface) {
|
||||
$processors = [$processors];
|
||||
}
|
||||
|
||||
foreach ($processors as $processor) {
|
||||
$processor($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function validate(): bool
|
||||
{
|
||||
if ($this->openapi !== null) {
|
||||
return $this->openapi->validate();
|
||||
}
|
||||
|
||||
$this->context->logger->warning('No openapi target set. Run the MergeIntoOpenApi processor before validate()');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,795 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Context;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Util;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* The openapi annotation base class.
|
||||
*/
|
||||
abstract class AbstractAnnotation implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* While the OpenAPI Specification tries to accommodate most use cases, additional data can be added to extend the specification at certain points.
|
||||
* For further details see https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specificationExtensions
|
||||
* The keys inside the array will be prefixed with `x-`.
|
||||
*
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
public $x = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Arbitrary attachables for this annotation.
|
||||
* These will be ignored but can be used for custom processing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $attachables = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @var Context|null
|
||||
*/
|
||||
public $_context = null;
|
||||
|
||||
/**
|
||||
* Annotations that couldn't be merged by mapping or postprocessing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $_unmerged = [];
|
||||
|
||||
/**
|
||||
* The properties which are required by [the spec](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $_required = [];
|
||||
|
||||
/**
|
||||
* Specify the type of the property.
|
||||
*
|
||||
* Examples:
|
||||
* 'name' => 'string' // a string
|
||||
* 'required' => 'boolean', // true or false
|
||||
* 'tags' => '[string]', // array containing strings
|
||||
* 'in' => ["query", "header", "path", "formData", "body"] // must be one on these
|
||||
* 'oneOf' => [Schema::class] // array of schema objects.
|
||||
*
|
||||
* @var array<string,string|array<string>>
|
||||
*/
|
||||
public static $_types = [];
|
||||
|
||||
/**
|
||||
* Declarative mapping of Annotation types to properties.
|
||||
* Examples:
|
||||
* Info::clas => 'info', // Set @OA\Info annotation as the info property.
|
||||
* Parameter::clas => ['parameters'], // Append @OA\Parameter annotations the parameters array.
|
||||
* PathItem::clas => ['paths', 'path'], // Append @OA\PathItem annotations the paths array and use path as key.
|
||||
*
|
||||
* @var array<class-string<AbstractAnnotation>,string|array<string>>
|
||||
*/
|
||||
public static $_nested = [];
|
||||
|
||||
/**
|
||||
* Reverse mapping of $_nested with the allowed parent annotations.
|
||||
*
|
||||
* @var array<class-string<AbstractAnnotation>>
|
||||
*/
|
||||
public static $_parents = [];
|
||||
|
||||
/**
|
||||
* List of properties are blacklisted from the JSON output.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
public static $_blacklist = ['_context', '_unmerged', '_analysis', 'attachables'];
|
||||
|
||||
public function __construct(array $properties)
|
||||
{
|
||||
if (isset($properties['_context'])) {
|
||||
$this->_context = $properties['_context'];
|
||||
unset($properties['_context']);
|
||||
} elseif (Generator::$context) {
|
||||
$this->_context = Generator::$context;
|
||||
} else {
|
||||
$this->_context = Context::detect(1);
|
||||
}
|
||||
|
||||
if ($this->_context->is('annotations') === false) {
|
||||
$this->_context->annotations = [];
|
||||
}
|
||||
|
||||
$this->_context->annotations[] = $this;
|
||||
$nestedContext = new Context(['nested' => $this], $this->_context);
|
||||
foreach ($properties as $property => $value) {
|
||||
if (property_exists($this, $property)) {
|
||||
$this->{$property} = $value;
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $key => $annotation) {
|
||||
if ($annotation instanceof AbstractAnnotation) {
|
||||
$this->{$property}[$key] = $this->nested($annotation, $nestedContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($property !== 'value') {
|
||||
$this->{$property} = $value;
|
||||
} elseif (is_array($value)) {
|
||||
$annotations = [];
|
||||
foreach ($value as $annotation) {
|
||||
if ($annotation instanceof AbstractAnnotation) {
|
||||
$annotations[] = $annotation;
|
||||
} else {
|
||||
$this->_context->logger->warning('Unexpected field in ' . $this->identity() . ' in ' . $this->_context);
|
||||
}
|
||||
}
|
||||
$this->merge($annotations);
|
||||
} elseif (is_object($value)) {
|
||||
$this->merge([$value]);
|
||||
} else {
|
||||
if (!Generator::isDefault($value)) {
|
||||
$this->_context->logger->warning('Unexpected parameter "' . $property . '" in ' . $this->identity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this instanceof OpenApi) {
|
||||
if ($this->_context->root()->version) {
|
||||
// override via `Generator::setVersion()`
|
||||
$this->openapi = $this->_context->root()->version;
|
||||
} else {
|
||||
$this->_context->root()->version = $this->openapi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __get(string $property)
|
||||
{
|
||||
$properties = get_object_vars($this);
|
||||
$this->_context->logger->warning('Property "' . $property . '" doesn\'t exist in a ' . $this->identity() . ', existing properties: "' . implode('", "', array_keys($properties)) . '" in ' . $this->_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set(string $property, $value): void
|
||||
{
|
||||
$fields = get_object_vars($this);
|
||||
foreach (static::$_blacklist as $_property) {
|
||||
unset($fields[$_property]);
|
||||
}
|
||||
$this->_context->logger->warning('Ignoring unexpected property "' . $property . '" for ' . $this->identity() . ', expecting "' . implode('", "', array_keys($fields)) . '" in ' . $this->_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge given annotations to their mapped properties configured in static::$_nested.
|
||||
*
|
||||
* Annotations that couldn't be merged are added to the _unmerged array.
|
||||
*
|
||||
* @param AbstractAnnotation[] $annotations
|
||||
* @param bool $ignore Ignore unmerged annotations
|
||||
*
|
||||
* @return AbstractAnnotation[] The unmerged annotations
|
||||
*/
|
||||
public function merge(array $annotations, bool $ignore = false): array
|
||||
{
|
||||
$unmerged = [];
|
||||
$nestedContext = new Context(['nested' => $this], $this->_context);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
$mapped = false;
|
||||
if ($details = $this->matchNested($annotation)) {
|
||||
$property = $details->value;
|
||||
if (is_array($property)) {
|
||||
$property = $property[0];
|
||||
if (Generator::isDefault($this->{$property})) {
|
||||
$this->{$property} = [];
|
||||
}
|
||||
$this->{$property}[] = $this->nested($annotation, $nestedContext);
|
||||
$mapped = true;
|
||||
} elseif (Generator::isDefault($this->{$property})) {
|
||||
// ignore duplicate nested if only one expected
|
||||
$this->{$property} = $this->nested($annotation, $nestedContext);
|
||||
$mapped = true;
|
||||
}
|
||||
}
|
||||
if (!$mapped) {
|
||||
$unmerged[] = $annotation;
|
||||
}
|
||||
}
|
||||
if (!$ignore) {
|
||||
foreach ($unmerged as $annotation) {
|
||||
$this->_unmerged[] = $this->nested($annotation, $nestedContext);
|
||||
}
|
||||
}
|
||||
|
||||
return $unmerged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the properties from the given object into this annotation.
|
||||
* Prevents overwriting properties that are already configured.
|
||||
*
|
||||
* @param object $object
|
||||
*/
|
||||
public function mergeProperties($object): void
|
||||
{
|
||||
$currentValues = get_object_vars($this);
|
||||
foreach ($object as $property => $value) {
|
||||
if ($property === '_context') {
|
||||
continue;
|
||||
}
|
||||
if (Generator::isDefault($currentValues[$property])) {
|
||||
// Overwrite default values
|
||||
$this->{$property} = $value;
|
||||
continue;
|
||||
}
|
||||
if ($property === '_unmerged') {
|
||||
$this->_unmerged = array_merge($this->_unmerged, $value);
|
||||
continue;
|
||||
}
|
||||
if ($currentValues[$property] !== $value) {
|
||||
// New value is not the same?
|
||||
if (Generator::isDefault($value)) {
|
||||
continue;
|
||||
}
|
||||
$identity = method_exists($object, 'identity') ? $object->identity() : get_class($object);
|
||||
$context1 = $this->_context;
|
||||
$context2 = property_exists($object, '_context') ? $object->_context : 'unknown';
|
||||
if (is_object($this->{$property}) && $this->{$property} instanceof AbstractAnnotation) {
|
||||
$context1 = $this->{$property}->_context;
|
||||
}
|
||||
$this->_context->logger->error('Multiple definitions for ' . $identity . '->' . $property . "\n Using: " . $context1 . "\n Skipping: " . $context2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the documentation in YAML format.
|
||||
*/
|
||||
public function toYaml(?int $flags = null): string
|
||||
{
|
||||
if ($flags === null) {
|
||||
$flags = Yaml::DUMP_OBJECT_AS_MAP ^ Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE;
|
||||
}
|
||||
|
||||
return Yaml::dump(json_decode($this->toJson(JSON_INVALID_UTF8_IGNORE)), 10, 2, $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the documentation in JSON format.
|
||||
*/
|
||||
public function toJson(?int $flags = null): string
|
||||
{
|
||||
if ($flags === null) {
|
||||
$flags = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_IGNORE;
|
||||
}
|
||||
|
||||
return json_encode($this, $flags);
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
$properties = [];
|
||||
foreach (get_object_vars($this) as $property => $value) {
|
||||
if (!Generator::isDefault($value)) {
|
||||
$properties[$property] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$data = new \stdClass();
|
||||
|
||||
// Strip undefined values.
|
||||
foreach (get_object_vars($this) as $property => $value) {
|
||||
if (!Generator::isDefault($value)) {
|
||||
$data->{$property} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Strip properties that are for internal (swagger-php) use.
|
||||
foreach (static::$_blacklist as $property) {
|
||||
unset($data->{$property});
|
||||
}
|
||||
|
||||
// Correct empty array to empty objects.
|
||||
foreach (static::$_types as $property => $type) {
|
||||
if ($type === 'object' && is_array($data->{$property}) && empty($data->{$property})) {
|
||||
$data->{$property} = new \stdClass();
|
||||
}
|
||||
}
|
||||
|
||||
// Inject vendor properties.
|
||||
unset($data->x);
|
||||
if (is_array($this->x)) {
|
||||
foreach ($this->x as $property => $value) {
|
||||
$prefixed = 'x-' . $property;
|
||||
$data->{$prefixed} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Map nested keys
|
||||
foreach (static::$_nested as $nested) {
|
||||
if (is_string($nested) || count($nested) === 1) {
|
||||
continue;
|
||||
}
|
||||
$property = $nested[0];
|
||||
if (Generator::isDefault($this->{$property})) {
|
||||
continue;
|
||||
}
|
||||
$keyField = $nested[1];
|
||||
$object = new \stdClass();
|
||||
foreach ($this->{$property} as $key => $item) {
|
||||
if (is_numeric($key) === false && is_array($item)) {
|
||||
$object->{$key} = $item;
|
||||
} else {
|
||||
$key = $item->{$keyField};
|
||||
if (!Generator::isDefault($key) && empty($object->{$key})) {
|
||||
if ($item instanceof \JsonSerializable) {
|
||||
$object->{$key} = $item->jsonSerialize();
|
||||
} else {
|
||||
$object->{$key} = $item;
|
||||
}
|
||||
unset($object->{$key}->{$keyField});
|
||||
}
|
||||
}
|
||||
}
|
||||
$data->{$property} = $object;
|
||||
}
|
||||
|
||||
// $ref
|
||||
if (isset($data->ref)) {
|
||||
// Only specific https://github.com/OAI/OpenAPI-Specification/blob/3.1.0/versions/3.1.0.md#reference-object
|
||||
$ref = ['$ref' => $data->ref];
|
||||
if ($this->_context->version === OpenApi::VERSION_3_1_0) {
|
||||
foreach (['summary', 'description'] as $prop) {
|
||||
if (property_exists($this, $prop)) {
|
||||
if (!Generator::isDefault($this->{$prop})) {
|
||||
$ref[$prop] = $data->{$prop};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (property_exists($this, 'nullable') && $this->nullable === true) {
|
||||
$ref = ['oneOf' => [$ref]];
|
||||
if ($this->_context->version == OpenApi::VERSION_3_1_0) {
|
||||
$ref['oneOf'][] = ['type' => 'null'];
|
||||
} else {
|
||||
$ref['nullable'] = $data->nullable;
|
||||
}
|
||||
unset($data->nullable);
|
||||
|
||||
// preserve other properties
|
||||
foreach (get_object_vars($this) as $property => $value) {
|
||||
if ('_' == $property[0] || in_array($property, ['ref', 'nullable'])) {
|
||||
continue;
|
||||
}
|
||||
if (!Generator::isDefault($value)) {
|
||||
$ref[$property] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$data = (object) $ref;
|
||||
}
|
||||
|
||||
if ($this->_context->version === OpenApi::VERSION_3_1_0) {
|
||||
if (isset($data->nullable)) {
|
||||
if (true === $data->nullable) {
|
||||
if (isset($data->oneOf)) {
|
||||
$data->oneOf[] = ['type' => 'null'];
|
||||
} elseif (isset($data->anyOf)) {
|
||||
$data->anyOf[] = ['type' => 'null'];
|
||||
} elseif (isset($data->allOf)) {
|
||||
$data->allOf[] = ['type' => 'null'];
|
||||
} else {
|
||||
$data->type = (array) $data->type;
|
||||
$data->type[] = 'null';
|
||||
}
|
||||
}
|
||||
unset($data->nullable);
|
||||
}
|
||||
|
||||
if (isset($data->minimum) && isset($data->exclusiveMinimum)) {
|
||||
if (true === $data->exclusiveMinimum) {
|
||||
$data->exclusiveMinimum = $data->minimum;
|
||||
unset($data->minimum);
|
||||
} elseif (false === $data->exclusiveMinimum) {
|
||||
unset($data->exclusiveMinimum);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data->maximum) && isset($data->exclusiveMaximum)) {
|
||||
if (true === $data->exclusiveMaximum) {
|
||||
$data->exclusiveMaximum = $data->maximum;
|
||||
unset($data->maximum);
|
||||
} elseif (false === $data->exclusiveMaximum) {
|
||||
unset($data->exclusiveMaximum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate annotation tree, and log notices & warnings.
|
||||
*
|
||||
* @param array $stack the path of annotations above this annotation in the tree
|
||||
* @param array $skip (prevent stack overflow, when traversing an infinite dependency graph)
|
||||
* @param string $ref Current ref path?
|
||||
* @param object $context a free-form context contains
|
||||
*/
|
||||
public function validate(array $stack = [], array $skip = [], string $ref = '', $context = null): bool
|
||||
{
|
||||
if (in_array($this, $skip, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$valid = true;
|
||||
|
||||
// Report orphaned annotations
|
||||
foreach ($this->_unmerged as $annotation) {
|
||||
if (!is_object($annotation)) {
|
||||
$this->_context->logger->warning('Unexpected type: "' . gettype($annotation) . '" in ' . $this->identity() . '->_unmerged, expecting a Annotation object');
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var class-string<AbstractAnnotation> $class */
|
||||
$class = get_class($annotation);
|
||||
if ($details = $this->matchNested($annotation)) {
|
||||
$property = $details->value;
|
||||
if (is_array($property)) {
|
||||
$this->_context->logger->warning('Only one ' . Util::shorten(get_class($annotation)) . '() allowed for ' . $this->identity() . ' multiple found, skipped: ' . $annotation->_context);
|
||||
} else {
|
||||
$this->_context->logger->warning('Only one ' . Util::shorten(get_class($annotation)) . '() allowed for ' . $this->identity() . " multiple found in:\n Using: " . $this->{$property}->_context . "\n Skipped: " . $annotation->_context);
|
||||
}
|
||||
} elseif ($annotation instanceof AbstractAnnotation) {
|
||||
$message = 'Unexpected ' . $annotation->identity();
|
||||
if ($class::$_parents) {
|
||||
$message .= ', expected to be inside ' . implode(', ', Util::shorten($class::$_parents));
|
||||
}
|
||||
$this->_context->logger->warning($message . ' in ' . $annotation->_context);
|
||||
}
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
// Report conflicting key
|
||||
foreach (static::$_nested as $annotationClass => $nested) {
|
||||
if (is_string($nested) || count($nested) === 1) {
|
||||
continue;
|
||||
}
|
||||
$property = $nested[0];
|
||||
if (Generator::isDefault($this->{$property})) {
|
||||
continue;
|
||||
}
|
||||
$keys = [];
|
||||
$keyField = $nested[1];
|
||||
foreach ($this->{$property} as $key => $item) {
|
||||
if (is_array($item) && is_numeric($key) === false) {
|
||||
$this->_context->logger->warning($this->identity() . '->' . $property . ' is an object literal, use nested ' . Util::shorten($annotationClass) . '() annotation(s) in ' . $this->_context);
|
||||
$keys[$key] = $item;
|
||||
} elseif (Generator::isDefault($item->{$keyField})) {
|
||||
$this->_context->logger->error($item->identity() . ' is missing key-field: "' . $keyField . '" in ' . $item->_context);
|
||||
} elseif (isset($keys[$item->{$keyField}])) {
|
||||
$this->_context->logger->error('Multiple ' . $item->_identity([]) . ' with the same ' . $keyField . '="' . $item->{$keyField} . "\":\n " . $item->_context . "\n " . $keys[$item->{$keyField}]->_context);
|
||||
} else {
|
||||
$keys[$item->{$keyField}] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (property_exists($this, 'ref') && !Generator::isDefault($this->ref) && is_string($this->ref)) {
|
||||
if (substr($this->ref, 0, 2) === '#/' && count($stack) > 0 && $stack[0] instanceof OpenApi) {
|
||||
// Internal reference
|
||||
try {
|
||||
$stack[0]->ref($this->ref);
|
||||
} catch (\Exception $e) {
|
||||
$this->_context->logger->warning($e->getMessage() . ' for ' . $this->identity() . ' in ' . $this->_context, ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Report missing required fields (when not a $ref)
|
||||
foreach (static::$_required as $property) {
|
||||
if (Generator::isDefault($this->{$property})) {
|
||||
$message = 'Missing required field "' . $property . '" for ' . $this->identity() . ' in ' . $this->_context;
|
||||
foreach (static::$_nested as $class => $nested) {
|
||||
$nestedProperty = is_array($nested) ? $nested[0] : $nested;
|
||||
if ($property === $nestedProperty) {
|
||||
if ($this instanceof OpenApi) {
|
||||
$message = 'Required ' . Util::shorten($class) . '() not found';
|
||||
} elseif (is_array($nested)) {
|
||||
$message = $this->identity() . ' requires at least one ' . Util::shorten($class) . '() in ' . $this->_context;
|
||||
} else {
|
||||
$message = $this->identity() . ' requires a ' . Util::shorten($class) . '() in ' . $this->_context;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->_context->logger->warning($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Report invalid types
|
||||
foreach (static::$_types as $property => $type) {
|
||||
$value = $this->{$property};
|
||||
if (Generator::isDefault($value) || $value === null) {
|
||||
continue;
|
||||
}
|
||||
if (is_string($type)) {
|
||||
if ($this->validateType($type, $value) === false) {
|
||||
$valid = false;
|
||||
$this->_context->logger->warning($this->identity() . '->' . $property . ' is a "' . gettype($value) . '", expecting a "' . $type . '" in ' . $this->_context);
|
||||
}
|
||||
} elseif (is_array($type)) { // enum?
|
||||
if (in_array($value, $type) === false) {
|
||||
$this->_context->logger->warning($this->identity() . '->' . $property . ' "' . $value . '" is invalid, expecting "' . implode('", "', $type) . '" in ' . $this->_context);
|
||||
}
|
||||
} else {
|
||||
throw new \Exception('Invalid ' . get_class($this) . '::$_types[' . $property . ']');
|
||||
}
|
||||
}
|
||||
$stack[] = $this;
|
||||
|
||||
return self::_validate($this, $stack, $skip, $ref, $context) ? $valid : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively validate all annotation properties.
|
||||
*
|
||||
* @param array|object $fields
|
||||
*/
|
||||
private static function _validate($fields, array $stack, array $skip, string $baseRef, ?object $context): bool
|
||||
{
|
||||
$valid = true;
|
||||
$blacklist = [];
|
||||
if (is_object($fields)) {
|
||||
if (in_array($fields, $skip, true)) {
|
||||
return true;
|
||||
}
|
||||
$skip[] = $fields;
|
||||
$blacklist = property_exists($fields, '_blacklist') ? $fields::$_blacklist : [];
|
||||
}
|
||||
|
||||
foreach ($fields as $field => $value) {
|
||||
if ($value === null || is_scalar($value) || in_array($field, $blacklist)) {
|
||||
continue;
|
||||
}
|
||||
$ref = $baseRef !== '' ? $baseRef . '/' . urlencode((string) $field) : urlencode((string) $field);
|
||||
if (is_object($value)) {
|
||||
if (method_exists($value, 'validate')) {
|
||||
if (!$value->validate($stack, $skip, $ref, $context)) {
|
||||
$valid = false;
|
||||
}
|
||||
} elseif (!self::_validate($value, $stack, $skip, $ref, $context)) {
|
||||
$valid = false;
|
||||
}
|
||||
} elseif (is_array($value) && !self::_validate($value, $stack, $skip, $ref, $context)) {
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a identity for easy debugging.
|
||||
* Example: "@OA\Get(path="/pets")".
|
||||
*/
|
||||
public function identity(): string
|
||||
{
|
||||
$class = get_class($this);
|
||||
$properties = [];
|
||||
/** @var class-string<AbstractAnnotation> $parent */
|
||||
foreach (static::$_parents as $parent) {
|
||||
foreach ($parent::$_nested as $annotationClass => $entry) {
|
||||
if ($annotationClass === $class && is_array($entry) && !Generator::isDefault($this->{$entry[1]})) {
|
||||
$properties[] = $entry[1];
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_identity($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if `$other` can be nested and if so return details about where/how.
|
||||
*
|
||||
* @param AbstractAnnotation $other the other annotation
|
||||
*
|
||||
* @return null|object key/value object or `null`
|
||||
*/
|
||||
public function matchNested($other)
|
||||
{
|
||||
if ($other instanceof AbstractAnnotation && array_key_exists($root = $other->getRoot(), static::$_nested)) {
|
||||
return (object) ['key' => $root, 'value' => static::$_nested[$root]];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root annotation.
|
||||
*
|
||||
* This is used for resolving type equality and nesting rules to allow those rules to also work for custom,
|
||||
* derived annotation classes.
|
||||
*
|
||||
* @return class-string the root annotation class in the `OpenApi\\Annotations` namespace
|
||||
*/
|
||||
public function getRoot(): string
|
||||
{
|
||||
$class = get_class($this);
|
||||
|
||||
do {
|
||||
if (0 === strpos($class, 'OpenApi\\Annotations\\')) {
|
||||
break;
|
||||
}
|
||||
} while ($class = get_parent_class($class));
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the annotation root.
|
||||
*
|
||||
* @param class-string $rootClass the root class to match
|
||||
*/
|
||||
public function isRoot(string $rootClass): bool
|
||||
{
|
||||
return $this->getRoot() == $rootClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for generating the identity().
|
||||
*/
|
||||
protected function _identity(array $properties): string
|
||||
{
|
||||
$fields = [];
|
||||
foreach ($properties as $property) {
|
||||
$value = $this->{$property};
|
||||
if ($value !== null && !Generator::isDefault($value)) {
|
||||
$fields[] = $property . '=' . (is_string($value) ? '"' . $value . '"' : $value);
|
||||
}
|
||||
}
|
||||
|
||||
return Util::shorten(get_class($this)) . '(' . implode(',', $fields) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the matching of the property value to a annotation type.
|
||||
*
|
||||
* @param string $type The annotations property type
|
||||
* @param mixed $value The property value
|
||||
*/
|
||||
private function validateType(string $type, $value): bool
|
||||
{
|
||||
if (substr($type, 0, 1) === '[' && substr($type, -1) === ']') { // Array of a specified type?
|
||||
if ($this->validateType('array', $value) === false) {
|
||||
return false;
|
||||
}
|
||||
$itemType = substr($type, 1, -1);
|
||||
foreach ($value as $i => $item) {
|
||||
if ($this->validateType($itemType, $item) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_subclass_of($type, AbstractAnnotation::class)) {
|
||||
$type = 'object';
|
||||
}
|
||||
|
||||
return $this->validateDefaultTypes($type, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates default Open Api types.
|
||||
*
|
||||
* @param string $type The property type
|
||||
* @param mixed $value The value to validate
|
||||
*/
|
||||
private function validateDefaultTypes(string $type, $value): bool
|
||||
{
|
||||
if (str_contains($type, '|')) {
|
||||
$types = explode('|', $type);
|
||||
foreach ($types as $type) {
|
||||
if ($this->validateDefaultTypes($type, $value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'string':
|
||||
return is_string($value);
|
||||
case 'boolean':
|
||||
return is_bool($value);
|
||||
case 'integer':
|
||||
return is_int($value);
|
||||
case 'number':
|
||||
return is_numeric($value);
|
||||
case 'object':
|
||||
return is_object($value);
|
||||
case 'array':
|
||||
return $this->validateArrayType($value);
|
||||
case 'scheme':
|
||||
return in_array($value, ['http', 'https', 'ws', 'wss'], true);
|
||||
default:
|
||||
throw new \Exception('Invalid type "' . $type . '"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate array type.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function validateArrayType($value): bool
|
||||
{
|
||||
if (is_array($value) === false) {
|
||||
return false;
|
||||
}
|
||||
$count = 0;
|
||||
foreach ($value as $i => $item) {
|
||||
// not a array, but a hash/map
|
||||
if ($count !== $i) {
|
||||
return false;
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the context with a reference to the annotation it is nested in.
|
||||
*
|
||||
* @param AbstractAnnotation $annotation
|
||||
*
|
||||
* @return AbstractAnnotation
|
||||
*/
|
||||
protected function nested(AbstractAnnotation $annotation, Context $nestedContext)
|
||||
{
|
||||
if (property_exists($annotation, '_context') && $annotation->_context === $this->_context) {
|
||||
$annotation->_context = $nestedContext;
|
||||
}
|
||||
|
||||
return $annotation;
|
||||
}
|
||||
|
||||
protected function combine(...$args): array
|
||||
{
|
||||
$combined = [];
|
||||
foreach ($args as $arg) {
|
||||
if (is_array($arg)) {
|
||||
$combined = array_merge($combined, $arg);
|
||||
} else {
|
||||
$combined[] = $arg;
|
||||
}
|
||||
}
|
||||
|
||||
return array_filter($combined, function ($value) {
|
||||
return !Generator::isDefault($value) && $value !== null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class AdditionalProperties extends Schema
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Schema::class,
|
||||
Property::class,
|
||||
Items::class,
|
||||
JsonContent::class,
|
||||
XmlContent::class,
|
||||
AdditionalProperties::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Discriminator::class => 'discriminator',
|
||||
Items::class => 'items',
|
||||
Property::class => ['properties', 'property'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Xml::class => 'xml',
|
||||
AdditionalProperties::class => 'additionalProperties',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* A container for custom data to be attached to an annotation.
|
||||
*
|
||||
* These will be ignored by `swagger-php` but can be used for custom processing.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Attachable extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
AdditionalProperties::class,
|
||||
Components::class,
|
||||
Contact::class,
|
||||
Delete::class,
|
||||
Discriminator::class,
|
||||
Examples::class,
|
||||
ExternalDocumentation::class,
|
||||
Flow::class,
|
||||
Get::class,
|
||||
Head::class,
|
||||
Header::class,
|
||||
Info::class,
|
||||
Items::class,
|
||||
JsonContent::class,
|
||||
License::class,
|
||||
Link::class,
|
||||
MediaType::class,
|
||||
OpenApi::class,
|
||||
Operation::class,
|
||||
Options::class,
|
||||
Parameter::class,
|
||||
Patch::class,
|
||||
PathItem::class,
|
||||
PathParameter::class,
|
||||
Post::class,
|
||||
Property::class,
|
||||
Put::class,
|
||||
RequestBody::class,
|
||||
Response::class,
|
||||
Schema::class,
|
||||
SecurityScheme::class,
|
||||
Server::class,
|
||||
ServerVariable::class,
|
||||
Tag::class,
|
||||
Trace::class,
|
||||
Webhook::class,
|
||||
Xml::class,
|
||||
XmlContent::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Allows to type-hint a specific parent annotation class.
|
||||
*
|
||||
* Container to allow custom annotations that are limited to a subset of potential parent
|
||||
* annotation classes.
|
||||
*
|
||||
* @return array<class-string>|null List of valid parent annotation classes. If `null`, the default nesting rules apply.
|
||||
*/
|
||||
public function allowedParents(): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Util;
|
||||
|
||||
/**
|
||||
* Holds a set of reusable objects for different aspects of the OA.
|
||||
*
|
||||
* All objects defined within the components object will have no effect on the API unless they are explicitly
|
||||
* referenced from properties outside the components object.
|
||||
*
|
||||
* @see [OAI Components Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#components-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Components extends AbstractAnnotation
|
||||
{
|
||||
public const COMPONENTS_PREFIX = '#/components/';
|
||||
|
||||
/**
|
||||
* Schema reference.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const SCHEMA_REF = '#/components/schemas/';
|
||||
|
||||
/**
|
||||
* Reusable Schemas.
|
||||
*
|
||||
* @var array<Schema|\OpenApi\Attributes\Schema>
|
||||
*/
|
||||
public $schemas = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Responses.
|
||||
*
|
||||
* @var Response[]
|
||||
*/
|
||||
public $responses = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Parameters.
|
||||
*
|
||||
* @var Parameter[]
|
||||
*/
|
||||
public $parameters = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Examples.
|
||||
*
|
||||
* @var Examples[]
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Request Bodies.
|
||||
*
|
||||
* @var RequestBody[]
|
||||
*/
|
||||
public $requestBodies = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Headers.
|
||||
*
|
||||
* @var Header[]
|
||||
*/
|
||||
public $headers = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Security Schemes.
|
||||
*
|
||||
* @var SecurityScheme[]
|
||||
*/
|
||||
public $securitySchemes = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Links.
|
||||
*
|
||||
* @var Link[]
|
||||
*/
|
||||
public $links = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Reusable Callbacks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $callbacks = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
OpenApi::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Response::class => ['responses', 'response'],
|
||||
Parameter::class => ['parameters', 'parameter'],
|
||||
PathParameter::class => ['parameters', 'parameter'],
|
||||
RequestBody::class => ['requestBodies', 'request'],
|
||||
Examples::class => ['examples', 'example'],
|
||||
Header::class => ['headers', 'header'],
|
||||
SecurityScheme::class => ['securitySchemes', 'securityScheme'],
|
||||
Link::class => ['links', 'link'],
|
||||
Schema::class => ['schemas', 'schema'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Generate a `#/components/...` reference for the given annotation.
|
||||
*
|
||||
* A `string` component value always assumes type `Schema`.
|
||||
*
|
||||
* @param AbstractAnnotation|string $component
|
||||
*/
|
||||
public static function ref($component, bool $encode = true): string
|
||||
{
|
||||
if ($component instanceof AbstractAnnotation) {
|
||||
foreach (Components::$_nested as $type => $nested) {
|
||||
// exclude attachables
|
||||
if (2 == count($nested)) {
|
||||
if ($component instanceof $type) {
|
||||
$type = $nested[0];
|
||||
$name = $component->{$nested[1]};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$type = 'schemas';
|
||||
$name = $component;
|
||||
}
|
||||
|
||||
return self::COMPONENTS_PREFIX . $type . '/' . ($encode ? Util::refEncode((string) $name) : $name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Contact information for the exposed API.
|
||||
*
|
||||
* @see [OAI Contact Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#contact-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Contact extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The identifying name of the contact person/organization.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The URL pointing to the contact information.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $url = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The email address of the contact person/organization.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $email = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'name' => 'string',
|
||||
'url' => 'string',
|
||||
'email' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Info::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* A `@OA\Request` cookie parameter.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class CookieParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
* This takes 'cookie' as the default location.
|
||||
*/
|
||||
public $in = 'cookie';
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Delete extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'delete';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* The discriminator is a specific object in a schema which is used to inform the consumer of
|
||||
* the specification of an alternative schema based on the value associated with it.
|
||||
*
|
||||
* This object is based on the [JSON Schema Specification](http://json-schema.org) and uses a predefined subset of it.
|
||||
* On top of this subset, there are extensions provided by this specification to allow for more complete documentation.
|
||||
*
|
||||
* @see [OAI Discriminator Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#discriminatorObject)
|
||||
* @see [JSON Schema](http://json-schema.org/)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Discriminator extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The name of the property in the payload that will hold the discriminator value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $propertyName = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An object to hold mappings between payload values and schema names or references.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $mapping = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['propertyName'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'propertyName' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Schema::class,
|
||||
Property::class,
|
||||
AdditionalProperties::class,
|
||||
Items::class,
|
||||
JsonContent::class,
|
||||
XmlContent::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Examples extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The relative or absolute path to an example.
|
||||
*
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The key into `#/components/examples`.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $example = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Short description for the example.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $summary = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Embedded literal example.
|
||||
*
|
||||
* The value field and externalValue field are mutually exclusive.
|
||||
*
|
||||
* To represent examples of media types that cannot naturally be represented
|
||||
* in JSON or YAML, use a string value to contain the example, escaping where necessary.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Embedded literal example.
|
||||
*
|
||||
* The value field and externalValue field are mutually exclusive.
|
||||
*
|
||||
* To represent examples of media types that cannot naturally be represented
|
||||
* in JSON or YAML, use a string value to contain the example, escaping where necessary.
|
||||
*
|
||||
* @var int|string|array
|
||||
*/
|
||||
public $value = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An URL that points to the literal example.
|
||||
*
|
||||
* This provides the capability to reference examples that cannot easily be included
|
||||
* in JSON or YAML documents.
|
||||
*
|
||||
* The value field and externalValue field are mutually exclusive.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $externalValue = Generator::UNDEFINED;
|
||||
|
||||
public static $_types = [
|
||||
'summary' => 'string',
|
||||
'description' => 'string',
|
||||
'externalValue' => 'string',
|
||||
];
|
||||
|
||||
public static $_required = ['summary'];
|
||||
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
Parameter::class,
|
||||
PathParameter::class,
|
||||
MediaType::class,
|
||||
JsonContent::class,
|
||||
XmlContent::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Allows referencing an external resource for extended documentation.
|
||||
*
|
||||
* @see [OAI External Documentation Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#external-documentation-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class ExternalDocumentation extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* A short description of the target documentation. GFM syntax can be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The URL for the target documentation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $url = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'description' => 'string',
|
||||
'url' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['url'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
OpenApi::class,
|
||||
Tag::class,
|
||||
Schema::class,
|
||||
AdditionalProperties::class,
|
||||
Property::class,
|
||||
Operation::class,
|
||||
Get::class,
|
||||
Post::class,
|
||||
Put::class,
|
||||
Delete::class,
|
||||
Patch::class,
|
||||
Head::class,
|
||||
Options::class,
|
||||
Trace::class,
|
||||
Items::class,
|
||||
JsonContent::class,
|
||||
XmlContent::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Configuration details for a supported OAuth Flow.
|
||||
*
|
||||
* @see [OAI OAuth Flow Object](https://swagger.io/specification/#oauthFlowObject)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Flow extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The authorization url to be used for this flow.
|
||||
*
|
||||
* This must be in the form of an url.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $authorizationUrl = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The token URL to be used for this flow.
|
||||
*
|
||||
* This must be in the form of an url.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $tokenUrl = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The URL to be used for obtaining refresh tokens.
|
||||
*
|
||||
* This must be in the form of an url.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $refreshUrl = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Flow name.
|
||||
*
|
||||
* One of ['implicit', 'password', 'authorizationCode', 'clientCredentials'].
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $flow = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The available scopes for the OAuth2 security scheme.
|
||||
*
|
||||
* A map between the scope name and a short description for it.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $scopes = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['scopes', 'flow'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'flow' => ['implicit', 'password', 'authorizationCode', 'clientCredentials'],
|
||||
'refreshUrl' => 'string',
|
||||
'authorizationUrl' => 'string',
|
||||
'tokenUrl' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
SecurityScheme::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
if (is_array($this->scopes) && empty($this->scopes)) {
|
||||
$this->scopes = new \stdClass();
|
||||
}
|
||||
|
||||
return parent::jsonSerialize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Get extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'get';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Head extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'head';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* @see [OAI Header Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#headerObject).
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Header extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The relative or absolute path to the endpoint.
|
||||
*
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $header = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A brief description of the parameter.
|
||||
*
|
||||
* This could contain examples of use.
|
||||
* CommonMark syntax MAY be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $required = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Schema object.
|
||||
*
|
||||
* @var Schema
|
||||
*/
|
||||
public $schema = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deprecated = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Sets the ability to pass empty-valued parameters.
|
||||
*
|
||||
* This is valid only for query parameters and allows sending a parameter with an empty value.
|
||||
*
|
||||
* Default value is false.
|
||||
*
|
||||
* If style is used, and if behavior is n/a (cannot be serialized), the value of allowEmptyValue SHALL be ignored.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $allowEmptyValue = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['header', 'schema'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'header' => 'string',
|
||||
'description' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Schema::class => 'schema',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
Response::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* A `@OA\Request` header parameter.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class HeaderParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
* This takes 'header' as the default location.
|
||||
*/
|
||||
public $in = 'header';
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* The object provides metadata about the API.
|
||||
*
|
||||
* The metadata may be used by the clients if needed and may be presented in editing or documentation generation tools for convenience.
|
||||
*
|
||||
* @see [OAI Info Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#info-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Info extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The title of the application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A short description of the application.
|
||||
*
|
||||
* CommonMark syntax may be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An URL to the Terms of Service for the API.
|
||||
*
|
||||
* Must be in the format of an url.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $termsOfService = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The contact information for the exposed API.
|
||||
*
|
||||
* @var Contact
|
||||
*/
|
||||
public $contact = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The license information for the exposed API.
|
||||
*
|
||||
* @var License
|
||||
*/
|
||||
public $license = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API implementation version).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['title', 'version'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'title' => 'string',
|
||||
'version' => 'string',
|
||||
'description' => 'string',
|
||||
'termsOfService' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Contact::class => 'contact',
|
||||
License::class => 'license',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
OpenApi::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* The description of an item in a Schema with type `array`.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Items extends Schema
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Discriminator::class => 'discriminator',
|
||||
Items::class => 'items',
|
||||
Property::class => ['properties', 'property'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Xml::class => 'xml',
|
||||
AdditionalProperties::class => 'additionalProperties',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Property::class,
|
||||
AdditionalProperties::class,
|
||||
Schema::class,
|
||||
JsonContent::class,
|
||||
XmlContent::class,
|
||||
Items::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validate(array $stack = [], array $skip = [], string $ref = '', $context = null): bool
|
||||
{
|
||||
if (in_array($this, $skip, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$valid = parent::validate($stack, $skip, $ref, $context);
|
||||
|
||||
$parent = end($stack);
|
||||
if ($parent instanceof Schema && $parent->type !== 'array') {
|
||||
$this->_context->logger->warning('@OA\\Items() parent type must be "array" in ' . $this->_context);
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
// @todo Additional validation when used inside a Header or Parameter context.
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Shorthand for a json response.
|
||||
*
|
||||
* Use as `@OA\Schema` inside a `Response` and `MediaType`->`'application/json'` will be generated.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class JsonContent extends Schema
|
||||
{
|
||||
/**
|
||||
* An associative array of Examples attributes.
|
||||
*
|
||||
* The keys represent the name of the example and the values are instances of the Examples attribute.
|
||||
* Each example is used to show how the content of the request or response should look like.
|
||||
*
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Discriminator::class => 'discriminator',
|
||||
Items::class => 'items',
|
||||
Property::class => ['properties', 'property'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
AdditionalProperties::class => 'additionalProperties',
|
||||
Examples::class => ['examples', 'example'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* License information for the exposed API.
|
||||
*
|
||||
* @see [OAI License Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#license-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class License extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The license name used for the API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An SPDX license expression for the API. The `identifier` field is mutually exclusive of the `url` field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $identifier = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An URL to the license used for the API. This MUST be in the form of a URL.
|
||||
*
|
||||
* The `url` field is mutually exclusive of the `identifier` field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $url = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'name' => 'string',
|
||||
'identifier' => 'string',
|
||||
'url' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['name'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Info::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$data = parent::jsonSerialize();
|
||||
|
||||
if ($this->_context->isVersion(OpenApi::VERSION_3_0_0)) {
|
||||
unset($data->identifier);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validate(array $stack = [], array $skip = [], string $ref = '', $context = null): bool
|
||||
{
|
||||
$valid = parent::validate($stack, $skip, $ref, $context);
|
||||
|
||||
if ($this->_context->isVersion(OpenApi::VERSION_3_1_0)) {
|
||||
if (!Generator::isDefault($this->url) && $this->identifier !== Generator::UNDEFINED) {
|
||||
$this->_context->logger->warning($this->identity() . ' url and identifier are mutually exclusive');
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* The Link object represents a possible design-time link for a response.
|
||||
*
|
||||
* The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known
|
||||
* relationship and traversal mechanism between responses and other operations.
|
||||
*
|
||||
* Unlike dynamic links (i.e. links provided in the response payload), the OA linking mechanism does not require
|
||||
* link information in the runtime response.
|
||||
*
|
||||
* For computing links, and providing instructions to execute them, a runtime expression is used for
|
||||
* accessing values in an operation and using them as parameters while invoking the linked operation.
|
||||
*
|
||||
* @see [OAI Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Link extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The key into MediaType->links array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $link = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A relative or absolute reference to an OA operation.
|
||||
*
|
||||
* This field is mutually exclusive of the <code>operationId</code> field, and must point to an Operation object.
|
||||
*
|
||||
* Relative values may be used to locate an existing Operation object in the OpenAPI definition.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $operationRef = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The name of an existing, resolvable OA operation, as defined with a unique <code>operationId</code>.
|
||||
*
|
||||
* This field is mutually exclusive of the <code>operationRef</code> field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $operationId = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map representing parameters to pass to an operation as specified with operationId or identified via
|
||||
* operationRef.
|
||||
*
|
||||
* The key is the parameter name to be used, whereas the value can be a constant or an expression to
|
||||
* be evaluated and passed to the linked operation.
|
||||
* The parameter name can be qualified using the parameter location [{in}.]{name} for operations
|
||||
* that use the same parameter name in different locations (e.g. path.id).
|
||||
*
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
public $parameters = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A literal value or {expression} to use as a request body when calling the target operation.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $requestBody = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A description of the link.
|
||||
*
|
||||
* CommonMark syntax may be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A server object to be used by the target operation.
|
||||
*
|
||||
* @var Server
|
||||
*/
|
||||
public $server = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Server::class => 'server',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
Response::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Each Media Type object provides schema and examples for the media type identified by its key.
|
||||
*
|
||||
* @see [OAI Media Type Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class MediaType extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The key into Operation->content array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $mediaType = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The schema defining the type used for the request body.
|
||||
*
|
||||
* @var Schema
|
||||
*/
|
||||
public $schema = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Example of the media type.
|
||||
*
|
||||
* The example object should be in the correct format as specified by the media type.
|
||||
* The example object is mutually exclusive of the examples object.
|
||||
*
|
||||
* Furthermore, if referencing a schema which contains an example,
|
||||
* the example value shall override the example provided by the schema.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $example = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Examples of the media type.
|
||||
*
|
||||
* Each example object should match the media type and specified schema if present.
|
||||
* The examples object is mutually exclusive of the example object.
|
||||
*
|
||||
* Furthermore, if referencing a schema which contains an example,
|
||||
* the examples value shall override the example provided by the schema.
|
||||
*
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map between a property name and its encoding information.
|
||||
*
|
||||
* The key, being the property name, must exist in the schema as a property.
|
||||
*
|
||||
* The encoding object shall only apply to requestBody objects when the media type is multipart or
|
||||
* application/x-www-form-urlencoded.
|
||||
*
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
public $encoding = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Schema::class => 'schema',
|
||||
Examples::class => ['examples', 'example'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Response::class,
|
||||
RequestBody::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Util;
|
||||
|
||||
/**
|
||||
* This is the root document object for the API specification.
|
||||
*
|
||||
* @see [OAI OpenApi Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#openapi-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class OpenApi extends AbstractAnnotation
|
||||
{
|
||||
public const VERSION_3_0_0 = '3.0.0';
|
||||
public const VERSION_3_1_0 = '3.1.0';
|
||||
public const DEFAULT_VERSION = self::VERSION_3_0_0;
|
||||
public const SUPPORTED_VERSIONS = [self::VERSION_3_0_0, self::VERSION_3_1_0];
|
||||
|
||||
/**
|
||||
* The semantic version number of the OpenAPI Specification version that the OpenAPI document uses.
|
||||
*
|
||||
* The openapi field should be used by tooling specifications and clients to interpret the OpenAPI document.
|
||||
*
|
||||
* A version specified via `Generator::setVersion()` will overwrite this value.
|
||||
*
|
||||
* This is not related to the API info::version string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $openapi = self::DEFAULT_VERSION;
|
||||
|
||||
/**
|
||||
* Provides metadata about the API. The metadata may be used by tooling as required.
|
||||
*
|
||||
* @var Info
|
||||
*/
|
||||
public $info = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An array of <code>@Server</code> objects, which provide connectivity information to a target server.
|
||||
*
|
||||
* If not provided, or is an empty array, the default value would be a Server Object with an url value of <code>/</code>.
|
||||
*
|
||||
* @var Server[]
|
||||
*/
|
||||
public $servers = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The available paths and operations for the API.
|
||||
*
|
||||
* @var PathItem[]
|
||||
*/
|
||||
public $paths = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An element to hold various components for the specification.
|
||||
*
|
||||
* @var Components
|
||||
*/
|
||||
public $components = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A declaration of which security mechanisms can be used across the API.
|
||||
*
|
||||
* The list of values includes alternative security requirement objects that can be used.
|
||||
* Only one of the security requirement objects need to be satisfied to authorize a request.
|
||||
* Individual operations can override this definition.
|
||||
* To make security optional, an empty security requirement `({})` can be included in the array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $security = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A list of tags used by the specification with additional metadata.
|
||||
*
|
||||
* The order of the tags can be used to reflect on their order by the parsing tools.
|
||||
* Not all tags that are used by the Operation Object must be declared.
|
||||
* The tags that are not declared may be organized randomly or based on the tools' logic.
|
||||
* Each tag name in the list must be unique.
|
||||
*
|
||||
* @var Tag[]
|
||||
*/
|
||||
public $tags = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Additional external documentation.
|
||||
*
|
||||
* @var ExternalDocumentation
|
||||
*/
|
||||
public $externalDocs = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The available webhooks for the API.
|
||||
*
|
||||
* @var Webhook[]
|
||||
*/
|
||||
public $webhooks = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @var Analysis
|
||||
*/
|
||||
public $_analysis = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['openapi', 'info'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Info::class => 'info',
|
||||
Server::class => ['servers'],
|
||||
PathItem::class => ['paths', 'path'],
|
||||
Components::class => 'components',
|
||||
Tag::class => ['tags'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Webhook::class => ['webhooks', 'webhook'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validate(?array $stack = null, ?array $skip = null, string $ref = '', $context = null): bool
|
||||
{
|
||||
if ($stack !== null || $skip !== null || $ref !== '') {
|
||||
$this->_context->logger->warning('Nested validation for ' . $this->identity() . ' not allowed');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!in_array($this->openapi, self::SUPPORTED_VERSIONS)) {
|
||||
$this->_context->logger->warning('Unsupported OpenAPI version "' . $this->openapi . '". Allowed versions are: ' . implode(', ', self::SUPPORTED_VERSIONS));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* paths is optional in 3.1.0 */
|
||||
if ($this->openapi === self::VERSION_3_0_0 && Generator::isDefault($this->paths)) {
|
||||
$this->_context->logger->warning('Required @OA\PathItem() not found');
|
||||
}
|
||||
|
||||
if ($this->openapi === self::VERSION_3_1_0
|
||||
&& Generator::isDefault($this->paths)
|
||||
&& Generator::isDefault($this->webhooks)
|
||||
&& Generator::isDefault($this->components)
|
||||
) {
|
||||
$this->_context->logger->warning("At least one of 'Required @OA\PathItem(), @OA\Components() or @OA\Webhook() not found'");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::validate([], [], '#', new \stdClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the OpenAPI documentation to a file.
|
||||
*/
|
||||
public function saveAs(string $filename, string $format = 'auto'): void
|
||||
{
|
||||
if ($format === 'auto') {
|
||||
$format = strtolower(substr($filename, -5)) === '.json' ? 'json' : 'yaml';
|
||||
}
|
||||
|
||||
if (strtolower($format) === 'json') {
|
||||
$content = $this->toJson();
|
||||
} else {
|
||||
$content = $this->toYaml();
|
||||
}
|
||||
|
||||
if (file_put_contents($filename, $content) === false) {
|
||||
throw new \Exception('Failed to saveAs("' . $filename . '", "' . $format . '")');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up an annotation with a $ref url.
|
||||
*
|
||||
* @param string $ref The $ref value, for example: "#/components/schemas/Product"
|
||||
*/
|
||||
public function ref(string $ref)
|
||||
{
|
||||
if (substr($ref, 0, 2) !== '#/') {
|
||||
// @todo Add support for external (http) refs?
|
||||
throw new \Exception('Unsupported $ref "' . $ref . '", it should start with "#/"');
|
||||
}
|
||||
|
||||
return $this->resolveRef($ref, '#/', $this, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper for ref().
|
||||
*
|
||||
* @param array|AbstractAnnotation $container
|
||||
*/
|
||||
private static function resolveRef(string $ref, string $resolved, $container, array $mapping)
|
||||
{
|
||||
if ($ref === $resolved) {
|
||||
return $container;
|
||||
}
|
||||
$path = substr($ref, strlen($resolved));
|
||||
$slash = strpos($path, '/');
|
||||
|
||||
$subpath = $slash === false ? $path : substr($path, 0, $slash);
|
||||
$property = Util::refDecode($subpath);
|
||||
$unresolved = $slash === false ? $resolved . $subpath : $resolved . $subpath . '/';
|
||||
|
||||
if (is_object($container)) {
|
||||
if (property_exists($container, $property) === false) {
|
||||
throw new \Exception('$ref "' . $ref . '" not found');
|
||||
}
|
||||
if ($slash === false) {
|
||||
return $container->{$property};
|
||||
}
|
||||
$mapping = [];
|
||||
if ($container instanceof AbstractAnnotation) {
|
||||
foreach ($container::$_nested as $nestedClass => $nested) {
|
||||
if (is_string($nested) === false && count($nested) === 2 && $nested[0] === $property) {
|
||||
$mapping[$nestedClass] = $nested[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::resolveRef($ref, $unresolved, $container->{$property}, $mapping);
|
||||
} elseif (is_array($container)) {
|
||||
if (array_key_exists($property, $container)) {
|
||||
return self::resolveRef($ref, $unresolved, $container[$property], []);
|
||||
}
|
||||
foreach ($mapping as $nestedClass => $keyField) {
|
||||
foreach ($container as $key => $item) {
|
||||
if (is_numeric($key) && is_object($item) && $item instanceof $nestedClass && (string) $item->{$keyField} === $property) {
|
||||
return self::resolveRef($ref, $unresolved, $item, []);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception('$ref "' . $unresolved . '" not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$data = parent::jsonSerialize();
|
||||
|
||||
if (false === $this->_context->isVersion(OpenApi::VERSION_3_1_0)) {
|
||||
unset($data->webhooks);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Base class for `@OA\Get`, `@OA\Post`, `@OA\Put`, etc.
|
||||
*
|
||||
* Describes a single API operation on a path.
|
||||
*
|
||||
* @see [OAI Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#operation-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
abstract class Operation extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* Key in the OpenApi "Paths Object" for this operation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A list of tags for API documentation control.
|
||||
*
|
||||
* Tags can be used for logical grouping of operations by resources or any other qualifier.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $tags = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Key in the OpenApi "Path Item Object" for this operation.
|
||||
*
|
||||
* Allowed values: 'get', 'post', put', 'patch', 'delete', 'options', 'head' and 'trace'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $method = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A short summary of what the operation does.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $summary = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A verbose explanation of the operation behavior.
|
||||
*
|
||||
* CommonMark syntax MAY be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Additional external documentation for this operation.
|
||||
*
|
||||
* @var ExternalDocumentation
|
||||
*/
|
||||
public $externalDocs = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Unique string used to identify the operation.
|
||||
*
|
||||
* The id must be unique among all operations described in the API.
|
||||
* Tools and libraries may use the operationId to uniquely identify an operation, therefore, it is recommended to
|
||||
* follow common programming naming conventions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $operationId = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A list of parameters that are applicable for this operation.
|
||||
*
|
||||
* If a parameter is already defined at the Path Item, the new definition will override it but can never remove it.
|
||||
* The list must not include duplicated parameters.
|
||||
*
|
||||
* A unique parameter is defined by a combination of a name and location.
|
||||
*
|
||||
* The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's
|
||||
* components/parameters.
|
||||
*
|
||||
* @var Parameter[]
|
||||
*/
|
||||
public $parameters = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The request body applicable for this operation.
|
||||
*
|
||||
* The requestBody is only supported in HTTP methods where the HTTP 1.1 specification RFC7231 has explicitly
|
||||
* defined semantics for request bodies. In other cases where the HTTP spec is vague, requestBody shall be ignored
|
||||
* by consumers.
|
||||
*
|
||||
* @var RequestBody
|
||||
*/
|
||||
public $requestBody = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The list of possible responses as they are returned from executing this operation.
|
||||
*
|
||||
* @var Response[]
|
||||
*/
|
||||
public $responses = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map of possible out-of band callbacks related to the parent operation.
|
||||
*
|
||||
* The key is a unique identifier for the Callback Object.
|
||||
*
|
||||
* Each value in the map is a Callback Object that describes a request that may be initiated by the API provider
|
||||
* and the expected responses. The key value used to identify the callback object is an expression, evaluated at
|
||||
* runtime, that identifies a URL to use for the callback operation.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $callbacks = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Declares this operation to be deprecated.
|
||||
*
|
||||
* Consumers should refrain from usage of the declared operation.
|
||||
*
|
||||
* Default value is false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deprecated = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A declaration of which security mechanisms can be used for this operation.
|
||||
*
|
||||
* The list of values includes alternative security requirement objects that can be used.
|
||||
*
|
||||
* Only one of the security requirement objects need to be satisfied to authorize a request.
|
||||
*
|
||||
* This definition overrides any declared top-level security.
|
||||
* To remove a top-level security declaration, an empty array can be used.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $security = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An alternative server array to service this operation.
|
||||
*
|
||||
* If an alternative server object is specified at the Path Item Object or Root level, it will be overridden by
|
||||
* this value.
|
||||
*
|
||||
* @var Server[]
|
||||
*/
|
||||
public $servers = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['responses'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'path' => 'string',
|
||||
'method' => 'string',
|
||||
'tags' => '[string]',
|
||||
'summary' => 'string',
|
||||
'description' => 'string',
|
||||
'deprecated' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Parameter::class => ['parameters'],
|
||||
PathParameter::class => ['parameters'],
|
||||
Response::class => ['responses', 'response'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Server::class => ['servers'],
|
||||
RequestBody::class => 'requestBody',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$data = parent::jsonSerialize();
|
||||
|
||||
unset($data->method);
|
||||
unset($data->path);
|
||||
|
||||
// ensure security elements are object
|
||||
if (isset($data->security) && is_array($data->security)) {
|
||||
foreach ($data->security as $key => $scheme) {
|
||||
$data->security[$key] = (object) $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validate(array $stack = [], array $skip = [], string $ref = '', $context = null): bool
|
||||
{
|
||||
if (in_array($this, $skip, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$valid = parent::validate($stack, $skip, $ref, $context);
|
||||
|
||||
if (!Generator::isDefault($this->responses)) {
|
||||
foreach ($this->responses as $response) {
|
||||
if (!Generator::isDefault($response->response) && $response->response !== 'default' && preg_match('/^([12345]{1}[0-9]{2})|([12345]{1}XX)$/', (string) $response->response) === 0) {
|
||||
$this->_context->logger->warning('Invalid value "' . $response->response . '" for ' . $response->_identity([]) . '->response, expecting "default", a HTTP Status Code or HTTP Status Code range definition in ' . $response->_context);
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_object($context) && !Generator::isDefault($this->operationId)) {
|
||||
if (!property_exists($context, 'operationIds')) {
|
||||
$context->operationIds = [];
|
||||
}
|
||||
|
||||
if (in_array($this->operationId, $context->operationIds)) {
|
||||
$this->_context->logger->warning('operationId must be unique. Duplicate value found: "' . $this->operationId . '"');
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
$context->operationIds[] = $this->operationId;
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Options extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'options';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Describes a single operation parameter.
|
||||
*
|
||||
* A unique parameter is defined by a combination of a name and location.
|
||||
*
|
||||
* @see [OAA Parameter Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Parameter extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The relative or absolute path to the endpoint.
|
||||
*
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The key into <code>Components::parameters</code> or <code>PathItem::parameters</code> array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $parameter = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The (case-sensitive) name of the parameter.
|
||||
*
|
||||
* If in is "path", the name field must correspond to the associated path segment from the path field in the Paths Object.
|
||||
*
|
||||
* If in is "header" and the name field is "Accept", "Content-Type" or "Authorization", the parameter definition shall be ignored.
|
||||
* For all other cases, the name corresponds to the parameter name used by the in property.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The location of the parameter.
|
||||
*
|
||||
* Possible values are "query", "header", "path" or "cookie".
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $in = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A brief description of the parameter.
|
||||
*
|
||||
* This could contain examples of use.
|
||||
*
|
||||
* CommonMark syntax may be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Determines whether this parameter is mandatory.
|
||||
*
|
||||
* If the parameter location is "path", this property is required and its value must be true.
|
||||
* Otherwise, the property may be included and its default value is false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $required = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Specifies that a parameter is deprecated and should be transitioned out of usage.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deprecated = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Sets the ability to pass empty-valued parameters.
|
||||
*
|
||||
* This is valid only for query parameters and allows sending a parameter with an empty value.
|
||||
*
|
||||
* Default value is false.
|
||||
*
|
||||
* If style is used, and if behavior is n/a (cannot be serialized), the value of allowEmptyValue shall be ignored.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $allowEmptyValue = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Describes how the parameter value will be serialized depending on the type of the parameter value.
|
||||
*
|
||||
* Default values (based on value of in): for query - form; for path - simple; for header - simple; for cookie - form.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $style = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* When this is true, parameter values of type array or object generate separate parameters for each value of the array or key-value pair of the map.
|
||||
*
|
||||
* For other types of parameters this property has no effect.
|
||||
*
|
||||
* When style is form, the default value is true.
|
||||
* For all other styles, the default value is false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $explode = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Determines whether the parameter value should allow reserved characters, as defined by RFC3986 :/?#[]@!$&'()*+,;= to be included without percent-encoding.
|
||||
*
|
||||
* This property only applies to parameters with an in value of query.
|
||||
*
|
||||
* The default value is false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $allowReserved = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The schema defining the type used for the parameter.
|
||||
*
|
||||
* @var Schema
|
||||
*/
|
||||
public $schema = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Example of the media type.
|
||||
*
|
||||
* The example should match the specified schema and encoding properties if present.
|
||||
* The example object is mutually exclusive of the examples object.
|
||||
* Furthermore, if referencing a schema which contains an example, the example value shall override the example provided by the schema.
|
||||
* To represent examples of media types that cannot naturally be represented in JSON or YAML, a string value can contain the example with escaping where necessary.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $example = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Examples of the media type.
|
||||
*
|
||||
* Each example should contain a value in the correct format as specified in the parameter encoding.
|
||||
* The examples object is mutually exclusive of the example object.
|
||||
* Furthermore, if referencing a schema which contains an example, the examples value shall override the example provided by the schema.
|
||||
*
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map containing the representations for the parameter.
|
||||
*
|
||||
* The key is the media type and the value describes it.
|
||||
* The map must only contain one entry.
|
||||
*
|
||||
* @var array<MediaType>|JsonContent|XmlContent|Attachable
|
||||
*/
|
||||
public $content = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Path-style parameters defined by RFC6570.
|
||||
*
|
||||
* @see [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7)
|
||||
*/
|
||||
public $matrix = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Label style parameters defined by RFC6570.
|
||||
*
|
||||
* @see [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.5)
|
||||
*/
|
||||
public $label = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Form style parameters defined by RFC6570.
|
||||
*
|
||||
* This option replaces collectionFormat with a csv (when explode is false) or multi (when explode is true) value from OpenAPI 2.0.
|
||||
*
|
||||
* @see [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.8)
|
||||
*/
|
||||
public $form = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Simple style parameters defined by RFC6570.
|
||||
*
|
||||
* This option replaces collectionFormat with a csv value from OpenAPI 2.0.
|
||||
*
|
||||
* @see [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.2)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $simple = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Space separated array values.
|
||||
*
|
||||
* This option replaces collectionFormat equal to ssv from OpenAPI 2.0.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $spaceDelimited = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Pipe separated array values.
|
||||
*
|
||||
* This option replaces collectionFormat equal to pipes from OpenAPI 2.0.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $pipeDelimited = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Provides a simple way of rendering nested objects using form parameters.
|
||||
*/
|
||||
public $deepObject = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['name', 'in'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'name' => 'string',
|
||||
'in' => ['query', 'header', 'path', 'cookie'],
|
||||
'description' => 'string',
|
||||
'style' => ['matrix', 'label', 'form', 'simple', 'spaceDelimited', 'pipeDelimited', 'deepObject'],
|
||||
'required' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Schema::class => 'schema',
|
||||
Examples::class => ['examples', 'example'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
PathItem::class,
|
||||
Operation::class,
|
||||
Get::class,
|
||||
Post::class,
|
||||
Put::class,
|
||||
Delete::class,
|
||||
Patch::class,
|
||||
Head::class,
|
||||
Options::class,
|
||||
Trace::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validate(array $stack = [], array $skip = [], string $ref = '', $context = null): bool
|
||||
{
|
||||
if (in_array($this, $skip, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$valid = parent::validate($stack, $skip, $ref, $context);
|
||||
|
||||
if (Generator::isDefault($this->ref)) {
|
||||
if ($this->in === 'body') {
|
||||
if (Generator::isDefault($this->schema)) {
|
||||
$this->_context->logger->warning('Field "schema" is required when ' . $this->identity() . ' is in "' . $this->in . '" in ' . $this->_context);
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function identity(): string
|
||||
{
|
||||
return parent::_identity(['name', 'in']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Patch extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'patch';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Describes the operations available on a single path.
|
||||
*
|
||||
* A Path Item may be empty, due to ACL constraints.
|
||||
* The path itself is still exposed to the documentation viewer, but they will not know which operations and parameters are available.
|
||||
*
|
||||
* @see [OAI Path Item Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class PathItem extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An optional, string summary, intended to apply to all operations in this path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $summary = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An optional, string description, intended to apply to all operations in this path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Key for the Path Object (OpenApi->paths array).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a GET operation on this path.
|
||||
*
|
||||
* @var Get
|
||||
*/
|
||||
public $get = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a PUT operation on this path.
|
||||
*
|
||||
* @var Put
|
||||
*/
|
||||
public $put = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a POST operation on this path.
|
||||
*
|
||||
* @var Post
|
||||
*/
|
||||
public $post = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a DELETE operation on this path.
|
||||
*
|
||||
* @var Delete
|
||||
*/
|
||||
public $delete = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a OPTIONS operation on this path.
|
||||
*
|
||||
* @var Options
|
||||
*/
|
||||
public $options = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a HEAD operation on this path.
|
||||
*
|
||||
* @var Head
|
||||
*/
|
||||
public $head = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a PATCH operation on this path.
|
||||
*
|
||||
* @var Patch
|
||||
*/
|
||||
public $patch = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A definition of a TRACE operation on this path.
|
||||
*
|
||||
* @var Trace
|
||||
*/
|
||||
public $trace = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An alternative server array to service all operations in this path.
|
||||
*
|
||||
* @var Server[]
|
||||
*/
|
||||
public $servers = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A list of parameters that are applicable for all the operations described under this path.
|
||||
*
|
||||
* These parameters can be overridden at the operation level, but cannot be removed there.
|
||||
* The list must not include duplicated parameters.
|
||||
* A unique parameter is defined by a combination of a name and location.
|
||||
* The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters.
|
||||
*
|
||||
* @var Parameter[]
|
||||
*/
|
||||
public $parameters = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'path' => 'string',
|
||||
'summary' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Get::class => 'get',
|
||||
Post::class => 'post',
|
||||
Put::class => 'put',
|
||||
Delete::class => 'delete',
|
||||
Patch::class => 'patch',
|
||||
Trace::class => 'trace',
|
||||
Head::class => 'head',
|
||||
Options::class => 'options',
|
||||
Parameter::class => ['parameters'],
|
||||
PathParameter::class => ['parameters'],
|
||||
Server::class => ['servers'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
OpenApi::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* A `@OA\Request` path parameter.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class PathParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
* This takes 'path' as the default location.
|
||||
*/
|
||||
public $in = 'path';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $required = true;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Post extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'post';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Property extends Schema
|
||||
{
|
||||
/**
|
||||
* The key into Schema->properties array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $property = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
AdditionalProperties::class,
|
||||
Schema::class,
|
||||
JsonContent::class,
|
||||
XmlContent::class,
|
||||
Property::class,
|
||||
Items::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Discriminator::class => 'discriminator',
|
||||
Items::class => 'items',
|
||||
Property::class => ['properties', 'property'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Xml::class => 'xml',
|
||||
AdditionalProperties::class => 'additionalProperties',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Put extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'put';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* A `@OA\Request` query parameter.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class QueryParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
* This takes 'query' as the default location.
|
||||
*/
|
||||
public $in = 'query';
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Attributes\JsonContent;
|
||||
use OpenApi\Attributes\XmlContent;
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Describes a single request body.
|
||||
*
|
||||
* @see [OAI Request Body Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#requestBodyObject)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class RequestBody extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The relative or absolute path to a request body.
|
||||
*
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Request body model name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $request = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A brief description of the parameter.
|
||||
*
|
||||
* This could contain examples of use.
|
||||
*
|
||||
* CommonMark syntax may be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Determines whether this parameter is mandatory.
|
||||
*
|
||||
* If the parameter location is "path", this property is required and its value must be true.
|
||||
* Otherwise, the property may be included and its default value is false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $required = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The content of the request body.
|
||||
*
|
||||
* The key is a media type or media type range and the value describes it. For requests that match multiple keys,
|
||||
* only the most specific key is applicable. e.g. text/plain overrides text/*.
|
||||
*
|
||||
* @var array<MediaType|JsonContent|XmlContent>|MediaType|JsonContent|XmlContent|Attachable
|
||||
*/
|
||||
public $content = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'description' => 'string',
|
||||
'required' => 'boolean',
|
||||
'request' => 'string',
|
||||
];
|
||||
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
Delete::class,
|
||||
Get::class,
|
||||
Head::class,
|
||||
Operation::class,
|
||||
Options::class,
|
||||
Patch::class,
|
||||
Post::class,
|
||||
Trace::class,
|
||||
Put::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
MediaType::class => ['content', 'mediaType'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Describes a single response from an API Operation, including design-time,
|
||||
* static links to operations based on the response.
|
||||
*
|
||||
* @see [OAI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#response-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Response extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The relative or absolute path to a response.
|
||||
*
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The key into Operations->responses array.
|
||||
*
|
||||
* A HTTP status code or <code>default</code>.
|
||||
*
|
||||
* @var string|int
|
||||
*/
|
||||
public $response = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A short description of the response.
|
||||
*
|
||||
* CommonMark syntax may be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Maps a header name to its definition.
|
||||
*
|
||||
* RFC7230 states header names are case insensitive.
|
||||
*
|
||||
* If a response header is defined with the name "Content-Type", it shall be ignored.
|
||||
*
|
||||
* @see [RFC7230](https://tools.ietf.org/html/rfc7230#page-22)
|
||||
*
|
||||
* @var Header[]
|
||||
*/
|
||||
public $headers = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map containing descriptions of potential response payloads.
|
||||
*
|
||||
* The key is a media type or media type range and the value describes it.
|
||||
*
|
||||
* For responses that match multiple keys, only the most specific key is applicable;
|
||||
* e.g. <code>text/plain</code> overrides <code>text/*</code>.
|
||||
*
|
||||
* @var MediaType|JsonContent|XmlContent|Attachable|array<MediaType|JsonContent|XmlContent|Attachable>
|
||||
*/
|
||||
public $content = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map of operations links that can be followed from the response.
|
||||
*
|
||||
* The key of the map is a short name for the link, following the naming constraints of the names for Component
|
||||
* Objects.
|
||||
*
|
||||
* @var Link[]
|
||||
*/
|
||||
public $links = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'description' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
MediaType::class => ['content', 'mediaType'],
|
||||
Header::class => ['headers', 'header'],
|
||||
Link::class => ['links', 'link'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
Operation::class,
|
||||
Get::class,
|
||||
Post::class,
|
||||
Put::class,
|
||||
Patch::class,
|
||||
Delete::class,
|
||||
Head::class,
|
||||
Options::class,
|
||||
Trace::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validate(array $stack = [], array $skip = [], string $ref = '', $context = null): bool
|
||||
{
|
||||
$valid = parent::validate($stack, $skip, $ref, $context);
|
||||
|
||||
if (Generator::isDefault($this->description) && Generator::isDefault($this->ref)) {
|
||||
$this->_context->logger->warning($this->identity() . ' One of description or ref is required');
|
||||
$valid = false;
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* The definition of input and output data types.
|
||||
*
|
||||
* These types can be objects, but also primitives and arrays.
|
||||
*
|
||||
* This object is based on the [JSON Schema Specification](http://json-schema.org) and uses a predefined subset of it.
|
||||
* On top of this subset, there are extensions provided by this specification to allow for more complete documentation.
|
||||
*
|
||||
* @see [OAI Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schemaObject)
|
||||
* @see [JSON Schema](http://json-schema.org/)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Schema extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The relative or absolute path to the endpoint.
|
||||
*
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The key into Components->schemas array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $schema = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Can be used to decorate a user interface with information about the data produced by this user interface.
|
||||
*
|
||||
* Preferably short; use <code>description</code> for more details.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A description will provide explanation about the purpose of the instance described by this schema.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The maximum number of properties allowed in an object instance.
|
||||
* An object instance is valid against this property if its number of properties is less than, or equal to, the value of this attribute.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $maxProperties = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The minimum number of properties allowed in an object instance.
|
||||
* An object instance is valid against this property if its number of properties is greater than, or equal to, the value of this attribute.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $minProperties = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An object instance is valid against this property if its property set contains all elements in this property's
|
||||
* array value.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $required = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A collection of properties to define for an object.
|
||||
*
|
||||
* Each property is represented as an instance of the <a href="#property">Property</a> class.
|
||||
*
|
||||
* @var Property[]
|
||||
*/
|
||||
public $properties = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The type of the schema/property.
|
||||
*
|
||||
* OpenApi v3.0: The value MUST be one of "string", "number", "integer", "boolean", "array" or "object".
|
||||
*
|
||||
* Since OpenApi v3.1 an array of types may be used.
|
||||
*
|
||||
* @var string|non-empty-array<string>
|
||||
*/
|
||||
public $type = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The extending format for the previously mentioned type. See Data Type Formats for further details.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $format = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Required if type is "array". Describes the type of items in the array.
|
||||
*
|
||||
* @var Items
|
||||
*/
|
||||
public $items = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Determines the format of the array if type array is used.
|
||||
*
|
||||
* Possible values are:
|
||||
* - csv: comma separated values foo,bar.
|
||||
* - ssv: space separated values foo bar.
|
||||
* - tsv: tab separated values foo\tbar.
|
||||
* - pipes: pipe separated values foo|bar.
|
||||
* - multi: corresponds to multiple parameter instances instead of multiple values for a single instance foo=bar&foo=baz.
|
||||
* This is valid only for parameters of type <code>query</code> or <code>formData</code>.
|
||||
* Default value is csv.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $collectionFormat = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Sets a default value to the parameter. The type of the value depends on the defined type.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor101)
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $default = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The maximum value allowed for a numeric property. This value must be a number.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor17)
|
||||
*
|
||||
* @var int|float
|
||||
*/
|
||||
public $maximum = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A boolean indicating whether the maximum value is excluded from the set of valid values.
|
||||
*
|
||||
* When set to true, the maximum value is excluded, and when false or not specified, it is included.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor17)
|
||||
*
|
||||
* @var bool|int|float
|
||||
*/
|
||||
public $exclusiveMaximum = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The minimum value allowed for a numeric property. This value must be a number.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor21)
|
||||
*
|
||||
* @var int|float
|
||||
*/
|
||||
public $minimum = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A boolean indicating whether the minimum value is excluded from the set of valid values.
|
||||
*
|
||||
* When set to true, the minimum value is excluded, and when false or not specified, it is included.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor21)
|
||||
*
|
||||
* @var bool|int|float
|
||||
*/
|
||||
public $exclusiveMinimum = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The maximum length of a string property.
|
||||
*
|
||||
* A string instance is valid against this property if its length is less than, or equal to, the value of this attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor26)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $maxLength = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The minimum length of a string property.
|
||||
*
|
||||
* A string instance is valid against this property if its length is greater than, or equal to, the value of this attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor29)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $minLength = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A string instance is considered valid if the regular expression matches the instance successfully.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pattern = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The maximum number of items allowed in an array property.
|
||||
*
|
||||
* An array instance is valid against this property if its number of items is less than, or equal to, the value of this attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor42)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $maxItems = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The minimum number of items allowed in an array property.
|
||||
*
|
||||
* An array instance is valid against this property if its number of items is greater than, or equal to, the value of this attribute.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor45)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $minItems = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A boolean value indicating whether all items in an array property must be unique.
|
||||
*
|
||||
* If this attribute is set to true, then all items in the array must be unique.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor49)
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $uniqueItems = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A collection of allowable values for a property.
|
||||
*
|
||||
* A property instance is valid against this attribute if its value is one of the values specified in this collection.
|
||||
*
|
||||
* @see [JSON schema validation](http://json-schema.org/latest/json-schema-validation.html#anchor76)
|
||||
*
|
||||
* @var string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string
|
||||
*/
|
||||
public $enum = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A numeric instance is valid against "multipleOf" if the result of the division of the instance by this
|
||||
* property's value is an integer.
|
||||
*
|
||||
* @var int|float
|
||||
*/
|
||||
public $multipleOf = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Adds support for polymorphism.
|
||||
*
|
||||
* The discriminator is an object name that is used to differentiate between other schemas which may satisfy the
|
||||
* payload description. See Composition and Inheritance for more details.
|
||||
*
|
||||
* @var Discriminator
|
||||
*/
|
||||
public $discriminator = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Declares the property as "read only".
|
||||
*
|
||||
* Relevant only for Schema "properties" definitions.
|
||||
*
|
||||
* This means that it may be sent as part of a response but should not be sent as part of the request.
|
||||
* If the property is marked as readOnly being true and is in the required list, the required will take effect on
|
||||
* the response only. A property must not be marked as both readOnly and writeOnly being true. Default value is
|
||||
* false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $readOnly = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Declares the property as "write only".
|
||||
*
|
||||
* Relevant only for Schema "properties" definitions.
|
||||
* Therefore, it may be sent as part of a request but should not be sent as part of the response.
|
||||
* If the property is marked as writeOnly being true and is in the required list, the required will take effect on
|
||||
* the request only. A property must not be marked as both readOnly and writeOnly being true. Default value is
|
||||
* false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $writeOnly = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* This may be used only on properties schemas.
|
||||
*
|
||||
* It has no effect on root schemas.
|
||||
* Adds additional metadata to describe the XML representation of this property.
|
||||
*
|
||||
* @var Xml
|
||||
*/
|
||||
public $xml = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Additional external documentation for this schema.
|
||||
*
|
||||
* @var ExternalDocumentation
|
||||
*/
|
||||
public $externalDocs = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A free-form property to include an example of an instance for this schema.
|
||||
*
|
||||
* To represent examples that cannot naturally be represented in JSON or YAML, a string value can be used to
|
||||
* contain the example with escaping where necessary.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $example = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Allows sending a null value for the defined schema.
|
||||
* Default value is false.
|
||||
*
|
||||
* This must not be used when using OpenApi version 3.1,
|
||||
* instead make the "type" property an array and add "null" as a possible type.
|
||||
*
|
||||
* @var bool
|
||||
*
|
||||
* @see https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0
|
||||
*/
|
||||
public $nullable = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Specifies that a schema is deprecated and should be transitioned out of usage.
|
||||
* Default value is false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $deprecated = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An instance validates successfully against this property if it validates successfully against all schemas
|
||||
* defined by this property's value.
|
||||
*
|
||||
* @var array<Schema|\OpenApi\Attributes\Schema>
|
||||
*/
|
||||
public $allOf = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An instance validates successfully against this property if it validates successfully against at least one
|
||||
* schema defined by this property's value.
|
||||
*
|
||||
* @var array<Schema|\OpenApi\Attributes\Schema>
|
||||
*/
|
||||
public $anyOf = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An instance validates successfully against this property if it validates successfully against exactly one schema
|
||||
* defined by this property's value.
|
||||
*
|
||||
* @var array<Schema|\OpenApi\Attributes\Schema>
|
||||
*/
|
||||
public $oneOf = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.29.
|
||||
*/
|
||||
public $not = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/latest/json-schema-validation.html#anchor64.
|
||||
*
|
||||
* @var bool|AdditionalProperties
|
||||
*/
|
||||
public $additionalProperties = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.10.
|
||||
*/
|
||||
public $additionalItems = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.14.
|
||||
*/
|
||||
public $contains = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.19.
|
||||
*/
|
||||
public $patternProperties = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.21.
|
||||
*/
|
||||
public $dependencies = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.22.
|
||||
*/
|
||||
public $propertyNames = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* http://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.1.3.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $const = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'title' => 'string',
|
||||
'description' => 'string',
|
||||
'required' => '[string]',
|
||||
'format' => 'string',
|
||||
'collectionFormat' => ['csv', 'ssv', 'tsv', 'pipes', 'multi'],
|
||||
'maximum' => 'number',
|
||||
'exclusiveMaximum' => 'boolean|integer|number',
|
||||
'minimum' => 'number',
|
||||
'exclusiveMinimum' => 'boolean|integer|number',
|
||||
'maxLength' => 'integer',
|
||||
'minLength' => 'integer',
|
||||
'pattern' => 'string',
|
||||
'maxItems' => 'integer',
|
||||
'minItems' => 'integer',
|
||||
'uniqueItems' => 'boolean',
|
||||
'multipleOf' => 'integer',
|
||||
'allOf' => '[' . Schema::class . ']',
|
||||
'oneOf' => '[' . Schema::class . ']',
|
||||
'anyOf' => '[' . Schema::class . ']',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Discriminator::class => 'discriminator',
|
||||
Items::class => 'items',
|
||||
Property::class => ['properties', 'property'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Xml::class => 'xml',
|
||||
AdditionalProperties::class => 'additionalProperties',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
Parameter::class,
|
||||
PathParameter::class,
|
||||
MediaType::class,
|
||||
Header::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$data = parent::jsonSerialize();
|
||||
|
||||
if (isset($data->const)) {
|
||||
if ($this->_context->isVersion(OpenApi::VERSION_3_0_0)) {
|
||||
$data->enum = [$data->const];
|
||||
unset($data->const);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function validate(array $stack = [], array $skip = [], string $ref = '', $context = null): bool
|
||||
{
|
||||
if ($this->type === 'array' && Generator::isDefault($this->items)) {
|
||||
$this->_context->logger->warning('@OA\\Items() is required when ' . $this->identity() . ' has type "array" in ' . $this->_context);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::validate($stack, $skip, $ref, $context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* @see [OAI Security Scheme Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#securitySchemeObject).
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class SecurityScheme extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The relative or absolute path to a security scheme.
|
||||
*
|
||||
* @see [Using refs](https://swagger.io/docs/specification/using-ref/)
|
||||
*
|
||||
* @var string|class-string|object
|
||||
*/
|
||||
public $ref = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The key into OpenApi->security array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $securityScheme = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The type of the security scheme.
|
||||
*
|
||||
* @var string|non-empty-array<string>
|
||||
*/
|
||||
public $type = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A short description for security scheme.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The name of the header or query parameter to be used.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Required The location of the API key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $in = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The flow used by the OAuth2 security scheme.
|
||||
*
|
||||
* @var Flow[]
|
||||
*/
|
||||
public $flows = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A hint to the client to identify how the bearer token is formatted.
|
||||
*
|
||||
* Bearer tokens are usually generated by an authorization server, so this information is primarily for documentation purposes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $bearerFormat = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The name of the HTTP Authorization scheme.
|
||||
*
|
||||
* @see [RFC7235](https://tools.ietf.org/html/rfc7235#section-5.1)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $scheme = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* OpenId Connect URL to discover OAuth2 configuration values. This MUST be in the form of a URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $openIdConnectUrl = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['securityScheme', 'type'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'type' => ['http', 'apiKey', 'oauth2', 'openIdConnect'],
|
||||
'description' => 'string',
|
||||
'name' => 'string',
|
||||
'bearerFormat' => 'string',
|
||||
'in' => ['query', 'header', 'cookie'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Flow::class => ['flows', 'flow'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Components::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function merge(array $annotations, bool $ignore = false): array
|
||||
{
|
||||
$unmerged = parent::merge($annotations, $ignore);
|
||||
|
||||
if ($this->type === 'oauth2') {
|
||||
$this->name = Generator::UNDEFINED;
|
||||
}
|
||||
|
||||
return $unmerged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* An object representing a server.
|
||||
*
|
||||
* @see [OAI Server Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#server-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Server extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* An URL to the target host.
|
||||
*
|
||||
* This URL supports Server Variables and may be relative,
|
||||
* to indicate that the host location is relative to the location where the OpenAPI document is being served.
|
||||
* Variable substitutions will be made when a variable is named in {brackets}.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $url = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An optional string describing the host designated by the URL.
|
||||
*
|
||||
* CommonMark syntax may be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map between a variable name and its value.
|
||||
*
|
||||
* The value is used for substitution in the server's URL template.
|
||||
*
|
||||
* @var ServerVariable[]
|
||||
*/
|
||||
public $variables = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
OpenApi::class,
|
||||
PathItem::class,
|
||||
Operation::class,
|
||||
Get::class,
|
||||
Post::class,
|
||||
Put::class,
|
||||
Delete::class,
|
||||
Patch::class,
|
||||
Head::class,
|
||||
Options::class,
|
||||
Trace::class,
|
||||
Link::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
ServerVariable::class => ['variables', 'serverVariable'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['url'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'url' => 'string',
|
||||
'description' => 'string',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* An object representing a server variable for server URL template substitution.
|
||||
*
|
||||
* @see [OAI Server Variable Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#server-variable-object)
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class ServerVariable extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The key into Server->variables array.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $serverVariable = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An enumeration of values to be used if the substitution options are from a limited set.
|
||||
*
|
||||
* @var string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string
|
||||
*/
|
||||
public $enum = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The default value to use for substitution, and to send, if an alternate value is not supplied.
|
||||
*
|
||||
* Unlike the Schema Object's default, this value must be provided by the consumer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $default = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A map between a variable name and its value.
|
||||
*
|
||||
* The value is used for substitution in the server's URL template.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $variables = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* An optional description for the server variable.
|
||||
*
|
||||
* CommonMark syntax MAY be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
Server::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['default'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'default' => 'string',
|
||||
'description' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* @see [OAI Tag Object]( https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#tagObject).
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Tag extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* The name of the tag.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* A short description for the tag. GFM syntax can be used for rich text representation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Additional external documentation for this tag.
|
||||
*
|
||||
* @var ExternalDocumentation
|
||||
*/
|
||||
public $externalDocs = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['name'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'name' => 'string',
|
||||
'description' => 'string',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
OpenApi::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class Trace extends Operation
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $method = 'trace';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
PathItem::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Acts like a `PathItem` with the main difference being that it requires `webhook` instead of `path`.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Webhook extends PathItem
|
||||
{
|
||||
/**
|
||||
* Key for the webhooks map.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $webhook = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_required = ['webhook'];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
OpenApi::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'webhook' => 'string',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* @see [OAI XML Object](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#xmlObject).
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Xml extends AbstractAnnotation
|
||||
{
|
||||
/**
|
||||
* Replaces the name of the element/attribute used for the described schema property.
|
||||
*
|
||||
* When defined within the Items Object (items), it will affect the name of the individual XML elements within the list.
|
||||
* When defined alongside type being array (outside the items), it will affect the wrapping element
|
||||
* and only if wrapped is <code>true</code>.
|
||||
*
|
||||
* If wrapped is <code>false</code>, it will be ignored.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The URL of the namespace definition. Value SHOULD be in the form of a URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $namespace = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* The prefix to be used for the name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $prefix = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* Declares whether the property definition translates to an attribute instead of an element.
|
||||
*
|
||||
* Default value is <code>false</code>.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $attribute = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* MAY be used only for an array definition.
|
||||
*
|
||||
* Signifies whether the array is wrapped (for example <code><books><book/><book/></books></code>)
|
||||
* or unwrapped (<code><book/><book/></code>).
|
||||
*
|
||||
* Default value is false. The definition takes effect only when defined alongside type being array (outside the items).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $wrapped = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_types = [
|
||||
'name' => 'string',
|
||||
'namespace' => 'string',
|
||||
'prefix' => 'string',
|
||||
'attribute' => 'boolean',
|
||||
'wrapped' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [
|
||||
AdditionalProperties::class,
|
||||
Schema::class,
|
||||
Property::class,
|
||||
Schema::class,
|
||||
Items::class,
|
||||
XmlContent::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Annotations;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Shorthand for a xml response.
|
||||
*
|
||||
* Use as `@OA\Schema` inside a `Response` and `MediaType`->`'application/xml'` will be generated.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class XmlContent extends Schema
|
||||
{
|
||||
/**
|
||||
* @var array<string,Examples>
|
||||
*/
|
||||
public $examples = Generator::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_parents = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $_nested = [
|
||||
Discriminator::class => 'discriminator',
|
||||
Items::class => 'items',
|
||||
Property::class => ['properties', 'property'],
|
||||
ExternalDocumentation::class => 'externalDocs',
|
||||
Xml::class => 'xml',
|
||||
AdditionalProperties::class => 'additionalProperties',
|
||||
Examples::class => ['examples', 'example'],
|
||||
Attachable::class => ['attachables'],
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class AdditionalProperties extends \OpenApi\Annotations\AdditionalProperties
|
||||
{
|
||||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string $enum
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $allOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $anyOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $oneOf
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
?int $maxProperties = null,
|
||||
?int $minProperties = null,
|
||||
?array $required = null,
|
||||
?array $properties = null,
|
||||
string|array|null $type = null,
|
||||
?string $format = null,
|
||||
?Items $items = null,
|
||||
?string $collectionFormat = null,
|
||||
mixed $default = Generator::UNDEFINED,
|
||||
$maximum = null,
|
||||
bool|int|float|null $exclusiveMaximum = null,
|
||||
$minimum = null,
|
||||
bool|int|float|null $exclusiveMinimum = null,
|
||||
?int $maxLength = null,
|
||||
?int $minLength = null,
|
||||
?int $maxItems = null,
|
||||
?int $minItems = null,
|
||||
?bool $uniqueItems = null,
|
||||
?string $pattern = null,
|
||||
array|string|null $enum = null,
|
||||
?Discriminator $discriminator = null,
|
||||
?bool $readOnly = null,
|
||||
?bool $writeOnly = null,
|
||||
?Xml $xml = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?bool $nullable = null,
|
||||
?bool $deprecated = null,
|
||||
?array $allOf = null,
|
||||
?array $anyOf = null,
|
||||
?array $oneOf = null,
|
||||
AdditionalProperties|bool|null $additionalProperties = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'schema' => $schema ?? Generator::UNDEFINED,
|
||||
'title' => $title ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'maxProperties' => $maxProperties ?? Generator::UNDEFINED,
|
||||
'minProperties' => $minProperties ?? Generator::UNDEFINED,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'properties' => $properties ?? Generator::UNDEFINED,
|
||||
'type' => $type ?? Generator::UNDEFINED,
|
||||
'format' => $format ?? Generator::UNDEFINED,
|
||||
'collectionFormat' => $collectionFormat ?? Generator::UNDEFINED,
|
||||
'default' => $default,
|
||||
'maximum' => $maximum ?? Generator::UNDEFINED,
|
||||
'exclusiveMaximum' => $exclusiveMaximum ?? Generator::UNDEFINED,
|
||||
'minimum' => $minimum ?? Generator::UNDEFINED,
|
||||
'exclusiveMinimum' => $exclusiveMinimum ?? Generator::UNDEFINED,
|
||||
'maxLength' => $maxLength ?? Generator::UNDEFINED,
|
||||
'minLength' => $minLength ?? Generator::UNDEFINED,
|
||||
'maxItems' => $maxItems ?? Generator::UNDEFINED,
|
||||
'minItems' => $minItems ?? Generator::UNDEFINED,
|
||||
'uniqueItems' => $uniqueItems ?? Generator::UNDEFINED,
|
||||
'pattern' => $pattern ?? Generator::UNDEFINED,
|
||||
'enum' => $enum ?? Generator::UNDEFINED,
|
||||
'readOnly' => $readOnly ?? Generator::UNDEFINED,
|
||||
'writeOnly' => $writeOnly ?? Generator::UNDEFINED,
|
||||
'xml' => $xml ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'nullable' => $nullable ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allOf' => $allOf ?? Generator::UNDEFINED,
|
||||
'anyOf' => $anyOf ?? Generator::UNDEFINED,
|
||||
'oneOf' => $oneOf ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'attachables' => $attachables ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($items, $discriminator, $externalDocs, $additionalProperties, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_ALL | \Attribute::IS_REPEATABLE)]
|
||||
class Attachable extends \OpenApi\Annotations\Attachable
|
||||
{
|
||||
public function __construct(array $properties = [])
|
||||
{
|
||||
parent::__construct($properties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class Components extends \OpenApi\Annotations\Components
|
||||
{
|
||||
/**
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema>|null $schemas
|
||||
* @param Response[]|null $responses
|
||||
* @param Parameter[]|null $parameters
|
||||
* @param RequestBody[]|null $requestBodies
|
||||
* @param Examples[]|null $examples
|
||||
* @param Header[]|null $headers
|
||||
* @param SecurityScheme[]|null $securitySchemes
|
||||
* @param Link[]|null $links
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?array $schemas = null,
|
||||
?array $responses = null,
|
||||
?array $parameters = null,
|
||||
?array $requestBodies = null,
|
||||
?array $examples = null,
|
||||
?array $headers = null,
|
||||
?array $securitySchemes = null,
|
||||
?array $links = null,
|
||||
?array $callbacks = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'callbacks' => $callbacks ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'attachables' => $attachables ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($schemas, $responses, $parameters, $examples, $requestBodies, $headers, $securitySchemes, $links, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class Contact extends \OpenApi\Annotations\Contact
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $url = null,
|
||||
?string $email = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'name' => $name ?? Generator::UNDEFINED,
|
||||
'url' => $url ?? Generator::UNDEFINED,
|
||||
'email' => $email ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
|
||||
class CookieParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $in = 'cookie';
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Delete extends \OpenApi\Annotations\Delete
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class Discriminator extends \OpenApi\Annotations\Discriminator
|
||||
{
|
||||
/**
|
||||
* @param string[]|null $mapping
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $propertyName = null,
|
||||
?array $mapping = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'propertyName' => $propertyName ?? Generator::UNDEFINED,
|
||||
'mapping' => $mapping ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||
class Examples extends \OpenApi\Annotations\Examples
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $example = null,
|
||||
?string $summary = null,
|
||||
?string $description = null,
|
||||
int|string|array|null $value = null,
|
||||
?string $externalValue = null,
|
||||
string|object|null $ref = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'example' => $example ?? Generator::UNDEFINED,
|
||||
'summary' => $summary ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'value' => $value ?? Generator::UNDEFINED,
|
||||
'externalValue' => $externalValue ?? Generator::UNDEFINED,
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
]);
|
||||
if ($attachables) {
|
||||
$this->merge($attachables);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class ExternalDocumentation extends \OpenApi\Annotations\ExternalDocumentation
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $description = null,
|
||||
?string $url = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'url' => $url ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class Flow extends \OpenApi\Annotations\Flow
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $authorizationUrl = null,
|
||||
?string $tokenUrl = null,
|
||||
?string $refreshUrl = null,
|
||||
?string $flow = null,
|
||||
?array $scopes = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'authorizationUrl' => $authorizationUrl ?? Generator::UNDEFINED,
|
||||
'tokenUrl' => $tokenUrl ?? Generator::UNDEFINED,
|
||||
'refreshUrl' => $refreshUrl ?? Generator::UNDEFINED,
|
||||
'flow' => $flow ?? Generator::UNDEFINED,
|
||||
'scopes' => $scopes ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Get extends \OpenApi\Annotations\Get
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Head extends \OpenApi\Annotations\Head
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Header extends \OpenApi\Annotations\Header
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
?string $header = null,
|
||||
?string $description = null,
|
||||
?bool $required = null,
|
||||
?Schema $schema = null,
|
||||
?bool $deprecated = null,
|
||||
?bool $allowEmptyValue = null,
|
||||
// annotation4
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'header' => $header ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allowEmptyValue' => $allowEmptyValue ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables, $schema),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
|
||||
class HeaderParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $in = 'header';
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class Info extends \OpenApi\Annotations\Info
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $version = null,
|
||||
?string $description = null,
|
||||
?string $title = null,
|
||||
?string $termsOfService = null,
|
||||
?Contact $contact = null,
|
||||
?License $license = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'version' => $version ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'title' => $title ?? Generator::UNDEFINED,
|
||||
'termsOfService' => $termsOfService ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($contact, $license, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
|
||||
class Items extends \OpenApi\Annotations\Items
|
||||
{
|
||||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string $enum
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $allOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $anyOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $oneOf
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
?int $maxProperties = null,
|
||||
?int $minProperties = null,
|
||||
?array $required = null,
|
||||
?array $properties = null,
|
||||
string|array|null $type = null,
|
||||
?string $format = null,
|
||||
?Items $items = null,
|
||||
?string $collectionFormat = null,
|
||||
mixed $default = Generator::UNDEFINED,
|
||||
$maximum = null,
|
||||
bool|int|float|null $exclusiveMaximum = null,
|
||||
$minimum = null,
|
||||
bool|int|float|null $exclusiveMinimum = null,
|
||||
?int $maxLength = null,
|
||||
?int $minLength = null,
|
||||
?int $maxItems = null,
|
||||
?int $minItems = null,
|
||||
?bool $uniqueItems = null,
|
||||
?string $pattern = null,
|
||||
array|string|null $enum = null,
|
||||
?Discriminator $discriminator = null,
|
||||
?bool $readOnly = null,
|
||||
?bool $writeOnly = null,
|
||||
?Xml $xml = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?bool $nullable = null,
|
||||
?bool $deprecated = null,
|
||||
?array $allOf = null,
|
||||
?array $anyOf = null,
|
||||
?array $oneOf = null,
|
||||
AdditionalProperties|bool|null $additionalProperties = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
// schema
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'schema' => $schema ?? Generator::UNDEFINED,
|
||||
'title' => $title ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'maxProperties' => $maxProperties ?? Generator::UNDEFINED,
|
||||
'minProperties' => $minProperties ?? Generator::UNDEFINED,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'properties' => $properties ?? Generator::UNDEFINED,
|
||||
'type' => $type ?? Generator::UNDEFINED,
|
||||
'format' => $format ?? Generator::UNDEFINED,
|
||||
'collectionFormat' => $collectionFormat ?? Generator::UNDEFINED,
|
||||
'default' => $default,
|
||||
'maximum' => $maximum ?? Generator::UNDEFINED,
|
||||
'exclusiveMaximum' => $exclusiveMaximum ?? Generator::UNDEFINED,
|
||||
'minimum' => $minimum ?? Generator::UNDEFINED,
|
||||
'exclusiveMinimum' => $exclusiveMinimum ?? Generator::UNDEFINED,
|
||||
'maxLength' => $maxLength ?? Generator::UNDEFINED,
|
||||
'minLength' => $minLength ?? Generator::UNDEFINED,
|
||||
'maxItems' => $maxItems ?? Generator::UNDEFINED,
|
||||
'minItems' => $minItems ?? Generator::UNDEFINED,
|
||||
'uniqueItems' => $uniqueItems ?? Generator::UNDEFINED,
|
||||
'pattern' => $pattern ?? Generator::UNDEFINED,
|
||||
'enum' => $enum ?? Generator::UNDEFINED,
|
||||
'readOnly' => $readOnly ?? Generator::UNDEFINED,
|
||||
'writeOnly' => $writeOnly ?? Generator::UNDEFINED,
|
||||
'xml' => $xml ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'nullable' => $nullable ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allOf' => $allOf ?? Generator::UNDEFINED,
|
||||
'anyOf' => $anyOf ?? Generator::UNDEFINED,
|
||||
'oneOf' => $oneOf ?? Generator::UNDEFINED,
|
||||
'additionalProperties' => $additionalProperties ?? Generator::UNDEFINED,
|
||||
// annotation
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'attachables' => $attachables ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($items, $discriminator, $externalDocs, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class JsonContent extends \OpenApi\Annotations\JsonContent
|
||||
{
|
||||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,Examples> $examples
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string $enum
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $allOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $anyOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $oneOf
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?array $examples = null,
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
?int $maxProperties = null,
|
||||
?int $minProperties = null,
|
||||
?array $required = null,
|
||||
?array $properties = null,
|
||||
string|array|null $type = null,
|
||||
?string $format = null,
|
||||
?Items $items = null,
|
||||
?string $collectionFormat = null,
|
||||
mixed $default = Generator::UNDEFINED,
|
||||
$maximum = null,
|
||||
bool|int|float|null $exclusiveMaximum = null,
|
||||
$minimum = null,
|
||||
bool|int|float|null $exclusiveMinimum = null,
|
||||
?int $maxLength = null,
|
||||
?int $minLength = null,
|
||||
?int $maxItems = null,
|
||||
?int $minItems = null,
|
||||
?bool $uniqueItems = null,
|
||||
?string $pattern = null,
|
||||
array|string|null $enum = null,
|
||||
?Discriminator $discriminator = null,
|
||||
?bool $readOnly = null,
|
||||
?bool $writeOnly = null,
|
||||
?Xml $xml = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?bool $nullable = null,
|
||||
?bool $deprecated = null,
|
||||
?array $allOf = null,
|
||||
?array $anyOf = null,
|
||||
?array $oneOf = null,
|
||||
AdditionalProperties|bool|null $additionalProperties = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'examples' => $examples ?? Generator::UNDEFINED,
|
||||
// schema
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'schema' => $schema ?? Generator::UNDEFINED,
|
||||
'title' => $title ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'maxProperties' => $maxProperties ?? Generator::UNDEFINED,
|
||||
'minProperties' => $minProperties ?? Generator::UNDEFINED,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'properties' => $properties ?? Generator::UNDEFINED,
|
||||
'type' => $type ?? Generator::UNDEFINED,
|
||||
'format' => $format ?? Generator::UNDEFINED,
|
||||
'collectionFormat' => $collectionFormat ?? Generator::UNDEFINED,
|
||||
'default' => $default,
|
||||
'maximum' => $maximum ?? Generator::UNDEFINED,
|
||||
'exclusiveMaximum' => $exclusiveMaximum ?? Generator::UNDEFINED,
|
||||
'minimum' => $minimum ?? Generator::UNDEFINED,
|
||||
'exclusiveMinimum' => $exclusiveMinimum ?? Generator::UNDEFINED,
|
||||
'maxLength' => $maxLength ?? Generator::UNDEFINED,
|
||||
'minLength' => $minLength ?? Generator::UNDEFINED,
|
||||
'maxItems' => $maxItems ?? Generator::UNDEFINED,
|
||||
'minItems' => $minItems ?? Generator::UNDEFINED,
|
||||
'uniqueItems' => $uniqueItems ?? Generator::UNDEFINED,
|
||||
'pattern' => $pattern ?? Generator::UNDEFINED,
|
||||
'enum' => $enum ?? Generator::UNDEFINED,
|
||||
'readOnly' => $readOnly ?? Generator::UNDEFINED,
|
||||
'writeOnly' => $writeOnly ?? Generator::UNDEFINED,
|
||||
'xml' => $xml ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'nullable' => $nullable ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allOf' => $allOf ?? Generator::UNDEFINED,
|
||||
'anyOf' => $anyOf ?? Generator::UNDEFINED,
|
||||
'oneOf' => $oneOf ?? Generator::UNDEFINED,
|
||||
'additionalProperties' => $additionalProperties ?? Generator::UNDEFINED,
|
||||
// annotation
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'attachables' => $attachables ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($items, $discriminator, $externalDocs, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class License extends \OpenApi\Annotations\License
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $identifier = null,
|
||||
?string $url = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'name' => $name ?? Generator::UNDEFINED,
|
||||
'identifier' => $identifier ?? Generator::UNDEFINED,
|
||||
'url' => $url ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
|
||||
class Link extends \OpenApi\Annotations\Link
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,mixed> $parameters
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $link = null,
|
||||
?string $operationRef = null,
|
||||
string|object|null $ref = null,
|
||||
?string $operationId = null,
|
||||
?array $parameters = null,
|
||||
mixed $requestBody = null,
|
||||
?string $description = null,
|
||||
?Server $server = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'link' => $link ?? Generator::UNDEFINED,
|
||||
'operationRef' => $operationRef ?? Generator::UNDEFINED,
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'operationId' => $operationId ?? Generator::UNDEFINED,
|
||||
'parameters' => $parameters ?? Generator::UNDEFINED,
|
||||
'requestBody' => $requestBody ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($server, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class MediaType extends \OpenApi\Annotations\MediaType
|
||||
{
|
||||
/**
|
||||
* @param array<string,Examples> $examples
|
||||
* @param array<string,mixed> $encoding
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $mediaType = null,
|
||||
?Schema $schema = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?array $examples = null,
|
||||
?array $encoding = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'mediaType' => $mediaType ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'encoding' => $encoding ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($schema, $examples, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class OpenApi extends \OpenApi\Annotations\OpenApi
|
||||
{
|
||||
/**
|
||||
* @param Server[]|null $servers
|
||||
* @param Tag[]|null $tags
|
||||
* @param PathItem[]|null $paths
|
||||
* @param Webhook[]|null $webhooks
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string $openapi = self::DEFAULT_VERSION,
|
||||
?Info $info = null,
|
||||
?array $servers = null,
|
||||
?array $security = null,
|
||||
?array $tags = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
?array $paths = null,
|
||||
?Components $components = null,
|
||||
?array $webhooks = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'openapi' => $openapi,
|
||||
'security' => $security ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($info, $servers, $tags, $externalDocs, $paths, $components, $webhooks, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
trait OperationTrait
|
||||
{
|
||||
/**
|
||||
* @param array $security
|
||||
* @param Server[] $servers
|
||||
* @param string[] $tags
|
||||
* @param Parameter[] $parameters
|
||||
* @param Response[] $responses
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $path = null,
|
||||
?string $operationId = null,
|
||||
?string $description = null,
|
||||
?string $summary = null,
|
||||
?array $security = null,
|
||||
?array $servers = null,
|
||||
?RequestBody $requestBody = null,
|
||||
?array $tags = null,
|
||||
?array $parameters = null,
|
||||
?array $responses = null,
|
||||
?array $callbacks = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
?bool $deprecated = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'path' => $path ?? Generator::UNDEFINED,
|
||||
'operationId' => $operationId ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'summary' => $summary ?? Generator::UNDEFINED,
|
||||
'security' => $security ?? Generator::UNDEFINED,
|
||||
'servers' => $servers ?? Generator::UNDEFINED,
|
||||
'tags' => $tags ?? Generator::UNDEFINED,
|
||||
'callbacks' => $callbacks ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($requestBody, $responses, $parameters, $externalDocs, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Options extends \OpenApi\Annotations\Options
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
|
||||
class Parameter extends \OpenApi\Annotations\Parameter
|
||||
{
|
||||
use ParameterTrait;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
trait ParameterTrait
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,Examples> $examples
|
||||
* @param array<MediaType>|JsonContent|XmlContent|Attachable|null $content
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $parameter = null,
|
||||
?string $name = null,
|
||||
?string $description = null,
|
||||
?string $in = null,
|
||||
?bool $required = null,
|
||||
?bool $deprecated = null,
|
||||
?bool $allowEmptyValue = null,
|
||||
string|object|null $ref = null,
|
||||
?Schema $schema = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?array $examples = null,
|
||||
array|JsonContent|XmlContent|Attachable|null $content = null,
|
||||
?string $style = null,
|
||||
?bool $explode = null,
|
||||
?bool $allowReserved = null,
|
||||
?array $spaceDelimited = null,
|
||||
?array $pipeDelimited = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'parameter' => $parameter ?? Generator::UNDEFINED,
|
||||
'name' => $name ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'in' => Generator::isDefault($this->in) ? $in : $this->in,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allowEmptyValue' => $allowEmptyValue ?? Generator::UNDEFINED,
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'style' => $style ?? Generator::UNDEFINED,
|
||||
'explode' => $explode ?? Generator::UNDEFINED,
|
||||
'allowReserved' => $allowReserved ?? Generator::UNDEFINED,
|
||||
'spaceDelimited' => $spaceDelimited ?? Generator::UNDEFINED,
|
||||
'pipeDelimited' => $pipeDelimited ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($schema, $examples, $content, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Patch extends \OpenApi\Annotations\Patch
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class PathItem extends \OpenApi\Annotations\PathItem
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param Server[]|null $servers
|
||||
* @param Parameter[]|null $parameters
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $path = null,
|
||||
string|object|null $ref = null,
|
||||
?string $summary = null,
|
||||
?string $description = null,
|
||||
?Get $get = null,
|
||||
?Put $put = null,
|
||||
?Post $post = null,
|
||||
?Delete $delete = null,
|
||||
?Options $options = null,
|
||||
?Head $head = null,
|
||||
?Patch $patch = null,
|
||||
?Trace $trace = null,
|
||||
?array $servers = null,
|
||||
?array $parameters = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'path' => $path ?? Generator::UNDEFINED,
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'summary' => $summary ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($get, $put, $post, $delete, $options, $head, $patch, $trace, $servers, $parameters, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
|
||||
class PathParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $in = 'path';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $required = true;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Post extends \OpenApi\Annotations\Post
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::TARGET_CLASS_CONSTANT | \Attribute::IS_REPEATABLE)]
|
||||
class Property extends \OpenApi\Annotations\Property
|
||||
{
|
||||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string $enum
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $allOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $anyOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $oneOf
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $property = null,
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
?int $maxProperties = null,
|
||||
?int $minProperties = null,
|
||||
?array $required = null,
|
||||
?array $properties = null,
|
||||
string|array|null $type = null,
|
||||
?string $format = null,
|
||||
?Items $items = null,
|
||||
?string $collectionFormat = null,
|
||||
mixed $default = Generator::UNDEFINED,
|
||||
$maximum = null,
|
||||
bool|int|float|null $exclusiveMaximum = null,
|
||||
$minimum = null,
|
||||
bool|int|float|null $exclusiveMinimum = null,
|
||||
?int $maxLength = null,
|
||||
?int $minLength = null,
|
||||
?int $maxItems = null,
|
||||
?int $minItems = null,
|
||||
?bool $uniqueItems = null,
|
||||
?string $pattern = null,
|
||||
array|string|null $enum = null,
|
||||
?Discriminator $discriminator = null,
|
||||
?bool $readOnly = null,
|
||||
?bool $writeOnly = null,
|
||||
?Xml $xml = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?bool $nullable = null,
|
||||
?bool $deprecated = null,
|
||||
?array $allOf = null,
|
||||
?array $anyOf = null,
|
||||
?array $oneOf = null,
|
||||
AdditionalProperties|bool|null $additionalProperties = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'property' => $property ?? Generator::UNDEFINED,
|
||||
// schema
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'schema' => $schema ?? Generator::UNDEFINED,
|
||||
'title' => $title ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'maxProperties' => $maxProperties ?? Generator::UNDEFINED,
|
||||
'minProperties' => $minProperties ?? Generator::UNDEFINED,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'properties' => $properties ?? Generator::UNDEFINED,
|
||||
'type' => $type ?? Generator::UNDEFINED,
|
||||
'format' => $format ?? Generator::UNDEFINED,
|
||||
'collectionFormat' => $collectionFormat ?? Generator::UNDEFINED,
|
||||
'default' => $default,
|
||||
'maximum' => $maximum ?? Generator::UNDEFINED,
|
||||
'exclusiveMaximum' => $exclusiveMaximum ?? Generator::UNDEFINED,
|
||||
'minimum' => $minimum ?? Generator::UNDEFINED,
|
||||
'exclusiveMinimum' => $exclusiveMinimum ?? Generator::UNDEFINED,
|
||||
'maxLength' => $maxLength ?? Generator::UNDEFINED,
|
||||
'minLength' => $minLength ?? Generator::UNDEFINED,
|
||||
'maxItems' => $maxItems ?? Generator::UNDEFINED,
|
||||
'minItems' => $minItems ?? Generator::UNDEFINED,
|
||||
'uniqueItems' => $uniqueItems ?? Generator::UNDEFINED,
|
||||
'pattern' => $pattern ?? Generator::UNDEFINED,
|
||||
'enum' => $enum ?? Generator::UNDEFINED,
|
||||
'readOnly' => $readOnly ?? Generator::UNDEFINED,
|
||||
'writeOnly' => $writeOnly ?? Generator::UNDEFINED,
|
||||
'xml' => $xml ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'nullable' => $nullable ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allOf' => $allOf ?? Generator::UNDEFINED,
|
||||
'anyOf' => $anyOf ?? Generator::UNDEFINED,
|
||||
'oneOf' => $oneOf ?? Generator::UNDEFINED,
|
||||
'additionalProperties' => $additionalProperties ?? Generator::UNDEFINED,
|
||||
// annotation
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'attachables' => $attachables ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($items, $discriminator, $externalDocs, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Put extends \OpenApi\Annotations\Put
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
|
||||
class QueryParameter extends Parameter
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $in = 'query';
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Annotations as OA;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)]
|
||||
class RequestBody extends OA\RequestBody
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<MediaType|JsonContent|XmlContent>|MediaType|JsonContent|XmlContent|Attachable|null $content
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
?string $request = null,
|
||||
?string $description = null,
|
||||
?bool $required = null,
|
||||
array|MediaType|JsonContent|XmlContent|Attachable|null $content = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'request' => $request ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($content, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Response extends OA\Response
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param Header[] $headers
|
||||
* @param MediaType|JsonContent|XmlContent|Attachable|array<MediaType|JsonContent|XmlContent|Attachable> $content
|
||||
* @param Link[] $links
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
int|string|null $response = null,
|
||||
?string $description = null,
|
||||
?array $headers = null,
|
||||
MediaType|JsonContent|XmlContent|Attachable|array|null $content = null,
|
||||
?array $links = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'response' => $response ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($headers, $content, $links, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||
class Schema extends \OpenApi\Annotations\Schema
|
||||
{
|
||||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param string[] $required
|
||||
* @param Property[] $properties
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string $enum
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $allOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $anyOf
|
||||
* @param array<Schema|\OpenApi\Annotations\Schema> $oneOf
|
||||
* @param mixed $const
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
// schema
|
||||
string|object|null $ref = null,
|
||||
?string $schema = null,
|
||||
?string $title = null,
|
||||
?string $description = null,
|
||||
?int $maxProperties = null,
|
||||
?int $minProperties = null,
|
||||
?array $required = null,
|
||||
?array $properties = null,
|
||||
string|array|null $type = null,
|
||||
?string $format = null,
|
||||
?Items $items = null,
|
||||
?string $collectionFormat = null,
|
||||
mixed $default = Generator::UNDEFINED,
|
||||
$maximum = null,
|
||||
bool|int|float|null $exclusiveMaximum = null,
|
||||
$minimum = null,
|
||||
bool|int|float|null $exclusiveMinimum = null,
|
||||
?int $maxLength = null,
|
||||
?int $minLength = null,
|
||||
?int $maxItems = null,
|
||||
?int $minItems = null,
|
||||
?bool $uniqueItems = null,
|
||||
?string $pattern = null,
|
||||
array|string|null $enum = null,
|
||||
?Discriminator $discriminator = null,
|
||||
?bool $readOnly = null,
|
||||
?bool $writeOnly = null,
|
||||
?Xml $xml = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
mixed $example = Generator::UNDEFINED,
|
||||
?bool $nullable = null,
|
||||
?bool $deprecated = null,
|
||||
?array $allOf = null,
|
||||
?array $anyOf = null,
|
||||
?array $oneOf = null,
|
||||
AdditionalProperties|bool|null $additionalProperties = null,
|
||||
mixed $const = Generator::UNDEFINED,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'schema' => $schema ?? Generator::UNDEFINED,
|
||||
'title' => $title ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'maxProperties' => $maxProperties ?? Generator::UNDEFINED,
|
||||
'minProperties' => $minProperties ?? Generator::UNDEFINED,
|
||||
'required' => $required ?? Generator::UNDEFINED,
|
||||
'properties' => $properties ?? Generator::UNDEFINED,
|
||||
'type' => $type ?? Generator::UNDEFINED,
|
||||
'format' => $format ?? Generator::UNDEFINED,
|
||||
'collectionFormat' => $collectionFormat ?? Generator::UNDEFINED,
|
||||
'default' => $default,
|
||||
'maximum' => $maximum ?? Generator::UNDEFINED,
|
||||
'exclusiveMaximum' => $exclusiveMaximum ?? Generator::UNDEFINED,
|
||||
'minimum' => $minimum ?? Generator::UNDEFINED,
|
||||
'exclusiveMinimum' => $exclusiveMinimum ?? Generator::UNDEFINED,
|
||||
'maxLength' => $maxLength ?? Generator::UNDEFINED,
|
||||
'minLength' => $minLength ?? Generator::UNDEFINED,
|
||||
'maxItems' => $maxItems ?? Generator::UNDEFINED,
|
||||
'minItems' => $minItems ?? Generator::UNDEFINED,
|
||||
'uniqueItems' => $uniqueItems ?? Generator::UNDEFINED,
|
||||
'pattern' => $pattern ?? Generator::UNDEFINED,
|
||||
'enum' => $enum ?? Generator::UNDEFINED,
|
||||
'readOnly' => $readOnly ?? Generator::UNDEFINED,
|
||||
'writeOnly' => $writeOnly ?? Generator::UNDEFINED,
|
||||
'xml' => $xml ?? Generator::UNDEFINED,
|
||||
'example' => $example,
|
||||
'nullable' => $nullable ?? Generator::UNDEFINED,
|
||||
'deprecated' => $deprecated ?? Generator::UNDEFINED,
|
||||
'allOf' => $allOf ?? Generator::UNDEFINED,
|
||||
'anyOf' => $anyOf ?? Generator::UNDEFINED,
|
||||
'oneOf' => $oneOf ?? Generator::UNDEFINED,
|
||||
'additionalProperties' => $additionalProperties ?? Generator::UNDEFINED,
|
||||
'const' => $const,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'attachables' => $attachables ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($items, $discriminator, $externalDocs, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
|
||||
class SecurityScheme extends \OpenApi\Annotations\SecurityScheme
|
||||
{
|
||||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param Flow[] $flows
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
string|object|null $ref = null,
|
||||
?string $securityScheme = null,
|
||||
string|array|null $type = null,
|
||||
?string $description = null,
|
||||
?string $name = null,
|
||||
?string $in = null,
|
||||
?string $bearerFormat = null,
|
||||
?string $scheme = null,
|
||||
?string $openIdConnectUrl = null,
|
||||
?array $flows = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'securityScheme' => $securityScheme ?? Generator::UNDEFINED,
|
||||
'type' => $type ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'name' => $name ?? Generator::UNDEFINED,
|
||||
'in' => $in ?? Generator::UNDEFINED,
|
||||
'bearerFormat' => $bearerFormat ?? Generator::UNDEFINED,
|
||||
'scheme' => $scheme ?? Generator::UNDEFINED,
|
||||
'openIdConnectUrl' => $openIdConnectUrl ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($flows, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
|
||||
class Server extends \OpenApi\Annotations\Server
|
||||
{
|
||||
/**
|
||||
* @param ServerVariable[] $variables
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $url = null,
|
||||
?string $description = null,
|
||||
?array $variables = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'url' => $url ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($variables, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class ServerVariable extends \OpenApi\Annotations\ServerVariable
|
||||
{
|
||||
/**
|
||||
* @param string[]|int[]|float[]|bool[]|\UnitEnum[]|class-string|null $enum
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $serverVariable = null,
|
||||
?string $description = null,
|
||||
?string $default = null,
|
||||
array|string|null $enum = null,
|
||||
?array $variables = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'serverVariable' => $serverVariable ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'default' => $default ?? Generator::UNDEFINED,
|
||||
'enum' => $enum ?? Generator::UNDEFINED,
|
||||
'variables' => $variables ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Tag extends \OpenApi\Annotations\Tag
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $description = null,
|
||||
?ExternalDocumentation $externalDocs = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'name' => $name ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($externalDocs, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Trace extends \OpenApi\Annotations\Trace
|
||||
{
|
||||
use OperationTrait;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Webhook extends \OpenApi\Annotations\Webhook
|
||||
{
|
||||
/**
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param Server[]|null $servers
|
||||
* @param Parameter[]|null $parameters
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $webhook = null,
|
||||
?string $path = null,
|
||||
string|object|null $ref = null,
|
||||
?string $summary = null,
|
||||
?string $description = null,
|
||||
?Get $get = null,
|
||||
?Put $put = null,
|
||||
?Post $post = null,
|
||||
?Delete $delete = null,
|
||||
?Options $options = null,
|
||||
?Head $head = null,
|
||||
?Patch $patch = null,
|
||||
?Trace $trace = null,
|
||||
?array $servers = null,
|
||||
?array $parameters = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'webhook' => $webhook ?? Generator::UNDEFINED,
|
||||
'path' => $path ?? Generator::UNDEFINED,
|
||||
'ref' => $ref ?? Generator::UNDEFINED,
|
||||
'summary' => $summary ?? Generator::UNDEFINED,
|
||||
'description' => $description ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($get, $put, $post, $delete, $options, $head, $patch, $trace, $servers, $parameters, $attachables),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user