welcome back to dyb-tech
This commit is contained in:
@@ -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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Attributes;
|
||||
|
||||
use OpenApi\Generator;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class Xml extends \OpenApi\Annotations\Xml
|
||||
{
|
||||
/**
|
||||
* @param array<string,mixed>|null $x
|
||||
* @param Attachable[]|null $attachables
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name = null,
|
||||
?string $namespace = null,
|
||||
?string $prefix = null,
|
||||
?bool $attribute = null,
|
||||
?bool $wrapped = null,
|
||||
// annotation
|
||||
?array $x = null,
|
||||
?array $attachables = null
|
||||
) {
|
||||
parent::__construct([
|
||||
'name' => $name ?? Generator::UNDEFINED,
|
||||
'namespace' => $namespace ?? Generator::UNDEFINED,
|
||||
'prefix' => $prefix ?? Generator::UNDEFINED,
|
||||
'attribute' => $attribute ?? Generator::UNDEFINED,
|
||||
'wrapped' => $wrapped ?? Generator::UNDEFINED,
|
||||
'x' => $x ?? Generator::UNDEFINED,
|
||||
'value' => $this->combine($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 XmlContent extends \OpenApi\Annotations\XmlContent
|
||||
{
|
||||
/**
|
||||
* @param string|non-empty-array<string>|null $type
|
||||
* @param string|class-string|object|null $ref
|
||||
* @param array<string,Examples> $examples
|
||||
* @param string[] $required
|
||||
* @param int|float $maximum
|
||||
* @param int|float $minimum
|
||||
* @param Property[] $properties
|
||||
* @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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi;
|
||||
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Loggers\DefaultLogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* The context in which the annotation is parsed.
|
||||
*
|
||||
* Contexts are nested to reflect the code/parsing hierarchy. They include useful metadata
|
||||
* which the processors can use to augment the annotations.
|
||||
*
|
||||
* @property string|null $comment The PHP DocComment
|
||||
* @property string|null $filename
|
||||
* @property int|null $line
|
||||
* @property int|null $character
|
||||
* @property string|null $namespace
|
||||
* @property array|null $uses
|
||||
* @property string|null $class
|
||||
* @property string|null $interface
|
||||
* @property string|null $trait
|
||||
* @property string|null $enum
|
||||
* @property array|string|null $extends Interfaces may extend a list of interfaces
|
||||
* @property array|null $implements
|
||||
* @property string|null $method
|
||||
* @property string|null $property
|
||||
* @property string|\ReflectionType|null $type
|
||||
* @property bool|null $static Indicate a static method
|
||||
* @property bool|null $nullable Indicate a nullable value
|
||||
* @property bool|null $generated Indicate the context was generated by a processor or
|
||||
* the serializer
|
||||
* @property OA\AbstractAnnotation|null $nested
|
||||
* @property OA\AbstractAnnotation[]|null $annotations
|
||||
* @property LoggerInterface|null $logger Guaranteed to be set when using the `Generator`
|
||||
* @property array|null $scanned Details of file scanner when using ReflectionAnalyser
|
||||
* @property string|null $version The OpenAPI version in use
|
||||
*/
|
||||
#[\AllowDynamicProperties]
|
||||
class Context
|
||||
{
|
||||
/**
|
||||
* Prototypical inheritance for properties.
|
||||
*
|
||||
* @var Context|null
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
public function clone()
|
||||
{
|
||||
return new Context(get_object_vars($this), $this->parent);
|
||||
}
|
||||
|
||||
public function __construct(array $properties = [], ?Context $parent = null)
|
||||
{
|
||||
foreach ($properties as $property => $value) {
|
||||
$this->{$property} = $value;
|
||||
}
|
||||
$this->parent = $parent;
|
||||
|
||||
$this->logger = $this->logger ?: new DefaultLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property is set directly on this context and not its parent context.
|
||||
*
|
||||
* Example: $c->is('method') or $c->is('class')
|
||||
*/
|
||||
public function is(string $property): bool
|
||||
{
|
||||
return property_exists($this, $property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property is NOT set directly on this context and its parent context.
|
||||
*
|
||||
* Example: $c->not('method') or $c->not('class')
|
||||
*/
|
||||
public function not(string $property): bool
|
||||
{
|
||||
return $this->is($property) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the context containing the specified property.
|
||||
*/
|
||||
public function with(string $property): ?Context
|
||||
{
|
||||
if ($this->is($property)) {
|
||||
return $this;
|
||||
}
|
||||
if ($this->parent !== null) {
|
||||
return $this->parent->with($property);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root context.
|
||||
*/
|
||||
public function root(): Context
|
||||
{
|
||||
if ($this->parent !== null) {
|
||||
return $this->parent->root();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if one of the given version numbers matches the current OpenAPI version.
|
||||
*
|
||||
* @param string|array $versions One or more version numbers
|
||||
*/
|
||||
public function isVersion($versions): bool
|
||||
{
|
||||
if (!$this->version) {
|
||||
throw new \RuntimeException('Version is only available reliably for validation and serialization');
|
||||
}
|
||||
|
||||
$versions = (array) $versions;
|
||||
$currentVersion = $this->version ?: OA\OpenApi::DEFAULT_VERSION;
|
||||
|
||||
return in_array($currentVersion, $versions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export location for debugging.
|
||||
*
|
||||
* @return string Example: "file1.php on line 12"
|
||||
*/
|
||||
public function getDebugLocation(): string
|
||||
{
|
||||
$location = '';
|
||||
if ($this->class && ($this->method || $this->property)) {
|
||||
$location .= $this->fullyQualifiedName($this->class);
|
||||
if ($this->method) {
|
||||
$location .= ($this->static ? '::' : '->') . $this->method . '()';
|
||||
} elseif ($this->property) {
|
||||
$location .= ($this->static ? '::$' : '->') . $this->property;
|
||||
}
|
||||
}
|
||||
if ($this->filename) {
|
||||
if ($location !== '') {
|
||||
$location .= ' in ';
|
||||
}
|
||||
$location .= $this->filename;
|
||||
}
|
||||
if ($this->line) {
|
||||
if ($location !== '') {
|
||||
$location .= ' on';
|
||||
}
|
||||
$location .= ' line ' . $this->line;
|
||||
if ($this->character) {
|
||||
$location .= ':' . $this->character;
|
||||
}
|
||||
}
|
||||
|
||||
return $location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the context tree to get the property value.
|
||||
*/
|
||||
public function __get(string $property)
|
||||
{
|
||||
if ($this->parent !== null) {
|
||||
return $this->parent->{$property};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getDebugLocation();
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
return ['-' => $this->getDebugLocation()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Context based on the debug_backtrace.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public static function detect(int $index = 0): Context
|
||||
{
|
||||
// trigger_deprecation('zircote/swagger-php', '4.0', 'Context detecting is deprecated');
|
||||
|
||||
$context = new Context();
|
||||
$backtrace = debug_backtrace();
|
||||
$position = $backtrace[$index];
|
||||
if (isset($position['file'])) {
|
||||
$context->filename = $position['file'];
|
||||
}
|
||||
if (isset($position['line'])) {
|
||||
$context->line = $position['line'];
|
||||
}
|
||||
$caller = $backtrace[$index + 1] ?? null;
|
||||
if (isset($caller['function'])) {
|
||||
$context->method = $caller['function'];
|
||||
if (isset($caller['type']) && $caller['type'] === '::') {
|
||||
$context->static = true;
|
||||
}
|
||||
}
|
||||
if (isset($caller['class'])) {
|
||||
$fqn = explode('\\', $caller['class']);
|
||||
$context->class = array_pop($fqn);
|
||||
if (count($fqn)) {
|
||||
$context->namespace = implode('\\', $fqn);
|
||||
}
|
||||
}
|
||||
|
||||
// @todo extract namespaces and use statements
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the fully qualified name.
|
||||
*/
|
||||
public function fullyQualifiedName(?string $source): string
|
||||
{
|
||||
if ($source === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->namespace) {
|
||||
$namespace = str_replace('\\\\', '\\', '\\' . $this->namespace . '\\');
|
||||
} else {
|
||||
// global namespace
|
||||
$namespace = '\\';
|
||||
}
|
||||
|
||||
$thisSource = $this->class ?? $this->interface ?? $this->trait;
|
||||
if ($thisSource && strcasecmp($source, $thisSource) === 0) {
|
||||
return $namespace . $thisSource;
|
||||
}
|
||||
$pos = strpos($source, '\\');
|
||||
if ($pos !== false) {
|
||||
if ($pos === 0) {
|
||||
// Fully qualified name (\Foo\Bar)
|
||||
return $source;
|
||||
}
|
||||
// Qualified name (Foo\Bar)
|
||||
if ($this->uses) {
|
||||
foreach ($this->uses as $alias => $aliasedNamespace) {
|
||||
$alias .= '\\';
|
||||
if (strcasecmp(substr($source, 0, strlen($alias)), $alias) === 0) {
|
||||
// Aliased namespace (use \Long\Namespace as Foo)
|
||||
return '\\' . $aliasedNamespace . substr($source, strlen($alias) - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($this->uses) {
|
||||
// Unqualified name (Foo)
|
||||
foreach ($this->uses as $alias => $aliasedNamespace) {
|
||||
if (strcasecmp($alias, $source) === 0) {
|
||||
return '\\' . $aliasedNamespace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $namespace . $source;
|
||||
}
|
||||
}
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
use OpenApi\Analysers\AnalyserInterface;
|
||||
use OpenApi\Analysers\AttributeAnnotationFactory;
|
||||
use OpenApi\Analysers\DocBlockAnnotationFactory;
|
||||
use OpenApi\Analysers\ReflectionAnalyser;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Loggers\DefaultLogger;
|
||||
use OpenApi\Processors\ProcessorInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* OpenApi spec generator.
|
||||
*
|
||||
* Scans PHP source code and generates OpenApi specifications from the found OpenApi annotations.
|
||||
*
|
||||
* This is an object-oriented alternative to using the now deprecated `\OpenApi\scan()` function and
|
||||
* static class properties of the `Analyzer` and `Analysis` classes.
|
||||
*/
|
||||
class Generator
|
||||
{
|
||||
/**
|
||||
* Allows Annotation classes to know the context of the annotation that is being processed.
|
||||
*
|
||||
* @var Context|null
|
||||
*/
|
||||
public static $context;
|
||||
|
||||
/** @var string Magic value to differentiate between null and undefined. */
|
||||
public const UNDEFINED = '@OA\Generator::UNDEFINED🙈';
|
||||
|
||||
/** @var array<string,string> */
|
||||
public const DEFAULT_ALIASES = ['oa' => 'OpenApi\\Annotations'];
|
||||
/** @var array<string> */
|
||||
public const DEFAULT_NAMESPACES = ['OpenApi\\Annotations\\'];
|
||||
|
||||
/** @var array<string,string> Map of namespace aliases to be supported by doctrine. */
|
||||
protected $aliases;
|
||||
|
||||
/** @var array<string>|null List of annotation namespaces to be autoloaded by doctrine. */
|
||||
protected $namespaces;
|
||||
|
||||
/** @var AnalyserInterface|null The configured analyzer. */
|
||||
protected $analyser;
|
||||
|
||||
/** @var array<string,mixed> */
|
||||
protected $config = [];
|
||||
|
||||
/** @var array<ProcessorInterface|callable>|null List of configured processors. */
|
||||
protected $processors = null;
|
||||
|
||||
/** @var LoggerInterface|null PSR logger. */
|
||||
protected $logger = null;
|
||||
|
||||
/**
|
||||
* OpenApi version override.
|
||||
*
|
||||
* If set, it will override the version set in the `OpenApi` annotation.
|
||||
*
|
||||
* Due to the order of processing any conditional code using this (via `Context::$version`)
|
||||
* must come only after the analysis is finished.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $version = null;
|
||||
|
||||
private $configStack;
|
||||
|
||||
public function __construct(?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
|
||||
$this->setAliases(self::DEFAULT_ALIASES);
|
||||
$this->setNamespaces(self::DEFAULT_NAMESPACES);
|
||||
|
||||
// kinda config stack to stay BC...
|
||||
// @deprecated Can be removed once doctrine/annotations 2.0 becomes mandatory
|
||||
$this->configStack = new class() {
|
||||
protected $generator;
|
||||
|
||||
public function push(Generator $generator): void
|
||||
{
|
||||
$this->generator = $generator;
|
||||
/* @phpstan-ignore-next-line */
|
||||
if (class_exists(AnnotationRegistry::class, true) && method_exists(AnnotationRegistry::class, 'registerLoader')) {
|
||||
// keeping track of &this->generator allows to 'disable' the loader after we are done;
|
||||
// no unload, unfortunately :/
|
||||
$gref = &$this->generator;
|
||||
AnnotationRegistry::registerLoader(
|
||||
function (string $class) use (&$gref): bool {
|
||||
if ($gref) {
|
||||
foreach ($gref->getNamespaces() as $namespace) {
|
||||
if (strtolower(substr($class, 0, strlen($namespace))) === strtolower($namespace)) {
|
||||
$loaded = class_exists($class);
|
||||
if (!$loaded && $namespace === 'OpenApi\\Annotations\\') {
|
||||
if (in_array(strtolower(substr($class, 20)), ['definition', 'path'])) {
|
||||
// Detected an 2.x annotation?
|
||||
throw new \Exception('The annotation @SWG\\' . substr($class, 20) . '() is deprecated. Found in ' . Generator::$context . "\nFor more information read the migration guide: https://github.com/zircote/swagger-php/blob/master/docs/Migrating-to-v3.md");
|
||||
}
|
||||
}
|
||||
|
||||
return $loaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function pop(): void
|
||||
{
|
||||
$this->generator = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function isDefault($value): bool
|
||||
{
|
||||
return $value === Generator::UNDEFINED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getAliases(): array
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
public function addAlias(string $alias, string $namespace): Generator
|
||||
{
|
||||
$this->aliases[$alias] = $namespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAliases(array $aliases): Generator
|
||||
{
|
||||
$this->aliases = $aliases;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string>|null
|
||||
*/
|
||||
public function getNamespaces(): ?array
|
||||
{
|
||||
return $this->namespaces;
|
||||
}
|
||||
|
||||
public function addNamespace(string $namespace): Generator
|
||||
{
|
||||
$namespaces = (array) $this->getNamespaces();
|
||||
$namespaces[] = $namespace;
|
||||
|
||||
return $this->setNamespaces(array_unique($namespaces));
|
||||
}
|
||||
|
||||
public function setNamespaces(?array $namespaces): Generator
|
||||
{
|
||||
$this->namespaces = $namespaces;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAnalyser(): AnalyserInterface
|
||||
{
|
||||
$this->analyser = $this->analyser ?: new ReflectionAnalyser([new DocBlockAnnotationFactory(), new AttributeAnnotationFactory()]);
|
||||
$this->analyser->setGenerator($this);
|
||||
|
||||
return $this->analyser;
|
||||
}
|
||||
|
||||
public function setAnalyser(?AnalyserInterface $analyser): Generator
|
||||
{
|
||||
$this->analyser = $analyser;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDefaultConfig(): array
|
||||
{
|
||||
return [
|
||||
'operationId' => [
|
||||
'hash' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getConfig(): array
|
||||
{
|
||||
return $this->config + $this->getDefaultConfig();
|
||||
}
|
||||
|
||||
protected function normaliseConfig(array $config): array
|
||||
{
|
||||
$normalised = [];
|
||||
foreach ($config as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
$token = explode('=', $value);
|
||||
if (2 == count($token)) {
|
||||
// 'operationId.hash=false'
|
||||
[$key, $value] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($value, ['true', 'false'])) {
|
||||
$value = 'true' == $value;
|
||||
}
|
||||
|
||||
$token = explode('.', $key);
|
||||
if (2 == count($token)) {
|
||||
// 'operationId.hash' => false
|
||||
$normalised[$token[0]][$token[1]] = $value;
|
||||
} else {
|
||||
$normalised[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $normalised;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set generator and/or processor config.
|
||||
*
|
||||
* @param array<string,mixed> $config
|
||||
*/
|
||||
public function setConfig(array $config): Generator
|
||||
{
|
||||
$this->config = $this->normaliseConfig($config) + $this->config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<ProcessorInterface|callable>
|
||||
*/
|
||||
public function getProcessors(): array
|
||||
{
|
||||
if (null === $this->processors) {
|
||||
$this->processors = [
|
||||
new Processors\DocBlockDescriptions(),
|
||||
new Processors\MergeIntoOpenApi(),
|
||||
new Processors\MergeIntoComponents(),
|
||||
new Processors\ExpandClasses(),
|
||||
new Processors\ExpandInterfaces(),
|
||||
new Processors\ExpandTraits(),
|
||||
new Processors\ExpandEnums(),
|
||||
new Processors\AugmentSchemas(),
|
||||
new Processors\AugmentProperties(),
|
||||
new Processors\BuildPaths(),
|
||||
new Processors\AugmentParameters(),
|
||||
new Processors\AugmentRefs(),
|
||||
new Processors\MergeJsonContent(),
|
||||
new Processors\MergeXmlContent(),
|
||||
new Processors\OperationId(),
|
||||
new Processors\CleanUnmerged(),
|
||||
];
|
||||
}
|
||||
|
||||
$config = $this->getConfig();
|
||||
foreach ($this->processors as $processor) {
|
||||
$rc = new \ReflectionClass($processor);
|
||||
|
||||
// apply config
|
||||
$processorKey = lcfirst($rc->getShortName());
|
||||
if (array_key_exists($processorKey, $config)) {
|
||||
foreach ($config[$processorKey] as $name => $value) {
|
||||
$setter = 'set' . ucfirst($name);
|
||||
if (method_exists($processor, $setter)) {
|
||||
$processor->{$setter}($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->processors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<ProcessorInterface|callable>|null $processors
|
||||
*/
|
||||
public function setProcessors(?array $processors): Generator
|
||||
{
|
||||
$this->processors = $processors;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|ProcessorInterface $processor
|
||||
* @param class-string|null $before
|
||||
*/
|
||||
public function addProcessor($processor, ?string $before = null): Generator
|
||||
{
|
||||
$processors = $this->getProcessors();
|
||||
if (!$before) {
|
||||
$processors[] = $processor;
|
||||
} else {
|
||||
$tmp = [];
|
||||
foreach ($processors as $current) {
|
||||
if ($current instanceof $before) {
|
||||
$tmp[] = $processor;
|
||||
}
|
||||
$tmp[] = $current;
|
||||
}
|
||||
$processors = $tmp;
|
||||
}
|
||||
|
||||
$this->setProcessors($processors);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|ProcessorInterface $processor
|
||||
*/
|
||||
public function removeProcessor($processor, bool $silent = false): Generator
|
||||
{
|
||||
$processors = $this->getProcessors();
|
||||
if (false === ($key = array_search($processor, $processors, true))) {
|
||||
if ($silent) {
|
||||
return $this;
|
||||
}
|
||||
throw new \InvalidArgumentException('Processor not found');
|
||||
}
|
||||
unset($processors[$key]);
|
||||
$this->setProcessors($processors);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update/replace an existing processor with a new one.
|
||||
*
|
||||
* @param ProcessorInterface|callable $processor the new processor
|
||||
* @param null|callable $matcher Optional matcher callable to identify the processor to replace.
|
||||
* If none given, matching is based on the processors class.
|
||||
*/
|
||||
public function updateProcessor($processor, ?callable $matcher = null): Generator
|
||||
{
|
||||
$matcher = $matcher ?: function ($other) use ($processor): bool {
|
||||
$otherClass = get_class($other);
|
||||
|
||||
return $processor instanceof $otherClass;
|
||||
};
|
||||
|
||||
$processors = array_map(function ($other) use ($processor, $matcher) {
|
||||
return $matcher($other) ? $processor : $other;
|
||||
}, $this->getProcessors());
|
||||
$this->setProcessors($processors);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLogger(): ?LoggerInterface
|
||||
{
|
||||
return $this->logger ?: new DefaultLogger();
|
||||
}
|
||||
|
||||
public function getVersion(): ?string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function setVersion(?string $version): Generator
|
||||
{
|
||||
$this->version = $version;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function scan(iterable $sources, array $options = []): ?OA\OpenApi
|
||||
{
|
||||
// merge with defaults
|
||||
$config = $options + [
|
||||
'aliases' => self::DEFAULT_ALIASES,
|
||||
'namespaces' => self::DEFAULT_NAMESPACES,
|
||||
'analyser' => null,
|
||||
'analysis' => null,
|
||||
'processors' => null,
|
||||
'logger' => null,
|
||||
'validate' => true,
|
||||
'version' => null,
|
||||
];
|
||||
|
||||
return (new Generator($config['logger']))
|
||||
->setVersion($config['version'])
|
||||
->setAliases($config['aliases'])
|
||||
->setNamespaces($config['namespaces'])
|
||||
->setAnalyser($config['analyser'])
|
||||
->setProcessors($config['processors'])
|
||||
->generate($sources, $config['analysis'], $config['validate']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run code in the context of this generator.
|
||||
*
|
||||
* @param callable $callable Callable in the form of
|
||||
* `function(Generator $generator, Analysis $analysis, Context $context): mixed`
|
||||
*
|
||||
* @return mixed the result of the `callable`
|
||||
*/
|
||||
public function withContext(callable $callable)
|
||||
{
|
||||
$rootContext = new Context([
|
||||
'version' => $this->getVersion(),
|
||||
'logger' => $this->getLogger(),
|
||||
]);
|
||||
$analysis = new Analysis([], $rootContext);
|
||||
|
||||
$this->configStack->push($this);
|
||||
try {
|
||||
return $callable($this, $analysis, $rootContext);
|
||||
} finally {
|
||||
$this->configStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate OpenAPI spec by scanning the given source files.
|
||||
*
|
||||
* @param iterable $sources PHP source files to scan.
|
||||
* Supported sources:
|
||||
* * string - file / directory name
|
||||
* * \SplFileInfo
|
||||
* * \Symfony\Component\Finder\Finder
|
||||
* @param null|Analysis $analysis custom analysis instance
|
||||
* @param bool $validate flag to enable/disable validation of the returned spec
|
||||
*/
|
||||
public function generate(iterable $sources, ?Analysis $analysis = null, bool $validate = true): ?OA\OpenApi
|
||||
{
|
||||
$rootContext = new Context([
|
||||
'version' => $this->getVersion(),
|
||||
'logger' => $this->getLogger(),
|
||||
]);
|
||||
$analysis = $analysis ?: new Analysis([], $rootContext);
|
||||
|
||||
$this->configStack->push($this);
|
||||
try {
|
||||
$this->scanSources($sources, $analysis, $rootContext);
|
||||
|
||||
// post-processing
|
||||
$analysis->process($this->getProcessors());
|
||||
|
||||
if ($analysis->openapi) {
|
||||
$analysis->openapi->openapi = $this->version ?: $analysis->openapi->openapi;
|
||||
$rootContext->version = $analysis->openapi->openapi;
|
||||
}
|
||||
|
||||
// validation
|
||||
if ($validate) {
|
||||
$analysis->validate();
|
||||
}
|
||||
} finally {
|
||||
$this->configStack->pop();
|
||||
}
|
||||
|
||||
return $analysis->openapi;
|
||||
}
|
||||
|
||||
protected function scanSources(iterable $sources, Analysis $analysis, Context $rootContext): void
|
||||
{
|
||||
$analyser = $this->getAnalyser();
|
||||
|
||||
foreach ($sources as $source) {
|
||||
if (is_iterable($source)) {
|
||||
$this->scanSources($source, $analysis, $rootContext);
|
||||
} else {
|
||||
$resolvedSource = $source instanceof \SplFileInfo ? $source->getPathname() : realpath($source);
|
||||
if (!$resolvedSource) {
|
||||
$rootContext->logger->warning(sprintf('Skipping invalid source: %s', $source));
|
||||
continue;
|
||||
}
|
||||
if (is_dir($resolvedSource)) {
|
||||
$this->scanSources(Util::finder($resolvedSource), $analysis, $rootContext);
|
||||
} else {
|
||||
$rootContext->logger->debug(sprintf('Analysing source: %s', $resolvedSource));
|
||||
$analysis->addAnalysis($analyser->fromFile($resolvedSource, $rootContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Loggers;
|
||||
|
||||
use Psr\Log\AbstractLogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
class ConsoleLogger extends AbstractLogger implements LoggerInterface
|
||||
{
|
||||
public const COLOR_ERROR = "\033[31m";
|
||||
public const COLOR_WARNING = "\033[33m";
|
||||
public const COLOR_STOP = "\033[0m";
|
||||
|
||||
private const LOG_LEVELS_UP_TO_NOTICE = [
|
||||
LogLevel::DEBUG,
|
||||
LogLevel::INFO,
|
||||
LogLevel::NOTICE,
|
||||
];
|
||||
|
||||
/** @var bool */
|
||||
protected $loggedMessageAboveNotice = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $debug;
|
||||
|
||||
public function __construct(bool $debug = false)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
}
|
||||
|
||||
public function loggedMessageAboveNotice(): bool
|
||||
{
|
||||
return $this->loggedMessageAboveNotice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $level
|
||||
* @param string|\Exception $message
|
||||
* @param array $context additional details; supports custom `prefix` and `exception`
|
||||
*/
|
||||
public function log($level, $message, array $context = []): void
|
||||
{
|
||||
$prefix = '';
|
||||
$color = '';
|
||||
// level adjustments
|
||||
switch ($level) {
|
||||
case LogLevel::DEBUG:
|
||||
if (!$this->debug) {
|
||||
return;
|
||||
}
|
||||
$prefix = 'Debug: ';
|
||||
// no break
|
||||
case LogLevel::WARNING:
|
||||
$prefix = $prefix ?: ($context['prefix'] ?? 'Warning: ');
|
||||
$color = static::COLOR_WARNING;
|
||||
break;
|
||||
case LogLevel::ERROR:
|
||||
$prefix = $context['prefix'] ?? 'Error: ';
|
||||
$color = static::COLOR_ERROR;
|
||||
break;
|
||||
}
|
||||
$stop = !empty($color) ? static::COLOR_STOP : '';
|
||||
|
||||
if (!in_array($level, self::LOG_LEVELS_UP_TO_NOTICE, true)) {
|
||||
$this->loggedMessageAboveNotice = true;
|
||||
}
|
||||
|
||||
/** @var ?\Exception $exception */
|
||||
$exception = $context['exception'] ?? null;
|
||||
if ($message instanceof \Exception) {
|
||||
$exception = $message;
|
||||
$message = $exception->getMessage();
|
||||
}
|
||||
|
||||
$logLine = sprintf('%s%s%s%s', $color, $prefix, $message, $stop);
|
||||
error_log($logLine);
|
||||
|
||||
if ($this->debug) {
|
||||
if ($exception) {
|
||||
error_log($exception->getTraceAsString());
|
||||
} elseif (!empty($logLine)) {
|
||||
$stack = explode(PHP_EOL, (new \Exception())->getTraceAsString());
|
||||
// self
|
||||
array_shift($stack);
|
||||
// AbstractLogger
|
||||
array_shift($stack);
|
||||
foreach ($stack as $line) {
|
||||
error_log($line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @license Apache 2.0
|
||||
*/
|
||||
|
||||
namespace OpenApi\Loggers;
|
||||
|
||||
use Psr\Log\AbstractLogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
class DefaultLogger extends AbstractLogger implements LoggerInterface
|
||||
{
|
||||
public function log($level, $message, array $context = []): void
|
||||
{
|
||||
if (LogLevel::DEBUG == $level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($message instanceof \Exception) {
|
||||
$message = $message->getMessage();
|
||||
}
|
||||
|
||||
if (in_array($level, [LogLevel::NOTICE, LogLevel::INFO])) {
|
||||
$error_level = E_USER_NOTICE;
|
||||
} else {
|
||||
$error_level = E_USER_WARNING;
|
||||
}
|
||||
|
||||
trigger_error($message, $error_level);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user