Server IP : 213.176.29.180 / Your IP : 3.128.201.71 Web Server : Apache System : Linux 213.176.29.180.hostiran.name 4.18.0-553.22.1.el8_10.x86_64 #1 SMP Tue Sep 24 05:16:59 EDT 2024 x86_64 User : webtaragh ( 1001) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON Directory (0750) : /home/webtaragh/public_html/wp-content/../ |
[ Home ] | [ C0mmand ] | [ Upload File ] |
---|
php-openapi/bin/php-openapi 0000644 00000036115 14736103140 0011704 0 ustar 00 #!/usr/bin/env php <?php /** * PHP OpenAPI validation tool * * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ use cebe\openapi\ReferenceContext; $composerAutoload = [ __DIR__ . '/../vendor/autoload.php', // standalone with "composer install" run __DIR__ . '/../../../autoload.php', // script is installed as a composer binary ]; foreach ($composerAutoload as $autoload) { if (file_exists($autoload)) { require($autoload); break; } } // Send all errors to stderr ini_set('display_errors', 'stderr'); // open streams if not in CLI sapi defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w')); defined('STDERR') or define('STDERR', fopen('php://stderr', 'w')); $command = null; $inputFile = null; $inputFormat = null; $outputFile = null; $outputFormat = null; $silentMode = false; $referenceMode = ReferenceContext::RESOLVE_MODE_ALL; foreach($argv as $k => $arg) { if ($k == 0) { continue; } if ($arg[0] == '-' || $arg === 'help') { $arg = explode('=', $arg); switch($arg[0]) { case '--read-yaml': if ($inputFormat === null) { $inputFormat = 'yaml'; } else { error("Conflicting arguments: only one of --read-json or --read-yaml is allowed!", "usage"); } break; case '--read-json': if ($inputFormat === null) { $inputFormat = 'json'; } else { error("Conflicting arguments: only one of --read-json or --read-yaml is allowed!", "usage"); } break; case '--resolve-none': $referenceMode = false; break; case '--resolve-external': $referenceMode = ReferenceContext::RESOLVE_MODE_INLINE; break; case '--resolve-all': $referenceMode = ReferenceContext::RESOLVE_MODE_ALL; break; case '--write-yaml': if ($outputFormat === null) { $outputFormat = 'yaml'; } else { error("Conflicting arguments: only one of --write-json or --write-yaml is allowed!", "usage"); } break; case '--write-json': if ($outputFormat === null) { $outputFormat = 'json'; } else { error("Conflicting arguments: only one of --write-json or --write-yaml is allowed!", "usage"); } break; case '-s': case '--silent': $silentMode = true; break; case '-h': case '--help': case 'help': print_formatted( "\BPHP OpenAPI 3 tool\C\n" . "\B------------------\C\n" . "by Carsten Brandt <mail@cebe.cc>\n\n", STDERR ); usage(); break; default: error("Unknown argument " . $arg[0], "usage"); } } else { if ($command === null) { $command = $arg; // inline is an alias for "convert --resolve-external" if ($command === 'inline') { $command = 'convert'; $referenceMode = ReferenceContext::RESOLVE_MODE_INLINE; } } elseif ($inputFile === null) { $inputFile = $arg; } elseif ($outputFile === null) { if ($command !== 'convert') { error("Too many arguments: " . $arg, "usage"); } $outputFile = $arg; } else { error("Too many arguments: " . $arg, "usage"); } } } switch ($command) { case 'validate': $errors = []; $openApi = read_input($inputFile, $inputFormat); $referenceContext = new ReferenceContext($openApi, $inputFile ? realpath($inputFile) : ''); $referenceContext->throwException = false; $referenceContext->mode = ReferenceContext::RESOLVE_MODE_INLINE; $openApi->resolveReferences($referenceContext); $openApi->setDocumentContext($openApi, new \cebe\openapi\json\JsonPointer('')); // Validate $openApi->validate(); $errors = array_merge($errors, $openApi->getErrors()); $validator = new JsonSchema\Validator; $openApiData = $openApi->getSerializableData(); $validator->validate($openApiData, (object)['$ref' => 'file://' . dirname(__DIR__) . '/schemas/openapi-v3.0.json']); if ($validator->isValid() && empty($errors)) { if(!$silentMode) { print_formatted("The supplied API Description \B\Gvalidates\C against the OpenAPI v3.0 schema.\n", STDERR); } exit(0); } if (!empty($errors)) { if ($inputFile === null) { print_formatted("\BErrors found while reading the API description from STDIN:\C\n", STDERR); } else { print_formatted("\BErrors found while reading the API description from {$inputFile}:\C\n", STDERR); } foreach ($errors as $error) { if (($openPos = strpos($error, '[')) !== false && ($closePos = strpos($error, ']')) !== false && $openPos < $closePos) { $error = escape_formatted(substr($error, 0, $openPos + 1)) . '\Y' . escape_formatted(substr($error, $openPos + 1, $closePos - $openPos - 1)) . '\C' . escape_formatted(substr($error, $closePos)); } else { $error = escape_formatted($error); } print_formatted("- " . $error . "\n", STDERR); } } if (!$validator->isValid()) { print_formatted("\BOpenAPI v3.0 schema violations:\C\n", STDERR); $errors = $validator->getErrors(); foreach ($errors as $error) { // hide some errors triggered by other errors further down the path if (strpos($error['message'], 'The property $ref is required') !== false && substr($error['property'], -4, 4) === '$ref') { $hasErrorInPath = false; foreach ($errors as $suberror) { if ($suberror['property'] !== $error['property'] && strpos($suberror['property'], substr($error['property'], 0, -4)) === 0) { $hasErrorInPath = true; break; } } if ($hasErrorInPath) { continue; } } if (strpos($error['message'], 'Failed to match exactly one schema') !== false) { $hasErrorInPath = false; foreach ($errors as $suberror) { if (strpos($suberror['property'], $error['property'] . '.') === 0) { $hasErrorInPath = true; break; } } if ($hasErrorInPath) { continue; } } print_formatted(sprintf("- [\Y%s\C] %s\n", escape_formatted($error['property']), escape_formatted($error['message'])), STDERR); } } exit(2); break; case 'convert': $openApi = read_input($inputFile, $inputFormat); try { // set document context for correctly converting recursive references $openApi->setDocumentContext($openApi, new \cebe\openapi\json\JsonPointer('')); if ($referenceMode) { $referenceContext = new ReferenceContext($openApi, $inputFile ? realpath($inputFile) : ''); $referenceContext->mode = $referenceMode; $openApi->resolveReferences($referenceContext); } } catch (\cebe\openapi\exceptions\UnresolvableReferenceException $e) { error("[\e[33m{$e->context}\e[0m] " . $e->getMessage()); } if ($outputFile === null) { if ($outputFormat === null) { error("No output fromat specified, please specify --write-json or --write-yaml.", "usage"); } elseif ($outputFormat === 'json') { fwrite(STDOUT, \cebe\openapi\Writer::writeToJson($openApi)); } else { fwrite(STDOUT, \cebe\openapi\Writer::writeToYaml($openApi)); } fclose(STDOUT); exit(0); } if ($outputFormat === null) { if (strtolower(substr($outputFile, -5, 5)) === '.json') { $outputFormat = 'json'; } elseif (strtolower(substr($outputFile, -5, 5)) === '.yaml') { $outputFormat = 'yaml'; } elseif (strtolower(substr($outputFile, -4, 4)) === '.yml') { $outputFormat = 'yaml'; } else { error("Failed to detect output format from file name, please specify --write-json or --write-yaml.", "usage"); } } if ($outputFormat === 'json') { \cebe\openapi\Writer::writeToJsonFile($openApi, $outputFile); } else { \cebe\openapi\Writer::writeToYamlFile($openApi, $outputFile); } exit(0); break; case null: error("No command specified.", "usage"); break; default: error("Unknown command " . $command, "usage"); } // functions function read_input($inputFile, $inputFormat) { try { if ($inputFile === null) { $fileContent = file_get_contents("php://stdin"); if ($inputFormat === null) { $inputFormat = (ltrim($fileContent) === '{' && rtrim($fileContent) === '}') ? 'json' : 'yaml'; } if ($inputFormat === 'json') { $openApi = \cebe\openapi\Reader::readFromJson($fileContent); } else { $openApi = \cebe\openapi\Reader::readFromYaml($fileContent); } } else { if (!file_exists($inputFile)) { error("File does not exist: " . $inputFile); } if ($inputFormat === null) { if (strtolower(substr($inputFile, -5, 5)) === '.json') { $inputFormat = 'json'; } elseif (strtolower(substr($inputFile, -5, 5)) === '.yaml') { $inputFormat = 'yaml'; } elseif (strtolower(substr($inputFile, -4, 4)) === '.yml') { $inputFormat = 'yaml'; } else { error("Failed to detect input format from file name, please specify --read-json or --read-yaml.", "usage"); } } if ($inputFormat === 'json') { $openApi = \cebe\openapi\Reader::readFromJsonFile(realpath($inputFile), \cebe\openapi\spec\OpenApi::class, false); } else { $openApi = \cebe\openapi\Reader::readFromYamlFile(realpath($inputFile), \cebe\openapi\spec\OpenApi::class, false); } } $openApi->setDocumentContext($openApi, new \cebe\openapi\json\JsonPointer('')); } catch (Symfony\Component\Yaml\Exception\ParseException $e) { error($e->getMessage()); exit(1); } catch (cebe\openapi\exceptions\IOException $e) { error($e->getMessage()); exit(1); } return $openApi; } /** * Display usage information */ function usage() { global $argv; $cmd = basename($argv[0]); print_formatted(<<<EOF Usage: $cmd \B<command>\C [\Y<options>\C] [\Ginput.yml\C|\Ginput.json\C] [\Goutput.yml\C|\Goutput.json\C] The following commands are available: \Bvalidate\C Validate the API Description in the specified \Ginput file\C against the OpenAPI v3.0 schema. Note: the validation is performed in two steps. The results are composed of (1) structural errors found while reading the API Description file, and (2) violations of the OpenAPI v3.0 schema. If no input file is specified input will be read from STDIN. The tool will try to auto-detect the content type of the input, but may fail to do so. You may specify \Y--read-yaml\C or \Y--read-json\C to force the file type. Exits with code 2 on validation errors, 1 on other errors and 0 on success. \Bconvert\C Convert a JSON or YAML input file to JSON or YAML output file. If no input file is specified input will be read from STDIN. If no output file is specified output will be written to STDOUT. The tool will try to auto-detect the content type of the input and output file, but may fail to do so. You may specify \Y--read-yaml\C or \Y--read-json\C to force the input file type. and \Y--write-yaml\C or \Y--write-json\C to force the output file type. By default all references are resolved (replaced with the object referred to). You can control handling of references with the following arguments: \Y--resolve-none\C Do not resolve references. \Y--resolve-external\C Only resolve references that point to external files. This process is often referred to as "inlining". \Y--resolve-all\C Resolve all references (default). Recursive pointers will stay references. \Binline\C Convert a JSON or YAML input file to JSON or YAML output file and resolve all external references. The output will be a single API Description file. This is a shortcut for calling \Bconvert\C \Y--resolve-external\C. \Bhelp\C Shows this usage information. Options: \Y--read-json\C force reading input as JSON. Auto-detect if not specified. \Y--read-yaml\C force reading input as YAML. Auto-detect if not specified. \Y--write-json\C force writing output as JSON. Auto-detect if not specified. \Y--write-yaml\C force writing output as YAML. Auto-detect if not specified. \Y-s, --silent\C silent mode. Will hide all success/information messages and only print errors. EOF , STDERR ); exit(1); } /** * Send custom error message to stderr * @param $message string * @param $callback mixed called before script exit * @return void */ function error($message, $callback = null) { print_formatted("\B\RError\C: " . escape_formatted($message) . "\n", STDERR); if (is_callable($callback)) { call_user_func($callback); } exit(1); } function print_formatted($string, $stream) { fwrite($stream, strtr($string, [ '\\Y' => "\033[33m", // yellow '\\G' => "\033[32m", // green '\\R' => "\033[31m", // red '\\B' => "\033[1m", // bold '\\C' => "\033[0m", // clear '\\\\' => '\\', ])); } function escape_formatted($string) { return strtr($string, ['\\' => '\\\\']); } php-openapi/src/ReferenceContext.php 0000644 00000022174 14736103140 0013534 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi; use cebe\openapi\exceptions\IOException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\JsonPointer; use cebe\openapi\spec\Reference; use Symfony\Component\Yaml\Yaml; /** * ReferenceContext represents a context in which references are resolved. */ class ReferenceContext { /** * only resolve external references. * The result will be a single API description file with references * inside of the file structure. */ const RESOLVE_MODE_INLINE = 'inline'; /** * resolve all references, except recursive ones. */ const RESOLVE_MODE_ALL = 'all'; /** * @var bool whether to throw UnresolvableReferenceException in case a reference can not * be resolved. If `false` errors are added to the Reference Objects error list instead. */ public $throwException = true; /** * @var string */ public $mode = self::RESOLVE_MODE_ALL; /** * @var SpecObjectInterface */ private $_baseSpec; /** * @var string */ private $_uri; /** * @var ReferenceContextCache */ private $_cache; /** * ReferenceContext constructor. * @param SpecObjectInterface $base the base object of the spec. * @param string $uri the URI to the base object. * @param ReferenceContextCache $cache cache instance for storing referenced file data. * @throws UnresolvableReferenceException in case an invalid or non-absolute URI is provided. */ public function __construct(?SpecObjectInterface $base, string $uri, $cache = null) { $this->_baseSpec = $base; $this->_uri = $this->normalizeUri($uri); $this->_cache = $cache ?? new ReferenceContextCache(); if ($cache === null && $base !== null) { $this->_cache->set($this->_uri, null, $base); } } public function getCache(): ReferenceContextCache { return $this->_cache; } /** * @throws UnresolvableReferenceException in case an invalid or non-absolute URI is provided. */ private function normalizeUri($uri) { if (strpos($uri, '://') !== false) { $parts = parse_url($uri); if (isset($parts['path'])) { $parts['path'] = $this->reduceDots($parts['path']); } return $this->buildUri($parts); } if (strncmp($uri, '/', 1) === 0) { $uri = $this->reduceDots($uri); return "file://$uri"; } if (stripos(PHP_OS, 'WIN') === 0 && strncmp(substr($uri, 1), ':\\', 2) === 0) { $uri = $this->reduceDots($uri); return "file://" . strtr($uri, [' ' => '%20', '\\' => '/']); } throw new UnresolvableReferenceException('Can not resolve references for a specification given as a relative path.'); } private function buildUri($parts) { $scheme = !empty($parts['scheme']) ? $parts['scheme'] . '://' : ''; $host = $parts['host'] ?? ''; $port = !empty($parts['port']) ? ':' . $parts['port'] : ''; $user = $parts['user'] ?? ''; $pass = !empty($parts['pass']) ? ':' . $parts['pass'] : ''; $pass = ($user || $pass) ? "$pass@" : ''; $path = $parts['path'] ?? ''; $query = !empty($parts['query']) ? '?' . $parts['query'] : ''; $fragment = !empty($parts['fragment']) ? '#' . $parts['fragment'] : ''; return "$scheme$user$pass$host$port$path$query$fragment"; } private function reduceDots($path) { $parts = explode('/', ltrim($path, '/')); $c = count($parts); $parentOffset = 1; for ($i = 0; $i < $c; $i++) { if ($parts[$i] === '.') { unset($parts[$i]); continue; } if ($i > 0 && $parts[$i] === '..' && $parts[$i - $parentOffset] !== '..') { unset($parts[$i - $parentOffset]); unset($parts[$i]); $parentOffset += 2; } } return '/'.implode('/', $parts); } /** * Returns parent directory's path. * This method is similar to `dirname()` except that it will treat * both \ and / as directory separators, independent of the operating system. * * @param string $path A path string. * @return string the parent directory's path. * @see http://www.php.net/manual/en/function.dirname.php * @see https://github.com/yiisoft/yii2/blob/e1f6761dfd9eba1ff1260cd37b04936aaa4959b5/framework/helpers/BaseStringHelper.php#L75-L92 */ private function dirname($path) { $pos = mb_strrpos(str_replace('\\', '/', $path), '/'); if ($pos !== false) { return mb_substr($path, 0, $pos); } return ''; } public function getBaseSpec(): ?SpecObjectInterface { return $this->_baseSpec; } public function getUri(): string { return $this->_uri; } /** * Resolve a relative URI to an absolute URI in the current context. * @param string $uri * @throws UnresolvableReferenceException * @return string */ public function resolveRelativeUri(string $uri): string { $parts = parse_url($uri); // absolute URI, no need to combine with baseURI if (isset($parts['scheme'])) { if (isset($parts['path'])) { $parts['path'] = $this->reduceDots($parts['path']); } return $this->buildUri($parts); } // convert absolute path on windows to a file:// URI. This is probably incomplete but should work with the majority of paths. if (stripos(PHP_OS, 'WIN') === 0 && strncmp(substr($uri, 1), ':\\', 2) === 0) { // convert absolute path on windows to a file:// URI. This is probably incomplete but should work with the majority of paths. $absoluteUri = "file:///" . strtr($uri, [' ' => '%20', '\\' => '/']); return $absoluteUri . (isset($parts['fragment']) ? '#' . $parts['fragment'] : ''); } $baseUri = $this->getUri(); $baseParts = parse_url($baseUri); if (isset($parts['path'][0]) && $parts['path'][0] === '/') { // absolute path $baseParts['path'] = $this->reduceDots($parts['path']); } elseif (isset($parts['path'])) { // relative path $baseParts['path'] = $this->reduceDots(rtrim($this->dirname($baseParts['path'] ?? ''), '/') . '/' . $parts['path']); } else { throw new UnresolvableReferenceException("Invalid URI: '$uri'"); } $baseParts['query'] = $parts['query'] ?? null; $baseParts['fragment'] = $parts['fragment'] ?? null; return $this->buildUri($baseParts); } /** * Fetch referenced file by URI. * * The current context will cache files by URI, so they are only loaded once. * * @throws IOException in case the file is not readable or fetching the file * from a remote URL failed. */ public function fetchReferencedFile($uri) { if ($this->_cache->has('FILE_CONTENT://' . $uri, 'FILE_CONTENT')) { return $this->_cache->get('FILE_CONTENT://' . $uri, 'FILE_CONTENT'); } $content = file_get_contents($uri); if ($content === false) { $e = new IOException("Failed to read file: '$uri'"); $e->fileName = $uri; throw $e; } // TODO lazy content detection, should be improved if (strpos(ltrim($content), '{') === 0) { $parsedContent = json_decode($content, true); } else { $parsedContent = Yaml::parse($content); } $this->_cache->set('FILE_CONTENT://' . $uri, 'FILE_CONTENT', $parsedContent); return $parsedContent; } /** * Retrieve the referenced data via JSON pointer. * * This function caches referenced data to make sure references to the same * data structures end up being the same object instance in PHP. * * @param string $uri * @param JsonPointer $pointer * @param array $data * @param string|null $toType * @return SpecObjectInterface|array|null */ public function resolveReferenceData($uri, JsonPointer $pointer, $data, $toType) { $ref = $uri . '#' . $pointer->getPointer(); if ($this->_cache->has($ref, $toType)) { return $this->_cache->get($ref, $toType); } $referencedData = $pointer->evaluate($data); if ($referencedData === null) { return null; } // transitive reference if (isset($referencedData['$ref'])) { return new Reference($referencedData, $toType); } else { /** @var SpecObjectInterface|array $referencedObject */ $referencedObject = $toType !== null ? new $toType($referencedData) : $referencedData; } $this->_cache->set($ref, $toType, $referencedObject); return $referencedObject; } } php-openapi/src/SpecObjectInterface.php 0000644 00000002512 14736103140 0014125 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi; /** * This interface is implemented by all classes that represent objects from the OpenAPI Spec. */ interface SpecObjectInterface { /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON */ public function __construct(array $data); /** * @return mixed returns the serializable data of this object for converting it * to JSON or YAML. */ public function getSerializableData(); /** * Validate object data according to OpenAPI spec. * @return bool whether the loaded data is valid according to OpenAPI spec * @see getErrors() */ public function validate(): bool; /** * @return string[] list of validation errors according to OpenAPI spec. * @see validate() */ public function getErrors(): array; /** * Resolves all Reference Objects in this object and replaces them with their resolution. */ public function resolveReferences(ReferenceContext $context = null); /** * Set context for all Reference Objects in this object. */ public function setReferenceContext(ReferenceContext $context); } php-openapi/src/exceptions/UnknownPropertyException.php 0000644 00000000414 14736103140 0017526 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\exceptions; /** * * */ class UnknownPropertyException extends \Exception { } php-openapi/src/exceptions/TypeErrorException.php 0000644 00000000566 14736103140 0016265 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\exceptions; /** * This exception is thrown if the input data from OpenAPI spec * provides data in another type that is expected. * */ class TypeErrorException extends \Exception { } php-openapi/src/exceptions/IOException.php 0000644 00000000671 14736103140 0014636 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\exceptions; /** * This exception is thrown when reading or writing of a file fails. * @since 1.2.1 */ class IOException extends \Exception { /** * @var string|null if available, the name of the affected file. */ public $fileName; } php-openapi/src/exceptions/UnresolvableReferenceException.php 0000644 00000001121 14736103140 0020576 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\exceptions; use cebe\openapi\json\JsonPointer; /** * This exception is thrown on attempt to resolve a reference which points to a non-existing target. */ class UnresolvableReferenceException extends \Exception { /** * @var JsonPointer|null may contain context information in form of a JSON pointer to the position * of the broken reference in the document. */ public $context; } php-openapi/src/exceptions/ReadonlyPropertyException.php 0000644 00000000501 14736103140 0017641 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\exceptions; /** * @deprecated this class will be removed in version 2.0. */ class ReadonlyPropertyException extends \Exception { } php-openapi/src/Writer.php 0000644 00000004212 14736103140 0011536 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi; use cebe\openapi\exceptions\IOException; use cebe\openapi\spec\OpenApi; use Symfony\Component\Yaml\Yaml; /** * Utility class to simplify writing JSON or YAML OpenAPI specs. * */ class Writer { /** * Convert OpenAPI spec object to JSON data. * @param SpecObjectInterface|OpenApi $object the OpenApi object instance. * @return string JSON string. */ public static function writeToJson(SpecObjectInterface $object): string { return json_encode($object->getSerializableData(), JSON_PRETTY_PRINT); } /** * Convert OpenAPI spec object to YAML data. * @param SpecObjectInterface|OpenApi $object the OpenApi object instance. * @return string YAML string. */ public static function writeToYaml(SpecObjectInterface $object): string { return Yaml::dump($object->getSerializableData(), 256, 2, Yaml::DUMP_OBJECT_AS_MAP | Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); } /** * Write OpenAPI spec object to JSON file. * @param SpecObjectInterface|OpenApi $object the OpenApi object instance. * @param string $fileName file name to write to. * @throws IOException when writing the file fails. */ public static function writeToJsonFile(SpecObjectInterface $object, string $fileName): void { if (file_put_contents($fileName, static::writeToJson($object)) === false) { throw new IOException("Failed to write file: '$fileName'"); } } /** * Write OpenAPI spec object to YAML file. * @param SpecObjectInterface|OpenApi $object the OpenApi object instance. * @param string $fileName file name to write to. * @throws IOException when writing the file fails. */ public static function writeToYamlFile(SpecObjectInterface $object, string $fileName): void { if (file_put_contents($fileName, static::writeToYaml($object)) === false) { throw new IOException("Failed to write file: '$fileName'"); } } } php-openapi/src/json/NonexistentJsonPointerReferenceException.php 0000644 00000001004 14736103140 0021436 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\json; use Exception; /** * NonexistentJsonPointerReferenceException represents the error condition * "A pointer that references a nonexistent value" of the JSON pointer specification. * * @link https://tools.ietf.org/html/rfc6901 (7. Error Handling) */ class NonexistentJsonPointerReferenceException extends Exception { } php-openapi/src/json/JsonReference.php 0000644 00000011121 14736103140 0013760 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\json; use JsonSerializable; /** * Represents a JSON Reference (IETF draft-pbryan-zyp-json-ref-03) * * Includes the URI to another JSON document and the JSON Pointer as * the fragment section of the URI. * * @link https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 * @see JsonPointer */ final class JsonReference implements JsonSerializable { /** * @var string */ private $_uri = ''; /** * @var JsonPointer */ private $_pointer; /** * Create a JSON Reference instance from a JSON document. * @param string $json the JSON object, e.g. `{ "$ref": "http://example.com/example.json#/foo/bar" }`. * @return JsonReference * @throws MalformedJsonReferenceObjectException * @throws InvalidJsonPointerSyntaxException if an invalid JSON pointer string is passed as part of the fragment section. */ public static function createFromJson(string $json): JsonReference { $refObject = json_decode($json, true); if (!isset($refObject['$ref'])) { throw new MalformedJsonReferenceObjectException('JSON Reference Object must contain the "$ref" member.'); } return static::createFromReference($refObject['$ref']); } /** * Create a JSON Reference instance from an URI and a JSON Pointer. * If no JSON Pointer is given this will be interpreted as an empty string JSON pointer, which * references the whole document. * @param string $uri the URI to the document without a fragment part. * @param JsonPointer $jsonPointer * @return JsonReference */ public static function createFromUri(string $uri, ?JsonPointer $jsonPointer = null): JsonReference { $jsonReference = static::createFromReference($uri); $jsonReference->_pointer = $jsonPointer ?: new JsonPointer(''); return $jsonReference; } /** * Create a JSON Reference instance from a reference URI. * @param string $referenceURI the JSON Reference URI, e.g. `"http://example.com/example.json#/foo/bar"`. * @return JsonReference * @throws InvalidJsonPointerSyntaxException if an invalid JSON pointer string is passed as part of the fragment section. */ public static function createFromReference(string $referenceURI): JsonReference { $jsonReference = new JsonReference(); if (strpos($referenceURI, '#') !== false) { list($uri, $fragment) = explode('#', $referenceURI, 2); $jsonReference->_uri = $uri; $jsonReference->_pointer = new JsonPointer(rawurldecode($fragment)); } else { $jsonReference->_uri = $referenceURI; $jsonReference->_pointer = new JsonPointer(''); } return $jsonReference; } private function __construct() { } public function __clone() { $this->_pointer = clone $this->_pointer; } public function getJsonPointer(): JsonPointer { return $this->_pointer; } /** * @return string returns the URI of the referenced JSON document without the fragment (JSON Pointer) part. */ public function getDocumentUri(): string { return $this->_uri; } /** * @return string returns the JSON Pointer in URI format. */ public function getReference(): string { // https://tools.ietf.org/html/rfc6901#section-6 // A JSON Pointer can be represented in a URI fragment identifier by // encoding it into octets using UTF-8 [RFC3629], while percent-encoding // those characters not allowed by the fragment rule in [RFC3986]. // https://tools.ietf.org/html/rfc3986#page-25 // The characters slash ("/") and question mark ("?") are allowed to // represent data within the fragment identifier. // https://tools.ietf.org/html/rfc3986#section-2.4 // the "%7E" can be replaced by "~" without changing its interpretation. return $this->_uri . '#' . strtr(rawurlencode($this->_pointer->getPointer()), ['%2F' => '/', '%3F' => '?', '%7E' => '~']); } /** * Specify data which should be serialized to JSON * @link https://php.net/manual/en/jsonserializable.jsonserialize.php * @return mixed data which can be serialized by <b>json_encode</b>, * which is a value of any type other than a resource. */ public function jsonSerialize() { return (object)['$ref' => $this->getReference()]; } } php-openapi/src/json/JsonPointer.php 0000644 00000012770 14736103140 0013515 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\json; /** * Represents a JSON Pointer (RFC 6901) * * A JSON Pointer only works in the context of a single JSON document, * if you need to reference values in external documents, use [[JsonReference]] instead. * * @link https://tools.ietf.org/html/rfc6901 * @see JsonReference */ final class JsonPointer { /** * @var string */ private $_pointer; /** * JSON Pointer constructor. * @param string $pointer The JSON Pointer. * Must be either an empty string (for referencing the whole document), or a string starting with `/`. * @throws InvalidJsonPointerSyntaxException in case an invalid JSON pointer string is passed */ public function __construct(string $pointer) { if (!preg_match('~^(/[^/]*)*$~', $pointer)) { throw new InvalidJsonPointerSyntaxException("Invalid JSON Pointer syntax: $pointer"); } $this->_pointer = $pointer; } public function __toString() { return $this->_pointer; } /** * @return string returns the JSON Pointer. */ public function getPointer(): string { return $this->_pointer; } /** * @return array the JSON pointer path as array. */ public function getPath(): array { if ($this->_pointer === '') { return []; } $pointer = substr($this->_pointer, 1); return array_map([get_class($this), 'decode'], explode('/', $pointer)); } /** * Append a new part to the JSON path. * @param string $subpath the path element to append. * @return JsonPointer a new JSON pointer pointing to the subpath. */ public function append(string $subpath): JsonPointer { return new JsonPointer($this->_pointer . '/' . static::encode($subpath)); } /** * Returns a JSON pointer to the parent path element of this pointer. * @return JsonPointer|null a new JSON pointer pointing to the parent element * or null if this pointer already points to the document root. */ public function parent(): ?JsonPointer { $path = $this->getPath(); if (empty($path)) { return null; } array_pop($path); if (empty($path)) { return new JsonPointer(''); } return new JsonPointer('/' . implode('/', array_map([get_class($this), 'encode'], $path))); } /** * Evaluate the JSON Pointer on the provided document. * * Note that this does only resolve the JSON Pointer, it will not load external * documents by URI. Loading the Document from the URI is supposed to be done outside of this class. * * @param mixed $jsonDocument * @return mixed * @throws NonexistentJsonPointerReferenceException */ public function evaluate($jsonDocument) { $currentReference = $jsonDocument; $currentPath = ''; foreach ($this->getPath() as $part) { if (is_array($currentReference)) { // if (!preg_match('~^([1-9]*[0-9]|-)$~', $part)) { // throw new NonexistentJsonPointerReferenceException( // "Failed to evaluate pointer '$this->_pointer'. Invalid pointer path '$part' for Array at path '$currentPath'." // ); // } if ($part === '-' || !array_key_exists($part, $currentReference)) { throw new NonexistentJsonPointerReferenceException( "Failed to evaluate pointer '$this->_pointer'. Array has no member $part at path '$currentPath'." ); } $currentReference = $currentReference[$part]; } elseif ($currentReference instanceof \ArrayAccess) { if (!$currentReference->offsetExists($part)) { throw new NonexistentJsonPointerReferenceException( "Failed to evaluate pointer '$this->_pointer'. Array has no member $part at path '$currentPath'." ); } $currentReference = $currentReference[$part]; } elseif (is_object($currentReference)) { if (!isset($currentReference->$part) && !property_exists($currentReference, $part)) { throw new NonexistentJsonPointerReferenceException( "Failed to evaluate pointer '$this->_pointer'. Object has no member $part at path '$currentPath'." ); } $currentReference = $currentReference->$part; } else { throw new NonexistentJsonPointerReferenceException( "Failed to evaluate pointer '$this->_pointer'. Value at path '$currentPath' is neither an array nor an object." ); } $currentPath = "$currentPath/$part"; } return $currentReference; } /** * Encodes a string for use inside of a JSON pointer. */ public static function encode(string $string): string { return strtr($string, [ '~' => '~0', '/' => '~1', ]); } /** * Decodes a string used inside of a JSON pointer. */ public static function decode(string $string): string { return strtr($string, [ '~1' => '/', '~0' => '~', ]); } } php-openapi/src/json/MalformedJsonReferenceObjectException.php 0000644 00000000737 14736103140 0020630 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\json; use Exception; /** * MalformedJsonReferenceObjectException is thrown if a JSON Reference Object does not contain the "$ref" member. * * @link https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 (3. Syntax) */ class MalformedJsonReferenceObjectException extends Exception { } php-openapi/src/json/InvalidJsonPointerSyntaxException.php 0000644 00000000734 14736103140 0020107 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\json; use Exception; /** * InvalidJsonPointerSyntaxException represents the error condition "Invalid pointer syntax" of the JSON pointer specification. * * @link https://tools.ietf.org/html/rfc6901 (7. Error Handling) */ class InvalidJsonPointerSyntaxException extends Exception { } php-openapi/src/spec/Responses.php 0000644 00000022713 14736103140 0013203 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use ArrayAccess; use ArrayIterator; use cebe\openapi\DocumentContextInterface; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\JsonPointer; use cebe\openapi\ReferenceContext; use cebe\openapi\SpecObjectInterface; use Countable; use IteratorAggregate; use Traversable; /** * A container for the expected responses of an operation. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#responsesObject */ class Responses implements SpecObjectInterface, DocumentContextInterface, ArrayAccess, Countable, IteratorAggregate { /** * @var (Response|Reference|null)[] */ private $_responses = []; private $_errors = []; private $_baseDocument; private $_jsonPointer; /** * Create an object from spec data. * @param Response[]|Reference[]|array[] $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { foreach ($data as $statusCode => $response) { // From Spec: This field MUST be enclosed in quotation marks (for example, "200") for compatibility between JSON and YAML. $statusCode = (string) $statusCode; if (preg_match('~^(?:default|[1-5](?:[0-9][0-9]|XX))$~', $statusCode)) { if ($response instanceof Response || $response instanceof Reference) { $this->_responses[$statusCode] = $response; } elseif (is_array($response) && isset($response['$ref'])) { $this->_responses[$statusCode] = new Reference($response, Response::class); } elseif (is_array($response)) { $this->_responses[$statusCode] = new Response($response); } else { $givenType = gettype($response); if ($givenType === 'object') { $givenType = get_class($response); } throw new TypeErrorException(sprintf('Response MUST be either an array, a Response or a Reference object, "%s" given', $givenType)); } } else { $this->_errors[] = "Responses: $statusCode is not a valid HTTP status code."; } } } /** * @return mixed returns the serializable data of this object for converting it * to JSON or YAML. */ public function getSerializableData() { $data = []; foreach ($this->_responses as $statusCode => $response) { $data[$statusCode] = ($response === null) ? null : $response->getSerializableData(); } return (object) $data; } /** * @param string $statusCode HTTP status code * @return bool */ public function hasResponse($statusCode): bool { return isset($this->_responses[$statusCode]); } /** * @param string $statusCode HTTP status code * @return Response|Reference|null */ public function getResponse($statusCode) { return $this->_responses[$statusCode] ?? null; } /** * @param string $statusCode HTTP status code * @param Response|Reference $response */ public function addResponse($statusCode, $response): void { $this->_responses[$statusCode] = $response; } /** * @param string $statusCode HTTP status code */ public function removeResponse($statusCode) { unset($this->_responses[$statusCode]); } /** * @return (Response|Reference|null)[] */ public function getResponses(): array { return $this->_responses; } /** * Validate object data according to OpenAPI spec. * @return bool whether the loaded data is valid according to OpenAPI spec * @see getErrors() */ public function validate(): bool { $valid = true; foreach ($this->_responses as $key => $response) { if ($response === null) { continue; } if (!$response->validate()) { $valid = false; } } return $valid && empty($this->_errors); } /** * @return string[] list of validation errors according to OpenAPI spec. * @see validate() */ public function getErrors(): array { if (($pos = $this->getDocumentPosition()) !== null) { $errors = [ array_map(function ($e) use ($pos) { return "[{$pos}] $e"; }, $this->_errors) ]; } else { $errors = [$this->_errors]; } foreach ($this->_responses as $response) { if ($response === null) { continue; } $errors[] = $response->getErrors(); } return array_merge(...$errors); } /** * Whether a offset exists * @link http://php.net/manual/en/arrayaccess.offsetexists.php * @param mixed $offset An offset to check for. * @return boolean true on success or false on failure. * The return value will be casted to boolean if non-boolean was returned. */ public function offsetExists($offset) { return $this->hasResponse($offset); } /** * Offset to retrieve * @link http://php.net/manual/en/arrayaccess.offsetget.php * @param mixed $offset The offset to retrieve. * @return mixed Can return all value types. */ public function offsetGet($offset) { return $this->getResponse($offset); } /** * Offset to set * @link http://php.net/manual/en/arrayaccess.offsetset.php * @param mixed $offset The offset to assign the value to. * @param mixed $value The value to set. */ public function offsetSet($offset, $value) { $this->addResponse($offset, $value); } /** * Offset to unset * @link http://php.net/manual/en/arrayaccess.offsetunset.php * @param mixed $offset The offset to unset. */ public function offsetUnset($offset) { $this->removeResponse($offset); } /** * Count elements of an object * @link http://php.net/manual/en/countable.count.php * @return int The custom count as an integer. * The return value is cast to an integer. */ public function count() { return count($this->_responses); } /** * Retrieve an external iterator * @link http://php.net/manual/en/iteratoraggregate.getiterator.php * @return Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b> */ public function getIterator() { return new ArrayIterator($this->_responses); } /** * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ public function resolveReferences(ReferenceContext $context = null) { foreach ($this->_responses as $key => $response) { if ($response instanceof Reference) { /** @var Response|Reference|null $referencedObject */ $referencedObject = $response->resolve($context); $this->_responses[$key] = $referencedObject; if (!$referencedObject instanceof Reference && $referencedObject !== null) { $referencedObject->resolveReferences(); } } else { $response->resolveReferences($context); } } } /** * Set context for all Reference Objects in this object. */ public function setReferenceContext(ReferenceContext $context) { foreach ($this->_responses as $key => $response) { if ($response instanceof Reference) { $response->setContext($context); } else { $response->setReferenceContext($context); } } } /** * Provide context information to the object. * * Context information contains a reference to the base object where it is contained in * as well as a JSON pointer to its position. * @param SpecObjectInterface $baseDocument * @param JsonPointer $jsonPointer */ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointer $jsonPointer) { $this->_baseDocument = $baseDocument; $this->_jsonPointer = $jsonPointer; foreach ($this->_responses as $key => $response) { if ($response instanceof DocumentContextInterface) { $response->setDocumentContext($baseDocument, $jsonPointer->append($key)); } } } /** * @return SpecObjectInterface|null returns the base document where this object is located in. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getBaseDocument(): ?SpecObjectInterface { return $this->_baseDocument; } /** * @return JsonPointer|null returns a JSON pointer describing the position of this object in the base document. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getDocumentPosition(): ?JsonPointer { return $this->_jsonPointer; } } php-openapi/src/spec/Tag.php 0000644 00000002056 14736103140 0011733 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Adds metadata to a single tag that is used by the Operation Object. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#tagObject * * @property string $name * @property string $description * @property ExternalDocumentation|null $externalDocs * */ class Tag extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'name' => Type::STRING, 'description' => Type::STRING, 'externalDocs' => ExternalDocumentation::class, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['name']); } } php-openapi/src/spec/Paths.php 0000644 00000021156 14736103140 0012301 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use ArrayAccess; use ArrayIterator; use cebe\openapi\DocumentContextInterface; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\JsonPointer; use cebe\openapi\ReferenceContext; use cebe\openapi\SpecObjectInterface; use Countable; use IteratorAggregate; use Traversable; /** * Holds the relative paths to the individual endpoints and their operations. * * The path is appended to the URL from the Server Object in order to construct the full URL. * The Paths MAY be empty, due to ACL constraints. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#pathsObject * */ class Paths implements SpecObjectInterface, DocumentContextInterface, ArrayAccess, Countable, IteratorAggregate { /** * @var (PathItem|null)[] */ private $_paths = []; /** * @var array */ private $_errors = []; /** * @var SpecObjectInterface|null */ private $_baseDocument; /** * @var JsonPointer|null */ private $_jsonPointer; /** * Create an object from spec data. * @param (PathItem|array|null)[] $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { foreach ($data as $path => $object) { if ($object === null) { $this->_paths[$path] = null; } elseif (is_array($object)) { $this->_paths[$path] = new PathItem($object); } elseif ($object instanceof PathItem) { $this->_paths[$path] = $object; } else { $givenType = gettype($object); if ($givenType === 'object') { $givenType = get_class($object); } throw new TypeErrorException(sprintf('Path MUST be either array or PathItem object, "%s" given', $givenType)); } } } /** * @return mixed returns the serializable data of this object for converting it * to JSON or YAML. */ public function getSerializableData() { $data = []; foreach ($this->_paths as $path => $pathItem) { $data[$path] = ($pathItem === null) ? null : $pathItem->getSerializableData(); } return (object) $data; } /** * @param string $name path name * @return bool */ public function hasPath(string $name): bool { return isset($this->_paths[$name]); } /** * @param string $name path name * @return PathItem */ public function getPath(string $name): ?PathItem { return $this->_paths[$name] ?? null; } /** * @param string $name path name * @param PathItem $pathItem the path item to add */ public function addPath(string $name, PathItem $pathItem): void { $this->_paths[$name] = $pathItem; } /** * @param string $name path name */ public function removePath(string $name): void { unset($this->_paths[$name]); } /** * @return PathItem[] */ public function getPaths(): array { return $this->_paths; } /** * Validate object data according to OpenAPI spec. * @return bool whether the loaded data is valid according to OpenAPI spec * @see getErrors() */ public function validate(): bool { $valid = true; $this->_errors = []; foreach ($this->_paths as $key => $path) { if ($path === null) { continue; } if (!$path->validate()) { $valid = false; } if (strpos($key, '/') !== 0) { $this->_errors[] = "Path must begin with /: $key"; } } return $valid && empty($this->_errors); } /** * @return string[] list of validation errors according to OpenAPI spec. * @see validate() */ public function getErrors(): array { if (($pos = $this->getDocumentPosition()) !== null) { $errors = [ array_map(function ($e) use ($pos) { return "[{$pos}] $e"; }, $this->_errors) ]; } else { $errors = [$this->_errors]; } foreach ($this->_paths as $path) { if ($path === null) { continue; } $errors[] = $path->getErrors(); } return array_merge(...$errors); } /** * Whether a offset exists * @link http://php.net/manual/en/arrayaccess.offsetexists.php * @param mixed $offset An offset to check for. * @return boolean true on success or false on failure. * The return value will be casted to boolean if non-boolean was returned. */ public function offsetExists($offset) { return $this->hasPath($offset); } /** * Offset to retrieve * @link http://php.net/manual/en/arrayaccess.offsetget.php * @param mixed $offset The offset to retrieve. * @return PathItem Can return all value types. */ public function offsetGet($offset) { return $this->getPath($offset); } /** * Offset to set * @link http://php.net/manual/en/arrayaccess.offsetset.php * @param mixed $offset The offset to assign the value to. * @param mixed $value The value to set. */ public function offsetSet($offset, $value) { $this->addPath($offset, $value); } /** * Offset to unset * @link http://php.net/manual/en/arrayaccess.offsetunset.php * @param mixed $offset The offset to unset. */ public function offsetUnset($offset) { $this->removePath($offset); } /** * Count elements of an object * @link http://php.net/manual/en/countable.count.php * @return int The custom count as an integer. * The return value is cast to an integer. */ public function count() { return count($this->_paths); } /** * Retrieve an external iterator * @link http://php.net/manual/en/iteratoraggregate.getiterator.php * @return Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b> */ public function getIterator() { return new ArrayIterator($this->_paths); } /** * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ public function resolveReferences(ReferenceContext $context = null) { foreach ($this->_paths as $key => $path) { if ($path === null) { continue; } $path->resolveReferences($context); } } /** * Set context for all Reference Objects in this object. */ public function setReferenceContext(ReferenceContext $context) { foreach ($this->_paths as $key => $path) { if ($path === null) { continue; } $path->setReferenceContext($context); } } /** * Provide context information to the object. * * Context information contains a reference to the base object where it is contained in * as well as a JSON pointer to its position. * @param SpecObjectInterface $baseDocument * @param JsonPointer $jsonPointer */ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointer $jsonPointer) { $this->_baseDocument = $baseDocument; $this->_jsonPointer = $jsonPointer; foreach ($this->_paths as $key => $path) { if ($path instanceof DocumentContextInterface) { $path->setDocumentContext($baseDocument, $jsonPointer->append($key)); } } } /** * @return SpecObjectInterface|null returns the base document where this object is located in. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getBaseDocument(): ?SpecObjectInterface { return $this->_baseDocument; } /** * @return JsonPointer|null returns a JSON pointer describing the position of this object in the base document. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getDocumentPosition(): ?JsonPointer { return $this->_jsonPointer; } } php-openapi/src/spec/Operation.php 0000644 00000003772 14736103140 0013166 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Describes a single API operation on a path. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#operationObject * * @property string[] $tags * @property string $summary * @property string $description * @property ExternalDocumentation|null $externalDocs * @property string $operationId * @property Parameter[]|Reference[] $parameters * @property RequestBody|Reference|null $requestBody * @property Responses|Response[]|null $responses * @property Callback[]|Reference[] $callbacks * @property bool $deprecated * @property SecurityRequirement[] $security * @property Server[] $servers */ class Operation extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'tags' => [Type::STRING], 'summary' => Type::STRING, 'description' => Type::STRING, 'externalDocs' => ExternalDocumentation::class, 'operationId' => Type::STRING, 'parameters' => [Parameter::class], 'requestBody' => RequestBody::class, 'responses' => Responses::class, 'callbacks' => [Type::STRING, Callback::class], 'deprecated' => Type::BOOLEAN, 'security' => [SecurityRequirement::class], 'servers' => [Server::class], ]; } protected function attributeDefaults(): array { return [ 'security' => null, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. * * Call `addError()` in case of validation errors. */ protected function performValidation() { $this->requireProperties(['responses']); } } php-openapi/src/spec/ServerVariable.php 0000644 00000002050 14736103140 0014126 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * An object representing a Server Variable for server URL template substitution. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#serverVariableObject * * @property string[] $enum * @property string $default * @property string $description * */ class ServerVariable extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'enum' => [Type::STRING], 'default' => Type::STRING, 'description' => Type::STRING, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['default']); } } php-openapi/src/spec/Parameter.php 0000644 00000010571 14736103140 0013141 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\SpecBaseObject; /** * Describes a single operation parameter. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#parameterObject * * @property string $name * @property string $in * @property string $description * @property bool $required * @property bool $deprecated * @property bool $allowEmptyValue * * @property string $style * @property boolean $explode * @property boolean $allowReserved * @property Schema|Reference|null $schema * @property mixed $example * @property Example[] $examples * * @property MediaType[] $content */ class Parameter extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'name' => Type::STRING, 'in' => Type::STRING, 'description' => Type::STRING, 'required' => Type::BOOLEAN, 'deprecated' => Type::BOOLEAN, 'allowEmptyValue' => Type::BOOLEAN, 'style' => Type::STRING, 'explode' => Type::BOOLEAN, 'allowReserved' => Type::BOOLEAN, 'schema' => Schema::class, 'example' => Type::ANY, 'examples' => [Type::STRING, Example::class], 'content' => [Type::STRING, MediaType::class], ]; } private $_attributeDefaults = []; /** * @return array array of attributes default values. */ protected function attributeDefaults(): array { return $this->_attributeDefaults; } /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { if (isset($data['in'])) { // Spec: Default values (based on value of in): // for query - form; // for path - simple; // for header - simple; // for cookie - form. switch ($data['in']) { case 'query': case 'cookie': $this->_attributeDefaults['style'] = 'form'; $this->_attributeDefaults['explode'] = true; break; case 'path': case 'header': $this->_attributeDefaults['style'] = 'simple'; $this->_attributeDefaults['explode'] = false; break; } } if (isset($data['style'])) { // Spec: When style is form, the default value is true. For all other styles, the default value is false. $this->_attributeDefaults['explode'] = ($data['style'] === 'form'); } parent::__construct($data); } /** * Perform validation on this object, check data against OpenAPI Specification rules. * * Call `addError()` in case of validation errors. */ protected function performValidation() { $this->requireProperties(['name', 'in']); if ($this->in === 'path') { $this->requireProperties(['required']); if (!$this->required) { $this->addError("Parameter 'required' must be true for 'in': 'path'."); } } if (!empty($this->content) && !empty($this->schema)) { $this->addError('A Parameter Object MUST contain either a schema property, or a content property, but not both.'); } if (!empty($this->content) && count($this->content) !== 1) { $this->addError('A Parameter Object with Content property MUST have A SINGLE content type.'); } $supportedSerializationStyles = [ 'path' => ['simple', 'label', 'matrix'], 'query' => ['form', 'spaceDelimited', 'pipeDelimited', 'deepObject'], 'header' => ['simple'], 'cookie' => ['form'], ]; if (isset($supportedSerializationStyles[$this->in]) && !in_array($this->style, $supportedSerializationStyles[$this->in])) { $this->addError('A Parameter Object DOES NOT support this serialization style.'); } } } php-openapi/src/spec/Server.php 0000644 00000002007 14736103140 0012462 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * An object representing a Server. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#serverObject * * @property string $url * @property string $description * @property ServerVariable[] $variables * */ class Server extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'url' => Type::STRING, 'description' => Type::STRING, 'variables' => [Type::STRING, ServerVariable::class], ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['url']); } } php-openapi/src/spec/License.php 0000644 00000001674 14736103140 0012607 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * License information for the exposed API. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#licenseObject * * @property string $name * @property string $url * */ class License extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'name' => Type::STRING, 'url' => Type::STRING, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['name']); $this->validateUrl('url'); } } php-openapi/src/spec/Schema.php 0000644 00000014030 14736103140 0012413 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\SpecBaseObject; /** * The Schema Object allows the definition of input and output data types. * * These types can be objects, but also primitives and arrays. This object is an extended subset of the * [JSON Schema Specification Wright Draft 00](http://json-schema.org/). * * For more information about the properties, see * [JSON Schema Core](https://tools.ietf.org/html/draft-wright-json-schema-00) and * [JSON Schema Validation](https://tools.ietf.org/html/draft-wright-json-schema-validation-00). * Unless stated otherwise, the property definitions follow the JSON Schema. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#schemaObject * * @property string $title * @property int|float $multipleOf * @property int|float $maximum * @property bool $exclusiveMaximum * @property int|float $minimum * @property bool $exclusiveMinimum * @property int $maxLength * @property int $minLength * @property string $pattern (This string SHOULD be a valid regular expression, according to the [ECMA 262 regular expression dialect](https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5)) * @property int $maxItems * @property int $minItems * @property bool $uniqueItems * @property int $maxProperties * @property int $minProperties * @property string[] $required list of required properties * @property array $enum * * @property string $type * @property Schema[]|Reference[] $allOf * @property Schema[]|Reference[] $oneOf * @property Schema[]|Reference[] $anyOf * @property Schema|Reference|null $not * @property Schema|Reference|null $items * @property Schema[]|Reference[] $properties * @property Schema|Reference|bool $additionalProperties * @property string $description * @property string $format * @property mixed $default * * @property bool $nullable * @property Discriminator|null $discriminator * @property bool $readOnly * @property bool $writeOnly * @property Xml|null $xml * @property ExternalDocumentation|null $externalDocs * @property mixed $example * @property bool $deprecated * */ class Schema extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ // The following properties are taken directly from the JSON Schema definition and follow the same specifications: // types from https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-4 ff. 'title' => Type::STRING, 'multipleOf' => Type::NUMBER, 'maximum' => Type::NUMBER, 'exclusiveMaximum' => Type::BOOLEAN, 'minimum' => Type::NUMBER, 'exclusiveMinimum' => Type::BOOLEAN, 'maxLength' => Type::INTEGER, 'minLength' => Type::INTEGER, 'pattern' => Type::STRING, 'maxItems' => Type::INTEGER, 'minItems' => Type::INTEGER, 'uniqueItems' => Type::BOOLEAN, 'maxProperties' => Type::INTEGER, 'minProperties' => Type::INTEGER, 'required' => [Type::STRING], 'enum' => [Type::ANY], // The following properties are taken from the JSON Schema definition but their definitions were adjusted to the OpenAPI Specification. 'type' => Type::STRING, 'allOf' => [Schema::class], 'oneOf' => [Schema::class], 'anyOf' => [Schema::class], 'not' => Schema::class, 'items' => Schema::class, 'properties' => [Type::STRING, Schema::class], //'additionalProperties' => 'boolean' | ['string', Schema::class], handled in constructor 'description' => Type::STRING, 'format' => Type::STRING, 'default' => Type::ANY, // Other than the JSON Schema subset fields, the following fields MAY be used for further schema documentation: 'nullable' => Type::BOOLEAN, 'discriminator' => Discriminator::class, 'readOnly' => Type::BOOLEAN, 'writeOnly' => Type::BOOLEAN, 'xml' => Xml::class, 'externalDocs' => ExternalDocumentation::class, 'example' => Type::ANY, 'deprecated' => Type::BOOLEAN, ]; } /** * @return array array of attributes default values. */ protected function attributeDefaults(): array { return [ 'additionalProperties' => true, 'required' => null, 'enum' => null, 'allOf' => null, 'oneOf' => null, 'anyOf' => null, ]; } /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { if (isset($data['additionalProperties'])) { if (is_array($data['additionalProperties'])) { $data['additionalProperties'] = $this->instantiate(Schema::class, $data['additionalProperties']); } elseif (!($data['additionalProperties'] instanceof Schema || $data['additionalProperties'] instanceof Reference || is_bool($data['additionalProperties']))) { $givenType = gettype($data['additionalProperties']); if ($givenType === 'object') { $givenType = get_class($data['additionalProperties']); } throw new TypeErrorException(sprintf('Schema::$additionalProperties MUST be either boolean or a Schema/Reference object, "%s" given', $givenType)); } } parent::__construct($data); } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { } } php-openapi/src/spec/SecurityRequirement.php 0000644 00000001627 14736103140 0015253 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Lists the required security schemes to execute this operation. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#securityRequirementObject * */ class SecurityRequirement extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { // this object does not have a fixed set of attribute names return []; } /** * Perform validation on this object, check data against OpenAPI Specification rules. * * Call `addError()` in case of validation errors. */ protected function performValidation() { } } php-openapi/src/spec/Reference.php 0000644 00000035245 14736103140 0013124 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\DocumentContextInterface; use cebe\openapi\exceptions\IOException; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\InvalidJsonPointerSyntaxException; use cebe\openapi\json\JsonPointer; use cebe\openapi\json\JsonReference; use cebe\openapi\json\NonexistentJsonPointerReferenceException; use cebe\openapi\ReferenceContext; use cebe\openapi\SpecObjectInterface; use Symfony\Component\Yaml\Yaml; /** * Reference Object * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#referenceObject * @link https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 * @link https://tools.ietf.org/html/rfc6901 * */ class Reference implements SpecObjectInterface, DocumentContextInterface { /** * @var string */ private $_to; /** * @var string */ private $_ref; /** * @var JsonReference|null */ private $_jsonReference; /** * @var ReferenceContext */ private $_context; /** * @var SpecObjectInterface|null */ private $_baseDocument; /** * @var JsonPointer|null */ private $_jsonPointer; /** * @var array */ private $_errors = []; /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @param string $to class name of the type referenced by this Reference * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data, string $to = null) { if (!isset($data['$ref'])) { throw new TypeErrorException( "Unable to instantiate Reference Object with data '" . print_r($data, true) . "'." ); } if ($to !== null && !is_subclass_of($to, SpecObjectInterface::class, true)) { throw new TypeErrorException( "Unable to instantiate Reference Object, Referenced Class type must implement SpecObjectInterface." ); } if (!is_string($data['$ref'])) { throw new TypeErrorException( 'Unable to instantiate Reference Object, value of $ref must be a string.' ); } $this->_to = $to; $this->_ref = $data['$ref']; try { $this->_jsonReference = JsonReference::createFromReference($this->_ref); } catch (InvalidJsonPointerSyntaxException $e) { $this->_errors[] = 'Reference: value of $ref is not a valid JSON pointer: ' . $e->getMessage(); } if (count($data) !== 1) { $this->_errors[] = 'Reference: additional properties are given. Only $ref should be set in a Reference Object.'; } } /** * @return mixed returns the serializable data of this object for converting it * to JSON or YAML. */ public function getSerializableData() { return (object) ['$ref' => $this->_ref]; } /** * Validate object data according to OpenAPI spec. * @return bool whether the loaded data is valid according to OpenAPI spec * @see getErrors() */ public function validate(): bool { return empty($this->_errors); } /** * @return string[] list of validation errors according to OpenAPI spec. * @see validate() */ public function getErrors(): array { if (($pos = $this->getDocumentPosition()) !== null) { return array_map(function ($e) use ($pos) { return "[{$pos}] $e"; }, $this->_errors); } else { return $this->_errors; } } /** * @return string the reference string. */ public function getReference() { return $this->_ref; } /** * @return JsonReference the JSON Reference. */ public function getJsonReference(): JsonReference { return $this->_jsonReference; } /** * @param ReferenceContext $context */ public function setContext(ReferenceContext $context) { $this->_context = $context; } /** * @return ReferenceContext */ public function getContext() : ?ReferenceContext { return $this->_context; } /** * Resolve this reference. * @param ReferenceContext $context the reference context to use for resolution. * If not specified, `getContext()` will be called to determine the context, if * that does not return a context, the UnresolvableReferenceException will be thrown. * @return SpecObjectInterface|array|null the resolved spec type. * You might want to call resolveReferences() on the resolved object to recursively resolve recursive references. * This is not done automatically to avoid recursion to run into the same function again. * If you call resolveReferences() make sure to replace the Reference with the resolved object first. * @throws UnresolvableReferenceException in case of errors. */ public function resolve(ReferenceContext $context = null) { if ($context === null) { $context = $this->getContext(); if ($context === null) { throw new UnresolvableReferenceException('No context given for resolving reference.'); } } $jsonReference = $this->_jsonReference; if ($jsonReference === null) { if ($context->throwException) { throw new UnresolvableReferenceException(implode("\n", $this->getErrors())); } return $this; } try { if ($jsonReference->getDocumentUri() === '') { if ($context->mode === ReferenceContext::RESOLVE_MODE_INLINE) { return $this; } // resolve in current document $baseSpec = $context->getBaseSpec(); if ($baseSpec !== null) { // TODO type error if resolved object does not match $this->_to ? /** @var SpecObjectInterface $referencedObject */ $referencedObject = $jsonReference->getJsonPointer()->evaluate($baseSpec); // transitive reference if ($referencedObject instanceof Reference) { $referencedObject = $this->resolveTransitiveReference($referencedObject, $context); } if ($referencedObject instanceof SpecObjectInterface) { $referencedObject->setReferenceContext($context); } return $referencedObject; } else { // if current document was loaded via reference, it may be null, // so we load current document by URI instead. $jsonReference = JsonReference::createFromUri($context->getUri(), $jsonReference->getJsonPointer()); } } // resolve in external document $file = $context->resolveRelativeUri($jsonReference->getDocumentUri()); try { $referencedDocument = $context->fetchReferencedFile($file); } catch (\Throwable $e) { $exception = new UnresolvableReferenceException( "Failed to resolve Reference '$this->_ref' to $this->_to Object: " . $e->getMessage(), $e->getCode(), $e ); $exception->context = $this->getDocumentPosition(); throw $exception; } $referencedDocument = $this->adjustRelativeReferences($referencedDocument, $file, null, $context); $referencedObject = $context->resolveReferenceData($file, $jsonReference->getJsonPointer(), $referencedDocument, $this->_to); if ($referencedObject instanceof DocumentContextInterface) { if ($referencedObject->getDocumentPosition() === null && $this->getDocumentPosition() !== null) { $referencedObject->setDocumentContext($context->getBaseSpec(), $this->getDocumentPosition()); } } // transitive reference if ($referencedObject instanceof Reference) { if ($context->mode === ReferenceContext::RESOLVE_MODE_INLINE && strncmp($referencedObject->getReference(), '#', 1) === 0) { $referencedObject->setContext($context); } else { return $this->resolveTransitiveReference($referencedObject, $context); } } else { if ($referencedObject instanceof SpecObjectInterface) { $referencedObject->setReferenceContext($context); } } return $referencedObject; } catch (NonexistentJsonPointerReferenceException $e) { $message = "Failed to resolve Reference '$this->_ref' to $this->_to Object: " . $e->getMessage(); if ($context->throwException) { $exception = new UnresolvableReferenceException($message, 0, $e); $exception->context = $this->getDocumentPosition(); throw $exception; } $this->_errors[] = $message; $this->_jsonReference = null; return $this; } catch (UnresolvableReferenceException $e) { $e->context = $this->getDocumentPosition(); if ($context->throwException) { throw $e; } $this->_errors[] = $e->getMessage(); $this->_jsonReference = null; return $this; } } private function resolveTransitiveReference(Reference $referencedObject, ReferenceContext $context) { if ($referencedObject->_to === null) { $referencedObject->_to = $this->_to; } $referencedObject->setContext($context); if ($referencedObject === $this) { // catch recursion throw new UnresolvableReferenceException('Cyclic reference detected on a Reference Object.'); } $transitiveRefResult = $referencedObject->resolve(); if ($transitiveRefResult === $this) { // catch recursion throw new UnresolvableReferenceException('Cyclic reference detected on a Reference Object.'); } return $transitiveRefResult; } /** * Adjust relative references inside of the file to match the context of the base file */ private function adjustRelativeReferences($referencedDocument, $basePath, $baseDocument = null, $oContext = null) { $context = new ReferenceContext(null, $basePath); if ($baseDocument === null) { $baseDocument = $referencedDocument; } foreach ($referencedDocument as $key => $value) { // adjust reference URLs if ($key === '$ref' && is_string($value)) { if (isset($value[0]) && $value[0] === '#') { // direcly inline references in the same document, // these are not going to be valid in the new context anymore $inlineDocument = (new JsonPointer(substr($value, 1)))->evaluate($baseDocument); return $this->adjustRelativeReferences($inlineDocument, $basePath, $baseDocument, $oContext); } $referencedDocument[$key] = $context->resolveRelativeUri($value); $parts = explode('#', $referencedDocument[$key], 2); if ($parts[0] === $oContext->getUri()) { $referencedDocument[$key] = '#' . ($parts[1] ?? ''); } else { $referencedDocument[$key] = $this->makeRelativePath($oContext->getUri(), $referencedDocument[$key]); } continue; } // adjust URLs for 'externalValue' references in Example Objects // https://spec.openapis.org/oas/v3.0.3#example-object if ($key === 'externalValue' && is_string($value)) { $referencedDocument[$key] = $this->makeRelativePath($oContext->getUri(), $context->resolveRelativeUri($value)); continue; } if (is_array($value)) { $referencedDocument[$key] = $this->adjustRelativeReferences($value, $basePath, $baseDocument, $oContext); } } return $referencedDocument; } /** * If $path can be expressed relative to $base, make it a relative path, otherwise $path is returned. * @param string $base * @param string $path */ private function makeRelativePath($base, $path) { if (strpos($path, dirname($base)) === 0) { return './' . substr($path, strlen(dirname($base) . '/')); } return $path; } /** * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ public function resolveReferences(ReferenceContext $context = null) { throw new UnresolvableReferenceException('Cyclic reference detected, resolveReferences() called on a Reference Object.'); } /** * Set context for all Reference Objects in this object. * @throws UnresolvableReferenceException */ public function setReferenceContext(ReferenceContext $context) { throw new UnresolvableReferenceException('Cyclic reference detected, setReferenceContext() called on a Reference Object.'); } /** * Provide context information to the object. * * Context information contains a reference to the base object where it is contained in * as well as a JSON pointer to its position. * @param SpecObjectInterface $baseDocument * @param JsonPointer $jsonPointer */ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointer $jsonPointer) { $this->_baseDocument = $baseDocument; $this->_jsonPointer = $jsonPointer; } /** * @return SpecObjectInterface|null returns the base document where this object is located in. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getBaseDocument(): ?SpecObjectInterface { return $this->_baseDocument; } /** * @return JsonPointer|null returns a JSON pointer describing the position of this object in the base document. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getDocumentPosition(): ?JsonPointer { return $this->_jsonPointer; } } php-openapi/src/spec/Header.php 0000644 00000002305 14736103140 0012405 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * The Header Object follows the structure of the Parameter Object with the following changes: * * 1. name MUST NOT be specified, it is given in the corresponding headers map. * 2. in MUST NOT be specified, it is implicitly in header. * 3. All traits that are affected by the location MUST be applicable to a location of header (for example, style). * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#headerObject * */ class Header extends Parameter { public function performValidation() { if (!empty($this->name)) { $this->addError("'name' must not be specified in Header Object."); } if (!empty($this->in)) { $this->addError("'in' must not be specified in Header Object."); } if (!empty($this->content) && !empty($this->schema)) { $this->addError("A Header Object MUST contain either a schema property, or a content property, but not both. "); } } } php-openapi/src/spec/Example.php 0000644 00000001762 14736103140 0012616 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Example Object * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#exampleObject * * @property string $summary * @property string $description * @property mixed $value * @property string $externalValue */ class Example extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'summary' => Type::STRING, 'description' => Type::STRING, 'value' => Type::ANY, 'externalValue' => Type::STRING, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { } } php-openapi/src/spec/SecurityScheme.php 0000644 00000005070 14736103140 0014153 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Defines a security scheme that can be used by the operations. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#securitySchemeObject * * @property string $type * @property string $description * @property string $name * @property string $in * @property string $scheme * @property string $bearerFormat * @property OAuthFlows|null $flows * @property string $openIdConnectUrl */ class SecurityScheme extends SpecBaseObject { private $knownTypes = [ "apiKey", "http", "oauth2", "openIdConnect" ]; /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'type' => Type::STRING, 'description' => Type::STRING, 'name' => Type::STRING, 'in' => Type::STRING, 'scheme' => Type::STRING, 'bearerFormat' => Type::STRING, 'flows' => OAuthFlows::class, 'openIdConnectUrl' => Type::STRING, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['type']); if (isset($this->type)) { if (!in_array($this->type, $this->knownTypes)) { $this->addError("Unknown Security Scheme type: $this->type"); } else { switch ($this->type) { case "apiKey": $this->requireProperties(['name', 'in']); if (isset($this->in)) { if (!in_array($this->in, ["query", "header", "cookie"])) { $this->addError("Invalid value for Security Scheme property 'in': $this->in"); } } break; case "http": $this->requireProperties(['scheme']); break; case "oauth2": $this->requireProperties(['flows']); break; case "openIdConnect": $this->requireProperties(['openIdConnectUrl']); break; } } } } } php-openapi/src/spec/RequestBody.php 0000644 00000002017 14736103140 0013463 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Describes a single request body. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#requestBodyObject * * @property string $description * @property MediaType[] $content * @property boolean $required */ class RequestBody extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'description' => Type::STRING, 'content' => [Type::STRING, MediaType::class], 'required' => Type::BOOLEAN, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['content']); } } php-openapi/src/spec/Discriminator.php 0000644 00000002141 14736103140 0014022 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be used to aid in serialization, deserialization, and validation. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#discriminatorObject * * @property string $propertyName * @property string[] $mapping * */ class Discriminator extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'propertyName' => Type::STRING, 'mapping' => [Type::STRING, Type::STRING], ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['propertyName']); } } php-openapi/src/spec/Encoding.php 0000644 00000006202 14736103140 0012743 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\SpecBaseObject; /** * A single encoding definition applied to a single schema property. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#encodingObject * * @property string $contentType * @property Header[]|Reference[] $headers * @property string $style * @property boolean $explode * @property boolean $allowReserved */ class Encoding extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'contentType' => Type::STRING, 'headers' => [Type::STRING, Header::class], // TODO implement default values for style // https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#encodingObject 'style' => Type::STRING, 'explode' => Type::BOOLEAN, 'allowReserved' => Type::BOOLEAN, ]; } private $_attributeDefaults = []; /** * @return array array of attributes default values. */ protected function attributeDefaults(): array { return $this->_attributeDefaults; } /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data, ?Schema $schema = null) { if (isset($data['style'])) { // Spec: When style is form, the default value is true. $this->_attributeDefaults['explode'] = ($data['style'] === 'form'); } if ($schema !== null) { // Spec: Default value depends on the property type: // for string with format being binary – application/octet-stream; // for other primitive types – text/plain; // for object - application/json; // for array – the default is defined based on the inner type. switch ($schema->type === 'array' ? ($schema->items->type ?? 'array') : $schema->type) { case Type::STRING: if ($schema->format === 'binary') { $this->_attributeDefaults['contentType'] = 'application/octet-stream'; break; } // no break here case Type::BOOLEAN: case Type::INTEGER: case Type::NUMBER: $this->_attributeDefaults['contentType'] = 'text/plain'; break; case 'object': $this->_attributeDefaults['contentType'] = 'application/json'; break; } } parent::__construct($data); } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { } } php-openapi/src/spec/Info.php 0000644 00000002556 14736103140 0012120 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * 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. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#infoObject * * @property string $title * @property string $description * @property string $termsOfService * @property Contact|null $contact * @property License|null $license * @property string $version * */ class Info extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'title' => Type::STRING, 'description' => Type::STRING, 'termsOfService' => Type::STRING, 'contact' => Contact::class, 'license' => License::class, 'version' => Type::STRING, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['title', 'version']); } } php-openapi/src/spec/Type.php 0000644 00000002037 14736103140 0012140 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; /** * Data Types * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#dataTypes */ class Type { const ANY = 'any'; const INTEGER = 'integer'; const NUMBER = 'number'; const STRING = 'string'; const BOOLEAN = 'boolean'; const OBJECT = 'object'; const ARRAY = 'array'; /** * Indicate whether a type is a scalar type, i.e. not an array or object. * * For ANY this will return false. * * @param string $type value from one of the type constants defined in this class. * @return bool whether the type is a scalar type. * @since 1.2.1 */ public static function isScalar(string $type): bool { return in_array($type, [ self::INTEGER, self::NUMBER, self::STRING, self::BOOLEAN, ]); } } php-openapi/src/spec/ExternalDocumentation.php 0000644 00000002000 14736103140 0015521 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Allows referencing an external resource for extended documentation. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#externalDocumentationObject * * @property string $description * @property string $url * */ class ExternalDocumentation extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'description' => Type::STRING, 'url' => Type::STRING, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['url']); $this->validateUrl('url'); } } php-openapi/src/spec/Response.php 0000644 00000002353 14736103140 0013016 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Describes a single response from an API Operation, including design-time, static links to operations based on the response. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#responseObject * * @property string $description * @property Header[]|Reference[] $headers * @property MediaType[]|Reference[] $content * @property Link[]|Reference[] $links */ class Response extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'description' => Type::STRING, 'headers' => [Type::STRING, Header::class], 'content' => [Type::STRING, MediaType::class], 'links' => [Type::STRING, Link::class], ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->requireProperties(['description']); } } php-openapi/src/spec/OAuthFlow.php 0000644 00000002647 14736103140 0013076 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Configuration details for a supported OAuth Flow. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#oauthFlowObject * * @property string $authorizationUrl * @property string $tokenUrl * @property string $refreshUrl * @property string[] $scopes */ class OAuthFlow extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'authorizationUrl' => Type::STRING, 'tokenUrl' => Type::STRING, 'refreshUrl' => Type::STRING, 'scopes' => [Type::STRING, Type::STRING], ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. * * Call `addError()` in case of validation errors. */ protected function performValidation() { $this->requireProperties(['scopes']); // TODO: Validation in context of the parent object // authorizationUrl is required if this object is in "implicit", "authorizationCode" // tokenUrl is required if this object is in "password", "clientCredentials", "authorizationCode" } } php-openapi/src/spec/Xml.php 0000644 00000002140 14736103140 0011752 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * A metadata object that allows for more fine-tuned XML model definitions. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#xmlObject * * @property string $name * @property string $namespace * @property string $prefix * @property boolean $attribute * @property boolean $wrapped */ class Xml extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'name' => Type::STRING, 'namespace' => Type::STRING, 'prefix' => Type::STRING, 'attribute' => Type::BOOLEAN, 'wrapped' => Type::BOOLEAN, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { } } php-openapi/src/spec/PathItem.php 0000644 00000017171 14736103140 0012737 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\ReferenceContext; use cebe\openapi\SpecBaseObject; use cebe\openapi\SpecObjectInterface; use cebe\openapi\json\JsonPointer; /** * 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. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#pathItemObject * * @property string $summary * @property string $description * @property Operation|null $get * @property Operation|null $put * @property Operation|null $post * @property Operation|null $delete * @property Operation|null $options * @property Operation|null $head * @property Operation|null $patch * @property Operation|null $trace * @property Server[] $servers * @property Parameter[]|Reference[] $parameters * */ class PathItem extends SpecBaseObject { /** * @var Reference|null */ private $_ref; /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'summary' => Type::STRING, 'description' => Type::STRING, 'get' => Operation::class, 'put' => Operation::class, 'post' => Operation::class, 'delete' => Operation::class, 'options' => Operation::class, 'head' => Operation::class, 'patch' => Operation::class, 'trace' => Operation::class, 'servers' => [Server::class], 'parameters' => [Parameter::class], ]; } /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @throws \cebe\openapi\exceptions\TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { if (isset($data['$ref'])) { // Allows for an external definition of this path item. // $ref in a Path Item Object is not a Reference. // https://github.com/OAI/OpenAPI-Specification/issues/1038 $this->_ref = new Reference(['$ref' => $data['$ref']], PathItem::class); unset($data['$ref']); } parent::__construct($data); } /** * @return mixed returns the serializable data of this object for converting it * to JSON or YAML. */ public function getSerializableData() { $data = parent::getSerializableData(); if ($this->_ref instanceof Reference) { $data->{'$ref'} = $this->_ref->getReference(); } if (isset($data->servers) && empty($data->servers)) { unset($data->servers); } if (isset($data->parameters) && empty($data->parameters)) { unset($data->parameters); } return $data; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { // no required arguments } /** * Return all operations of this Path. * @return Operation[] */ public function getOperations() { $operations = []; foreach ($this->attributes() as $attribute => $type) { if ($type === Operation::class && isset($this->$attribute)) { $operations[$attribute] = $this->$attribute; } } return $operations; } /** * Allows for an external definition of this path item. The referenced structure MUST be in the format of a * PathItem Object. The properties of the referenced structure are merged with the local Path Item Object. * If the same property exists in both, the referenced structure and the local one, this is a conflict. * In this case the behavior is *undefined*. * @return Reference|null */ public function getReference(): ?Reference { return $this->_ref; } /** * Set context for all Reference Objects in this object. */ public function setReferenceContext(ReferenceContext $context) { if ($this->_ref instanceof Reference) { $this->_ref->setContext($context); } parent::setReferenceContext($context); } /** * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws \cebe\openapi\exceptions\UnresolvableReferenceException in case resolving a reference fails. */ public function resolveReferences(ReferenceContext $context = null) { if ($this->_ref instanceof Reference) { $pathItem = $this->_ref->resolve($context); $this->_ref = null; // The properties of the referenced structure are merged with the local Path Item Object. foreach (self::attributes() as $attribute => $type) { if (!isset($pathItem->$attribute)) { continue; } // If the same property exists in both, the referenced structure and the local one, this is a conflict. if (isset($this->$attribute) && !empty($this->$attribute)) { $this->addError("Conflicting properties, property '$attribute' exists in local PathItem and also in the referenced one."); } $this->$attribute = $pathItem->$attribute; // resolve references in all properties assinged from the reference // use the referenced object context in this case if ($this->$attribute instanceof Reference) { $referencedObject = $this->$attribute->resolve(); $this->$attribute = $referencedObject; if (!$referencedObject instanceof Reference && $referencedObject !== null) { $referencedObject->resolveReferences(); } } elseif ($this->$attribute instanceof SpecObjectInterface) { $this->$attribute->resolveReferences(); } elseif (is_array($this->$attribute)) { foreach ($this->$attribute as $k => $item) { if ($item instanceof Reference) { $referencedObject = $item->resolve(); $this->$attribute = [$k => $referencedObject] + $this->$attribute; if (!$referencedObject instanceof Reference && $referencedObject !== null) { $referencedObject->resolveReferences(); } } elseif ($item instanceof SpecObjectInterface) { $item->resolveReferences(); } } } } } parent::resolveReferences($context); } /** * Provide context information to the object. * * Context information contains a reference to the base object where it is contained in * as well as a JSON pointer to its position. * @param SpecObjectInterface $baseDocument * @param JsonPointer $jsonPointer */ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointer $jsonPointer) { parent::setDocumentContext($baseDocument, $jsonPointer); if ($this->_ref instanceof Reference) { $this->_ref->setDocumentContext($baseDocument, $jsonPointer->append('$ref')); } } } php-openapi/src/spec/Components.php 0000644 00000004731 14736103140 0013347 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Holds a set of reusable objects for different aspects of the OAS. * * 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. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#componentsObject * * @property Schema[]|Reference[] $schemas * @property Response[]|Reference[] $responses * @property Parameter[]|Reference[] $parameters * @property Example[]|Reference[] $examples * @property RequestBody[]|Reference[] $requestBodies * @property Header[]|Reference[] $headers * @property SecurityScheme[]|Reference[] $securitySchemes * @property Link[]|Reference[] $links * @property Callback[]|Reference[] $callbacks * * */ class Components extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'schemas' => [Type::STRING, Schema::class], 'responses' => [Type::STRING, Response::class], 'parameters' => [Type::STRING, Parameter::class], 'examples' => [Type::STRING, Example::class], 'requestBodies' => [Type::STRING, RequestBody::class], 'headers' => [Type::STRING, Header::class], 'securitySchemes' => [Type::STRING, SecurityScheme::class], 'links' => [Type::STRING, Link::class], 'callbacks' => [Type::STRING, Callback::class], ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { // All the fixed fields declared above are objects that MUST use keys that match the regular expression: ^[a-zA-Z0-9\.\-_]+$. foreach (array_keys($this->attributes()) as $attribute) { if (is_array($this->$attribute)) { foreach ($this->$attribute as $k => $v) { if (!preg_match('~^[a-zA-Z0-9\.\-_]+$~', $k)) { $this->addError("Invalid key '$k' used in Components Object for attribute '$attribute', does not match ^[a-zA-Z0-9\.\-_]+\$."); } } } } } } php-openapi/src/spec/OpenApi.php 0000644 00000004701 14736103140 0012552 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\SpecBaseObject; /** * This is the root document object of the OpenAPI document. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#openapi-object * * @property string $openapi * @property Info $info * @property Server[] $servers * @property Paths|PathItem[] $paths * @property Components|null $components * @property SecurityRequirement[] $security * @property Tag[] $tags * @property ExternalDocumentation|null $externalDocs * */ class OpenApi extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'openapi' => Type::STRING, 'info' => Info::class, 'servers' => [Server::class], 'paths' => Paths::class, 'components' => Components::class, 'security' => [SecurityRequirement::class], 'tags' => [Tag::class], 'externalDocs' => ExternalDocumentation::class, ]; } /** * @return array array of attributes default values. */ protected function attributeDefaults(): array { return [ // Spec: If the servers property is not provided, or is an empty array, // the default value would be a Server Object with a url value of /. 'servers' => [ new Server(['url' => '/']) ], ]; } public function __get($name) { $ret = parent::__get($name); // Spec: If the servers property is not provided, or is an empty array, // the default value would be a Server Object with a url value of /. if ($name === 'servers' && $ret === []) { return $this->attributeDefaults()['servers']; } return $ret; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ public function performValidation() { $this->requireProperties(['openapi', 'info', 'paths']); if (!empty($this->openapi) && !preg_match('/^3\.0\.\d+(-rc\d)?$/i', $this->openapi)) { $this->addError('Unsupported openapi version: ' . $this->openapi); } } } php-openapi/src/spec/OAuthFlows.php 0000644 00000002154 14736103140 0013252 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Allows configuration of the supported OAuth Flows. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#oauthFlowsObject * * @property OAuthFlow|null $implicit * @property OAuthFlow|null $password * @property OAuthFlow|null $clientCredentials * @property OAuthFlow|null $authorizationCode */ class OAuthFlows extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'implicit' => OAuthFlow::class, 'password' => OAuthFlow::class, 'clientCredentials' => OAuthFlow::class, 'authorizationCode' => OAuthFlow::class, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { } } php-openapi/src/spec/MediaType.php 0000644 00000005324 14736103140 0013102 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\SpecBaseObject; /** * Each Media Type Object provides schema and examples for the media type identified by its key. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#mediaTypeObject * * @property Schema|Reference|null $schema * @property mixed $example * @property Example[]|Reference[] $examples * @property Encoding[] $encoding */ class MediaType extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'schema' => Schema::class, 'example' => Type::ANY, 'examples' => [Type::STRING, Example::class], 'encoding' => [Type::STRING, Encoding::class], ]; } /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { // instantiate Encoding by passing the schema for extracting default values $encoding = $data['encoding'] ?? null; unset($data['encoding']); parent::__construct($data); if (!empty($encoding)) { foreach ($encoding as $property => $encodingData) { if ($encodingData instanceof Encoding) { $encoding[$property] = $encodingData; } elseif (is_array($encodingData)) { $schema = $this->schema->properties[$property] ?? null; // Don't pass the schema if it's still an unresolved reference. if ($schema instanceof Reference) { $encoding[$property] = new Encoding($encodingData); } else { $encoding[$property] = new Encoding($encodingData, $schema); } } else { $givenType = gettype($encodingData); if ($givenType === 'object') { $givenType = get_class($encodingData); } throw new TypeErrorException(sprintf('Encoding MUST be either array or Encoding object, "%s" given', $givenType)); } } $this->encoding = $encoding; } } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { } } php-openapi/src/spec/Contact.php 0000644 00000001767 14736103140 0012623 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * Contact information for the exposed API. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#contactObject * * @property string $name * @property string $url * @property string $email * */ class Contact extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'name' => Type::STRING, 'url' => Type::STRING, 'email' => Type::STRING, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { $this->validateEmail('email'); $this->validateUrl('url'); } } php-openapi/src/spec/Callback.php 0000644 00000012143 14736103140 0012712 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\DocumentContextInterface; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\JsonPointer; use cebe\openapi\ReferenceContext; use cebe\openapi\SpecObjectInterface; /** * A map of possible out-of band callbacks related to the parent operation. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#callbackObject * */ class Callback implements SpecObjectInterface, DocumentContextInterface { /** * @var string|null */ private $_url; /** * @var PathItem */ private $_pathItem; /** * @var array */ private $_errors = []; /** * @var SpecObjectInterface|null */ private $_baseDocument; /** * @var JsonPointer|null */ private $_jsonPointer; /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { if (count($data) !== 1) { $this->_errors[] = 'Callback object must have exactly one URL.'; return; } $this->_pathItem = new PathItem(reset($data)); $this->_url = key($data); } /** * @return mixed returns the serializable data of this object for converting it * to JSON or YAML. */ public function getSerializableData() { return (object) [$this->_url => ($this->_pathItem === null) ? null : $this->_pathItem->getSerializableData()]; } /** * @return string */ public function getUrl() { return $this->_url; } /** * @param string $url */ public function setUrl(string $url): void { $this->_url = $url; } /** * @return PathItem */ public function getRequest(): ?PathItem { return $this->_pathItem; } /** * @param PathItem $request */ public function setRequest(?PathItem $request): void { $this->_pathItem = $request; } /** * Validate object data according to OpenAPI spec. * @return bool whether the loaded data is valid according to OpenAPI spec * @see getErrors() */ public function validate(): bool { $pathItemValid = $this->_pathItem === null || $this->_pathItem->validate(); return $pathItemValid && empty($this->_errors); } /** * @return string[] list of validation errors according to OpenAPI spec. * @see validate() */ public function getErrors(): array { if (($pos = $this->getDocumentPosition()) !== null) { $errors = array_map(function ($e) use ($pos) { return "[{$pos}] $e"; }, $this->_errors); } else { $errors = $this->_errors; } $pathItemErrors = $this->_pathItem === null ? [] : $this->_pathItem->getErrors(); return array_merge($errors, $pathItemErrors); } /** * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws UnresolvableReferenceException */ public function resolveReferences(ReferenceContext $context = null) { if ($this->_pathItem !== null) { $this->_pathItem->resolveReferences($context); } } /** * Set context for all Reference Objects in this object. */ public function setReferenceContext(ReferenceContext $context) { if ($this->_pathItem !== null) { $this->_pathItem->setReferenceContext($context); } } /** * Provide context information to the object. * * Context information contains a reference to the base object where it is contained in * as well as a JSON pointer to its position. * @param SpecObjectInterface $baseDocument * @param JsonPointer $jsonPointer */ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointer $jsonPointer) { $this->_baseDocument = $baseDocument; $this->_jsonPointer = $jsonPointer; if ($this->_pathItem instanceof DocumentContextInterface) { $this->_pathItem->setDocumentContext($baseDocument, $jsonPointer->append($this->_url)); } } /** * @return SpecObjectInterface|null returns the base document where this object is located in. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getBaseDocument(): ?SpecObjectInterface { return $this->_baseDocument; } /** * @return JsonPointer|null returns a JSON pointer describing the position of this object in the base document. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getDocumentPosition(): ?JsonPointer { return $this->_jsonPointer; } } php-openapi/src/spec/Link.php 0000644 00000002721 14736103140 0012114 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi\spec; use cebe\openapi\SpecBaseObject; /** * The Link object represents a possible design-time link for a response. * * @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#linkObject * * @property string $operationRef * @property string $operationId * @property array $parameters * @property mixed $requestBody * @property string $description * @property Server|null $server * */ class Link extends SpecBaseObject { /** * @return array array of attributes available in this object. */ protected function attributes(): array { return [ 'operationRef' => Type::STRING, 'operationId' => Type::STRING, 'parameters' => [Type::STRING, Type::ANY], // TODO: how to specify {expression}? 'requestBody' => Type::ANY, // TODO: how to specify {expression}? 'description' => Type::STRING, 'server' => Server::class, ]; } /** * Perform validation on this object, check data against OpenAPI Specification rules. */ protected function performValidation() { if (!empty($this->operationId) && !empty($this->operationRef)) { $this->addError('Link: operationId and operationRef are mutually exclusive.'); } } } php-openapi/src/ReferenceContextCache.php 0000644 00000001613 14736103140 0014453 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi; /** * ReferenceContextCache represents a cache storage for caching content of referenced files. */ class ReferenceContextCache { private $_cache = []; public function set($ref, $type, $data) { $this->_cache[$ref][$type ?? ''] = $data; // store fallback value for resolving with unknown type if ($type !== null && !isset($this->_cache[$ref][''])) { $this->_cache[$ref][''] = $data; } } public function get($ref, $type) { return $this->_cache[$ref][$type ?? ''] ?? null; } public function has($ref, $type) { return isset($this->_cache[$ref]) && array_key_exists($type ?? '', $this->_cache[$ref]); } } php-openapi/src/SpecBaseObject.php 0000644 00000042267 14736103140 0013112 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnknownPropertyException; use cebe\openapi\json\JsonPointer; use cebe\openapi\json\JsonReference; use cebe\openapi\spec\Reference; use cebe\openapi\spec\Type; /** * Base class for all spec objects. * * Implements property management and validation basics. * */ abstract class SpecBaseObject implements SpecObjectInterface, DocumentContextInterface { private $_properties = []; private $_errors = []; private $_recursingSerializableData = false; private $_recursingValidate = false; private $_recursingErrors = false; private $_recursingReferences = false; private $_recursingReferenceContext = false; private $_recursingDocumentContext = false; private $_baseDocument; private $_jsonPointer; /** * @return array array of attributes available in this object. */ abstract protected function attributes(): array; /** * @return array array of attributes default values. */ protected function attributeDefaults(): array { return []; } /** * Perform validation on this object, check data against OpenAPI Specification rules. * * Call `addError()` in case of validation errors. */ abstract protected function performValidation(); /** * Create an object from spec data. * @param array $data spec data read from YAML or JSON * @throws TypeErrorException in case invalid data is supplied. */ public function __construct(array $data) { foreach ($this->attributes() as $property => $type) { if (!isset($data[$property])) { continue; } if ($type === Type::BOOLEAN) { if (!\is_bool($data[$property])) { $this->_errors[] = "property '$property' must be boolean, but " . gettype($data[$property]) . " given."; continue; } $this->_properties[$property] = (bool) $data[$property]; } elseif (\is_array($type)) { if (!\is_array($data[$property])) { $this->_errors[] = "property '$property' must be array, but " . gettype($data[$property]) . " given."; continue; } switch (\count($type)) { case 1: if (isset($data[$property]['$ref'])) { $this->_properties[$property] = new Reference($data[$property], null); } else { // array $this->_properties[$property] = []; foreach ($data[$property] as $item) { if ($type[0] === Type::STRING) { if (!is_string($item)) { $this->_errors[] = "property '$property' must be array of strings, but array has " . gettype($item) . " element."; } $this->_properties[$property][] = $item; } elseif (Type::isScalar($type[0])) { $this->_properties[$property][] = $item; } elseif ($type[0] === Type::ANY) { if (is_array($item) && isset($item['$ref'])) { $this->_properties[$property][] = new Reference($item, null); } else { $this->_properties[$property][] = $item; } } else { $this->_properties[$property][] = $this->instantiate($type[0], $item); } } } break; case 2: // map if ($type[0] !== Type::STRING) { throw new TypeErrorException('Invalid map key type: ' . $type[0]); } $this->_properties[$property] = []; foreach ($data[$property] as $key => $item) { if ($type[1] === Type::STRING) { if (!is_string($item)) { $this->_errors[] = "property '$property' must be map<string, string>, but entry '$key' is of type " . \gettype($item) . '.'; } $this->_properties[$property][$key] = $item; } elseif ($type[1] === Type::ANY || Type::isScalar($type[1])) { $this->_properties[$property][$key] = $item; } else { $this->_properties[$property][$key] = $this->instantiate($type[1], $item); } } break; } } elseif (Type::isScalar($type)) { $this->_properties[$property] = $data[$property]; } elseif ($type === Type::ANY) { if (is_array($data[$property]) && isset($data[$property]['$ref'])) { $this->_properties[$property] = new Reference($data[$property], null); } else { $this->_properties[$property] = $data[$property]; } } else { $this->_properties[$property] = $this->instantiate($type, $data[$property]); } unset($data[$property]); } foreach ($data as $additionalProperty => $value) { $this->_properties[$additionalProperty] = $value; } } /** * @throws TypeErrorException */ protected function instantiate($type, $data) { if ($data instanceof $type || $data instanceof Reference) { return $data; } if (is_array($data) && isset($data['$ref'])) { return new Reference($data, $type); } if (!is_array($data)) { throw new TypeErrorException( "Unable to instantiate {$type} Object with data '" . print_r($data, true) . "' at " . $this->getDocumentPosition() ); } try { return new $type($data); } catch (\TypeError $e) { throw new TypeErrorException( "Unable to instantiate {$type} Object with data '" . print_r($data, true) . "' at " . $this->getDocumentPosition(), $e->getCode(), $e ); } } /** * @return mixed returns the serializable data of this object for converting it * to JSON or YAML. */ public function getSerializableData() { if ($this->_recursingSerializableData) { // return a reference return (object) ['$ref' => JsonReference::createFromUri('', $this->getDocumentPosition())->getReference()]; } $this->_recursingSerializableData = true; $data = $this->_properties; foreach ($data as $k => $v) { if ($v instanceof SpecObjectInterface) { $data[$k] = $v->getSerializableData(); } elseif (is_array($v)) { $toObject = false; $j = 0; foreach ($v as $i => $d) { if ($j++ !== $i) { $toObject = true; } if ($d instanceof SpecObjectInterface) { $data[$k][$i] = $d->getSerializableData(); } } if ($toObject) { $data[$k] = (object) $data[$k]; } } } $this->_recursingSerializableData = false; return (object) $data; } /** * Validate object data according to OpenAPI spec. * @return bool whether the loaded data is valid according to OpenAPI spec * @see getErrors() */ public function validate(): bool { // avoid recursion to get stuck in a loop if ($this->_recursingValidate) { return true; } $this->_recursingValidate = true; $valid = true; foreach ($this->_properties as $v) { if ($v instanceof SpecObjectInterface) { if (!$v->validate()) { $valid = false; } } elseif (is_array($v)) { foreach ($v as $item) { if ($item instanceof SpecObjectInterface) { if (!$item->validate()) { $valid = false; } } } } } $this->_recursingValidate = false; $this->performValidation(); if (!empty($this->_errors)) { $valid = false; } return $valid; } /** * @return string[] list of validation errors according to OpenAPI spec. * @see validate() */ public function getErrors(): array { // avoid recursion to get stuck in a loop if ($this->_recursingErrors) { return []; } $this->_recursingErrors = true; if (($pos = $this->getDocumentPosition()) !== null) { $errors = [ array_map(function ($e) use ($pos) { return "[{$pos->getPointer()}] $e"; }, $this->_errors) ]; } else { $errors = [$this->_errors]; } foreach ($this->_properties as $v) { if ($v instanceof SpecObjectInterface) { $errors[] = $v->getErrors(); } elseif (is_array($v)) { foreach ($v as $item) { if ($item instanceof SpecObjectInterface) { $errors[] = $item->getErrors(); } } } } $this->_recursingErrors = false; return array_merge(...$errors); } /** * @param string $error error message to add. */ protected function addError(string $error, $class = '') { $shortName = explode('\\', $class); $this->_errors[] = end($shortName).$error; } protected function hasProperty(string $name): bool { return isset($this->_properties[$name]) || isset($this->attributes()[$name]); } protected function requireProperties(array $names) { foreach ($names as $name) { if (!isset($this->_properties[$name])) { $this->addError(" is missing required property: $name", get_class($this)); } } } protected function validateEmail(string $property) { if (!empty($this->$property) && strpos($this->$property, '@') === false) { $this->addError('::$'.$property.' does not seem to be a valid email address: ' . $this->$property, get_class($this)); } } protected function validateUrl(string $property) { if (!empty($this->$property) && strpos($this->$property, '//') === false) { $this->addError('::$'.$property.' does not seem to be a valid URL: ' . $this->$property, get_class($this)); } } public function __get($name) { if (isset($this->_properties[$name])) { return $this->_properties[$name]; } $defaults = $this->attributeDefaults(); if (array_key_exists($name, $defaults)) { return $defaults[$name]; } if (isset($this->attributes()[$name])) { if (is_array($this->attributes()[$name])) { return []; } elseif ($this->attributes()[$name] === Type::BOOLEAN) { return false; } return null; } throw new UnknownPropertyException('Getting unknown property: ' . \get_class($this) . '::' . $name); } public function __set($name, $value) { $this->_properties[$name] = $value; } public function __isset($name) { if (isset($this->_properties[$name]) || isset($this->attributeDefaults()[$name]) || isset($this->attributes()[$name])) { return $this->__get($name) !== null; } return false; } public function __unset($name) { unset($this->_properties[$name]); } /** * Resolves all Reference Objects in this object and replaces them with their resolution. * @throws exceptions\UnresolvableReferenceException in case resolving a reference fails. */ public function resolveReferences(ReferenceContext $context = null) { // avoid recursion to get stuck in a loop if ($this->_recursingReferences) { return; } $this->_recursingReferences = true; foreach ($this->_properties as $property => $value) { if ($value instanceof Reference) { $referencedObject = $value->resolve($context); $this->_properties[$property] = $referencedObject; if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) { $referencedObject->resolveReferences(); } } elseif ($value instanceof SpecObjectInterface) { $value->resolveReferences($context); } elseif (is_array($value)) { foreach ($value as $k => $item) { if ($item instanceof Reference) { $referencedObject = $item->resolve($context); $this->_properties[$property][$k] = $referencedObject; if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) { $referencedObject->resolveReferences(); } } elseif ($item instanceof SpecObjectInterface) { $item->resolveReferences($context); } } } } $this->_recursingReferences = false; } /** * Set context for all Reference Objects in this object. */ public function setReferenceContext(ReferenceContext $context) { // avoid recursion to get stuck in a loop if ($this->_recursingReferenceContext) { return; } $this->_recursingReferenceContext = true; foreach ($this->_properties as $property => $value) { if ($value instanceof Reference) { $value->setContext($context); } elseif ($value instanceof SpecObjectInterface) { $value->setReferenceContext($context); } elseif (is_array($value)) { foreach ($value as $k => $item) { if ($item instanceof Reference) { $item->setContext($context); } elseif ($item instanceof SpecObjectInterface) { $item->setReferenceContext($context); } } } } $this->_recursingReferenceContext = false; } /** * Provide context information to the object. * * Context information contains a reference to the base object where it is contained in * as well as a JSON pointer to its position. * @param SpecObjectInterface $baseDocument * @param JsonPointer $jsonPointer */ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointer $jsonPointer) { $this->_baseDocument = $baseDocument; $this->_jsonPointer = $jsonPointer; // avoid recursion to get stuck in a loop if ($this->_recursingDocumentContext) { return; } $this->_recursingDocumentContext = true; foreach ($this->_properties as $property => $value) { if ($value instanceof DocumentContextInterface) { $value->setDocumentContext($baseDocument, $jsonPointer->append($property)); } elseif (is_array($value)) { foreach ($value as $k => $item) { if ($item instanceof DocumentContextInterface) { $item->setDocumentContext($baseDocument, $jsonPointer->append($property)->append($k)); } } } } $this->_recursingDocumentContext = false; } /** * @return SpecObjectInterface|null returns the base document where this object is located in. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getBaseDocument(): ?SpecObjectInterface { return $this->_baseDocument; } /** * @return JsonPointer|null returns a JSON pointer describing the position of this object in the base document. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getDocumentPosition(): ?JsonPointer { return $this->_jsonPointer; } } php-openapi/src/Reader.php 0000644 00000016773 14736103140 0011503 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi; use cebe\openapi\exceptions\IOException; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\InvalidJsonPointerSyntaxException; use cebe\openapi\json\JsonPointer; use cebe\openapi\spec\OpenApi; use Symfony\Component\Yaml\Yaml; /** * Utility class to simplify reading JSON or YAML OpenAPI specs. * */ class Reader { /** * Populate OpenAPI spec object from JSON data. * @phpstan-template T of SpecObjectInterface * @phpstan-param class-string<T> $baseType * @phpstan-return T * @param string $json the JSON string to decode. * @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]]. * The default is [[OpenApi]] which is the base type of a OpenAPI specification file. * You may choose a different type if you instantiate objects from sub sections of a specification. * @return SpecObjectInterface|OpenApi the OpenApi object instance. * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. */ public static function readFromJson(string $json, string $baseType = OpenApi::class): SpecObjectInterface { return new $baseType(json_decode($json, true)); } /** * Populate OpenAPI spec object from YAML data. * @phpstan-template T of SpecObjectInterface * @phpstan-param class-string<T> $baseType * @phpstan-return T * @param string $yaml the YAML string to decode. * @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]]. * The default is [[OpenApi]] which is the base type of a OpenAPI specification file. * You may choose a different type if you instantiate objects from sub sections of a specification. * @return SpecObjectInterface|OpenApi the OpenApi object instance. * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. */ public static function readFromYaml(string $yaml, string $baseType = OpenApi::class): SpecObjectInterface { return new $baseType(Yaml::parse($yaml)); } /** * Populate OpenAPI spec object from a JSON file. * @phpstan-template T of SpecObjectInterface * @phpstan-param class-string<T> $baseType * @phpstan-return T * @param string $fileName the file name of the file to be read. * If `$resolveReferences` is true (the default), this should be an absolute URL, a `file://` URI or * an absolute path to allow resolving relative path references. * @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]]. * The default is [[OpenApi]] which is the base type of a OpenAPI specification file. * You may choose a different type if you instantiate objects from sub sections of a specification. * @param bool|string $resolveReferences whether to automatically resolve references in the specification. * If `true`, all [[Reference]] objects will be replaced with their referenced spec objects by calling * [[SpecObjectInterface::resolveReferences()]]. * Since version 1.5.0 this can be a string indicating the reference resolving mode: * - `inline` only resolve references to external files. * - `all` resolve all references except recursive references. * @return SpecObjectInterface|OpenApi the OpenApi object instance. * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. * @throws UnresolvableReferenceException in case references could not be resolved. * @throws IOException when the file is not readable. * @throws InvalidJsonPointerSyntaxException in case an invalid JSON pointer string is passed to the spec references. */ public static function readFromJsonFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface { $fileContent = file_get_contents($fileName); if ($fileContent === false) { $e = new IOException("Failed to read file: '$fileName'"); $e->fileName = $fileName; throw $e; } $spec = static::readFromJson($fileContent, $baseType); $context = new ReferenceContext($spec, $fileName); $spec->setReferenceContext($context); if ($resolveReferences !== false) { if (is_string($resolveReferences)) { $context->mode = $resolveReferences; } if ($spec instanceof DocumentContextInterface) { $spec->setDocumentContext($spec, new JsonPointer('')); } $spec->resolveReferences(); } return $spec; } /** * Populate OpenAPI spec object from YAML file. * @phpstan-template T of SpecObjectInterface * @phpstan-param class-string<T> $baseType * @phpstan-return T * @param string $fileName the file name of the file to be read. * If `$resolveReferences` is true (the default), this should be an absolute URL, a `file://` URI or * an absolute path to allow resolving relative path references. * @param string $baseType the base Type to instantiate. This must be an instance of [[SpecObjectInterface]]. * The default is [[OpenApi]] which is the base type of a OpenAPI specification file. * You may choose a different type if you instantiate objects from sub sections of a specification. * @param bool|string $resolveReferences whether to automatically resolve references in the specification. * If `true`, all [[Reference]] objects will be replaced with their referenced spec objects by calling * [[SpecObjectInterface::resolveReferences()]]. * Since version 1.5.0 this can be a string indicating the reference resolving mode: * - `inline` only resolve references to external files. * - `all` resolve all references except recursive references. * @return SpecObjectInterface|OpenApi the OpenApi object instance. * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. * @throws UnresolvableReferenceException in case references could not be resolved. * @throws IOException when the file is not readable. */ public static function readFromYamlFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface { $fileContent = file_get_contents($fileName); if ($fileContent === false) { $e = new IOException("Failed to read file: '$fileName'"); $e->fileName = $fileName; throw $e; } $spec = static::readFromYaml($fileContent, $baseType); $context = new ReferenceContext($spec, $fileName); $spec->setReferenceContext($context); if ($resolveReferences !== false) { if (is_string($resolveReferences)) { $context->mode = $resolveReferences; } if ($spec instanceof DocumentContextInterface) { $spec->setDocumentContext($spec, new JsonPointer('')); } $spec->resolveReferences(); } return $spec; } } php-openapi/src/DocumentContextInterface.php 0000644 00000002733 14736103140 0015234 0 ustar 00 <?php /** * @copyright Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors * @license https://github.com/cebe/php-openapi/blob/master/LICENSE */ namespace cebe\openapi; use cebe\openapi\json\JsonPointer; /** * Interface implemented by OpenAPI objects that provide functionality for context in the document. * * Allows an object to reference the base OpenAPI document as well as its own position inside of * the document in form of a [JSON pointer](https://tools.ietf.org/html/rfc6901). */ interface DocumentContextInterface { /** * Provide context information to the object. * * Context information contains a reference to the base object where it is contained in * as well as a JSON pointer to its position. * @param SpecObjectInterface $baseDocument * @param JsonPointer $jsonPointer */ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointer $jsonPointer); /** * @return SpecObjectInterface|null returns the base document where this object is located in. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getBaseDocument(): ?SpecObjectInterface; /** * @return JsonPointer|null returns a JSON pointer describing the position of this object in the base document. * Returns `null` if no context information was provided by [[setDocumentContext]]. */ public function getDocumentPosition(): ?JsonPointer; } php-openapi/README.md 0000644 00000023636 14736103140 0010254 0 ustar 00 # php-openapi Read and write [OpenAPI](https://www.openapis.org/) 3.0.x YAML and JSON files and make the content accessible in PHP objects. It also provides a CLI tool for validating and converting OpenAPI 3.0.x Description files. [![Latest Stable Version](https://poser.pugx.org/cebe/php-openapi/v/stable)](https://packagist.org/packages/cebe/php-openapi) [![Total Downloads](https://poser.pugx.org/cebe/php-openapi/downloads)](https://packagist.org/packages/cebe/php-openapi) [![Build Status](https://github.com/cebe/php-openapi/workflows/PHP%20Composer/badge.svg)](https://github.com/cebe/php-openapi/actions) [![License](https://poser.pugx.org/cebe/php-openapi/license)](https://packagist.org/packages/cebe/php-openapi) ## Install composer require cebe/php-openapi ## Requirements - PHP 7.1 or higher (works fine with PHP 8) ## Used by This library provides a low level API for reading and writing OpenAPI files. It is used by higher level tools to do awesome work: - [cebe/yii2-openapi](https://github.com/cebe/yii2-openapi) Code Generator for REST API from OpenAPI 3 Descriptions, includes fake data generator. - [cebe/yii2-app-api](https://github.com/cebe/yii2-app-api) Yii framework application template for developing API-first applications. - [league/openapi-psr7-validator](https://github.com/thephpleague/openapi-psr7-validator) validates PSR-7 messages (HTTP request/response) against OpenAPI descriptions. - [dsuurlant/response2schema](https://github.com/dsuurlant/response2schema) a quick and easy tool for generating OpenAPI schemas based on example data. - ... ([add yours](https://github.com/cebe/php-openapi/edit/master/README.md#L24)) ## Usage ### CLI Tool $ vendor/bin/php-openapi help PHP OpenAPI 3 tool ------------------ by Carsten Brandt <mail@cebe.cc> Usage: php-openapi <command> [<options>] [input.yml|input.json] [output.yml|output.json] The following commands are available: validate Validate the API Description in the specified input file against the OpenAPI v3.0 schema. Note: the validation is performed in two steps. The results are composed of (1) structural errors found while reading the API Description file, and (2) violations of the OpenAPI v3.0 schema. If no input file is specified input will be read from STDIN. The tool will try to auto-detect the content type of the input, but may fail to do so. You may specify --read-yaml or --read-json to force the file type. Exits with code 2 on validation errors, 1 on other errors and 0 on success. convert Convert a JSON or YAML input file to JSON or YAML output file. If no input file is specified input will be read from STDIN. If no output file is specified output will be written to STDOUT. The tool will try to auto-detect the content type of the input and output file, but may fail to do so. You may specify --read-yaml or --read-json to force the input file type. and --write-yaml or --write-json to force the output file type. By default all references are resolved (replaced with the object referred to). You can control handling of references with the following arguments: --resolve-none Do not resolve references. --resolve-external Only resolve references that point to external files. This process is often referred to as "inlining". --resolve-all Resolve all references (default). Recursive pointers will stay references. inline Convert a JSON or YAML input file to JSON or YAML output file and resolve all external references. The output will be a single API Description file. This is a shortcut for calling convert --resolve-external. help Shows this usage information. Options: --read-json force reading input as JSON. Auto-detect if not specified. --read-yaml force reading input as YAML. Auto-detect if not specified. --write-json force writing output as JSON. Auto-detect if not specified. --write-yaml force writing output as YAML. Auto-detect if not specified. -s, --silent silent mode. Will hide all success/information messages and only print errors. ### Reading API Description Files Read OpenAPI Description from JSON file: ```php use cebe\openapi\Reader; // realpath is needed for resolving references with relative Paths or URLs $openapi = Reader::readFromJsonFile(realpath('openapi.json')); ``` Read OpenAPI Description from YAML: ```php use cebe\openapi\Reader; // realpath is needed for resolving references with relative Paths or URLs $openapi = Reader::readFromYamlFile(realpath('openapi.yaml')); // you may also specify the URL to your API Description file $openapi = Reader::readFromYamlFile('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/3.0.2/examples/v3.0/petstore-expanded.yaml'); ``` Access API Description data: ```php echo $openapi->openapi; // openAPI version, e.g. 3.0.0 echo $openapi->info->title; // API title foreach($openapi->paths as $path => $definition) { // iterate path definitions } ``` Object properties are exactly like in the [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#openapi-specification). You may also access additional properties added by [specification extensions](https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#specificationExtensions). ### Writing API Description Files ```php use cebe\openapi\spec\OpenApi; use cebe\openapi\spec\PathItem; // create base API Description $openapi = new OpenApi([ 'openapi' => '3.0.2', 'info' => [ 'title' => 'Test API', 'version' => '1.0.0', ], 'paths' => [], ]); // manipulate description as needed $openapi->paths['/test'] = new PathItem([ 'description' => 'something' ]); // ... $json = \cebe\openapi\Writer::writeToJson($openapi); ``` results in the following JSON data: ```json { "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": { "/test": { "description": "something" } } } ``` ### Writing API Description Files using prepared Objects Since version 1.2.0, the above example can also be written like this (passing objects instead of arrays): ```php use cebe\openapi\spec\OpenApi; use cebe\openapi\spec\PathItem; use cebe\openapi\spec\Info; // create base API Description $openapi = new OpenApi([ 'openapi' => '3.0.2', 'info' => new Info([ 'title' => 'Test API', 'version' => '1.0.0', ]), 'paths' => [ '/test' => new PathItem([ 'description' => 'something' ]), ], ]); $json = \cebe\openapi\Writer::writeToJson($openapi); ``` ### Reading API Description Files and Resolving References In the above we have passed the raw JSON or YAML data to the Reader. In order to be able to resolve references to structures in external files, we must provide the full context. ```php use cebe\openapi\Reader; // an absolute URL or file path is needed to allow resolving external references $openapi = Reader::readFromJsonFile('https://www.example.com/api/openapi.json'); $openapi = Reader::readFromYamlFile('https://www.example.com/api/openapi.yaml'); ``` If data has been loaded in a different way you can manually resolve references like this by giving a context: ```php $openapi->resolveReferences( new \cebe\openapi\ReferenceContext($openapi, 'https://www.example.com/api/openapi.yaml') ); ``` ### Validation The library provides simple validation operations, that check basic OpenAPI spec requirements. This is the same as "structural errors found while reading the API Description file" from the CLI tool. This validation does not include checking against the OpenAPI v3.0 JSON schema, this is only implemented in the CLI. ``` // return `true` in case no errors have been found, `false` in case of errors. $specValid = $openapi->validate(); // after validation getErrors() can be used to retrieve the list of errors found. $errors = $openapi->getErrors(); ``` > **Note:** Validation is done on a very basic level and is not complete. So a failing validation will show some errors, > but the list of errors given may not be complete. Also a passing validation does not necessarily indicate a completely > valid spec. ## Completeness This library is currently work in progress, the following list tracks completeness: - [x] read OpenAPI 3.0 JSON - [x] read OpenAPI 3.0 YAML - [ ] OpenAPI 3.0 Schema - [x] OpenAPI Object - [x] Info Object - [x] Contact Object - [x] License Object - [x] Server Object - [x] Server Variable Object - [x] Components Object - [x] Paths Object - [x] Path Item Object - [x] Operation Object - [x] External Documentation Object - [x] Parameter Object - [x] Request Body Object - [x] Media Type Object - [x] Encoding Object - [x] Responses Object - [x] Response Object - [x] Callback Object - [x] Example Object - [x] Link Object - [ ] [Runtime Expressions](https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#runtime-expressions) - [x] Header Object - [x] Tag Object - [x] Reference Object - [x] Schema Object - [x] load/read - [ ] validation - [x] Discriminator Object - [x] XML Object - [x] Security Scheme Object - [x] OAuth Flows Object - [x] OAuth Flow Object - [x] Security Requirement Object # Support **Need help with your API project?** Professional support, consulting as well as software development services are available: https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). php-openapi/LICENSE 0000644 00000002117 14736103140 0007771 0 ustar 00 MIT License Copyright (c) 2018 Carsten Brandt <mail@cebe.cc> and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. php-openapi/package.json 0000644 00000000064 14736103140 0011251 0 ustar 00 { "dependencies": { "speccy": "^0.11.0" } } php-openapi/schemas/openapi-v3.0.yaml 0000644 00000053051 14736103140 0013415 0 ustar 00 id: https://spec.openapis.org/oas/3.0/schema/2019-04-02 $schema: http://json-schema.org/draft-04/schema# description: Validation schema for OpenAPI Specification 3.0.X. type: object required: - openapi - info - paths properties: openapi: type: string pattern: ^3\.0\.\d(-.+)?$ info: $ref: '#/definitions/Info' externalDocs: $ref: '#/definitions/ExternalDocumentation' servers: type: array items: $ref: '#/definitions/Server' security: type: array items: $ref: '#/definitions/SecurityRequirement' tags: type: array items: $ref: '#/definitions/Tag' uniqueItems: true paths: $ref: '#/definitions/Paths' components: $ref: '#/definitions/Components' patternProperties: '^x-': {} additionalProperties: false definitions: Reference: type: object required: - $ref patternProperties: '^\$ref$': type: string format: uri-reference Info: type: object required: - title - version properties: title: type: string description: type: string termsOfService: type: string format: uri-reference contact: $ref: '#/definitions/Contact' license: $ref: '#/definitions/License' version: type: string patternProperties: '^x-': {} additionalProperties: false Contact: type: object properties: name: type: string url: type: string format: uri-reference email: type: string format: email patternProperties: '^x-': {} additionalProperties: false License: type: object required: - name properties: name: type: string url: type: string format: uri-reference patternProperties: '^x-': {} additionalProperties: false Server: type: object required: - url properties: url: type: string description: type: string variables: type: object additionalProperties: $ref: '#/definitions/ServerVariable' patternProperties: '^x-': {} additionalProperties: false ServerVariable: type: object required: - default properties: enum: type: array items: type: string default: type: string description: type: string patternProperties: '^x-': {} additionalProperties: false Components: type: object properties: schemas: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' responses: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/Response' parameters: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/Parameter' examples: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/Example' requestBodies: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/RequestBody' headers: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/Header' securitySchemes: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/SecurityScheme' links: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/Link' callbacks: type: object patternProperties: '^[a-zA-Z0-9\.\-_]+$': oneOf: - $ref: '#/definitions/Reference' - $ref: '#/definitions/Callback' patternProperties: '^x-': {} additionalProperties: false Schema: type: object properties: title: type: string multipleOf: type: number minimum: 0 exclusiveMinimum: true maximum: type: number exclusiveMaximum: type: boolean default: false minimum: type: number exclusiveMinimum: type: boolean default: false maxLength: type: integer minimum: 0 minLength: type: integer minimum: 0 default: 0 pattern: type: string format: regex maxItems: type: integer minimum: 0 minItems: type: integer minimum: 0 default: 0 uniqueItems: type: boolean default: false maxProperties: type: integer minimum: 0 minProperties: type: integer minimum: 0 default: 0 required: type: array items: type: string minItems: 1 uniqueItems: true enum: type: array items: {} minItems: 1 uniqueItems: false type: type: string enum: - array - boolean - integer - number - object - string not: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' allOf: type: array items: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' oneOf: type: array items: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' anyOf: type: array items: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' items: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' properties: type: object additionalProperties: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' additionalProperties: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' - type: boolean default: true description: type: string format: type: string default: {} nullable: type: boolean default: false discriminator: $ref: '#/definitions/Discriminator' readOnly: type: boolean default: false writeOnly: type: boolean default: false example: {} externalDocs: $ref: '#/definitions/ExternalDocumentation' deprecated: type: boolean default: false xml: $ref: '#/definitions/XML' patternProperties: '^x-': {} additionalProperties: false Discriminator: type: object required: - propertyName properties: propertyName: type: string mapping: type: object additionalProperties: type: string XML: type: object properties: name: type: string namespace: type: string format: uri prefix: type: string attribute: type: boolean default: false wrapped: type: boolean default: false patternProperties: '^x-': {} additionalProperties: false Response: type: object required: - description properties: description: type: string headers: type: object additionalProperties: oneOf: - $ref: '#/definitions/Header' - $ref: '#/definitions/Reference' content: type: object additionalProperties: $ref: '#/definitions/MediaType' links: type: object additionalProperties: oneOf: - $ref: '#/definitions/Link' - $ref: '#/definitions/Reference' patternProperties: '^x-': {} additionalProperties: false MediaType: type: object properties: schema: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' example: {} examples: type: object additionalProperties: oneOf: - $ref: '#/definitions/Example' - $ref: '#/definitions/Reference' encoding: type: object additionalProperties: $ref: '#/definitions/Encoding' patternProperties: '^x-': {} additionalProperties: false allOf: - $ref: '#/definitions/ExampleXORExamples' Example: type: object properties: summary: type: string description: type: string value: {} externalValue: type: string format: uri-reference patternProperties: '^x-': {} additionalProperties: false Header: type: object properties: description: type: string required: type: boolean default: false deprecated: type: boolean default: false allowEmptyValue: type: boolean default: false style: type: string enum: - simple default: simple explode: type: boolean allowReserved: type: boolean default: false schema: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' content: type: object additionalProperties: $ref: '#/definitions/MediaType' minProperties: 1 maxProperties: 1 example: {} examples: type: object additionalProperties: oneOf: - $ref: '#/definitions/Example' - $ref: '#/definitions/Reference' patternProperties: '^x-': {} additionalProperties: false allOf: - $ref: '#/definitions/ExampleXORExamples' - $ref: '#/definitions/SchemaXORContent' Paths: type: object patternProperties: '^\/': $ref: '#/definitions/PathItem' '^x-': {} additionalProperties: false PathItem: type: object properties: $ref: type: string summary: type: string description: type: string servers: type: array items: $ref: '#/definitions/Server' parameters: type: array items: oneOf: - $ref: '#/definitions/Parameter' - $ref: '#/definitions/Reference' uniqueItems: true patternProperties: '^(get|put|post|delete|options|head|patch|trace)$': $ref: '#/definitions/Operation' '^x-': {} additionalProperties: false Operation: type: object required: - responses properties: tags: type: array items: type: string summary: type: string description: type: string externalDocs: $ref: '#/definitions/ExternalDocumentation' operationId: type: string parameters: type: array items: oneOf: - $ref: '#/definitions/Parameter' - $ref: '#/definitions/Reference' uniqueItems: true requestBody: oneOf: - $ref: '#/definitions/RequestBody' - $ref: '#/definitions/Reference' responses: $ref: '#/definitions/Responses' callbacks: type: object additionalProperties: oneOf: - $ref: '#/definitions/Callback' - $ref: '#/definitions/Reference' deprecated: type: boolean default: false security: type: array items: $ref: '#/definitions/SecurityRequirement' servers: type: array items: $ref: '#/definitions/Server' patternProperties: '^x-': {} additionalProperties: false Responses: type: object properties: default: oneOf: - $ref: '#/definitions/Response' - $ref: '#/definitions/Reference' patternProperties: '^[1-5](?:\d{2}|XX)$': oneOf: - $ref: '#/definitions/Response' - $ref: '#/definitions/Reference' '^x-': {} minProperties: 1 additionalProperties: false SecurityRequirement: type: object additionalProperties: type: array items: type: string Tag: type: object required: - name properties: name: type: string description: type: string externalDocs: $ref: '#/definitions/ExternalDocumentation' patternProperties: '^x-': {} additionalProperties: false ExternalDocumentation: type: object required: - url properties: description: type: string url: type: string format: uri-reference patternProperties: '^x-': {} additionalProperties: false ExampleXORExamples: description: Example and examples are mutually exclusive not: required: [example, examples] SchemaXORContent: description: Schema and content are mutually exclusive, at least one is required not: required: [schema, content] oneOf: - required: [schema] - required: [content] description: Some properties are not allowed if content is present allOf: - not: required: [style] - not: required: [explode] - not: required: [allowReserved] - not: required: [example] - not: required: [examples] Parameter: type: object properties: name: type: string in: type: string description: type: string required: type: boolean default: false deprecated: type: boolean default: false allowEmptyValue: type: boolean default: false style: type: string explode: type: boolean allowReserved: type: boolean default: false schema: oneOf: - $ref: '#/definitions/Schema' - $ref: '#/definitions/Reference' content: type: object additionalProperties: $ref: '#/definitions/MediaType' minProperties: 1 maxProperties: 1 example: {} examples: type: object additionalProperties: oneOf: - $ref: '#/definitions/Example' - $ref: '#/definitions/Reference' patternProperties: '^x-': {} additionalProperties: false required: - name - in allOf: - $ref: '#/definitions/ExampleXORExamples' - $ref: '#/definitions/SchemaXORContent' - $ref: '#/definitions/ParameterLocation' ParameterLocation: description: Parameter location oneOf: - description: Parameter in path required: - required properties: in: enum: [path] style: enum: [matrix, label, simple] default: simple required: enum: [true] - description: Parameter in query properties: in: enum: [query] style: enum: [form, spaceDelimited, pipeDelimited, deepObject] default: form - description: Parameter in header properties: in: enum: [header] style: enum: [simple] default: simple - description: Parameter in cookie properties: in: enum: [cookie] style: enum: [form] default: form RequestBody: type: object required: - content properties: description: type: string content: type: object additionalProperties: $ref: '#/definitions/MediaType' required: type: boolean default: false patternProperties: '^x-': {} additionalProperties: false SecurityScheme: oneOf: - $ref: '#/definitions/APIKeySecurityScheme' - $ref: '#/definitions/HTTPSecurityScheme' - $ref: '#/definitions/OAuth2SecurityScheme' - $ref: '#/definitions/OpenIdConnectSecurityScheme' APIKeySecurityScheme: type: object required: - type - name - in properties: type: type: string enum: - apiKey name: type: string in: type: string enum: - header - query - cookie description: type: string patternProperties: '^x-': {} additionalProperties: false HTTPSecurityScheme: type: object required: - scheme - type properties: scheme: type: string bearerFormat: type: string description: type: string type: type: string enum: - http patternProperties: '^x-': {} additionalProperties: false oneOf: - description: Bearer properties: scheme: enum: [bearer] - description: Non Bearer not: required: [bearerFormat] properties: scheme: not: enum: [bearer] OAuth2SecurityScheme: type: object required: - type - flows properties: type: type: string enum: - oauth2 flows: $ref: '#/definitions/OAuthFlows' description: type: string patternProperties: '^x-': {} additionalProperties: false OpenIdConnectSecurityScheme: type: object required: - type - openIdConnectUrl properties: type: type: string enum: - openIdConnect openIdConnectUrl: type: string format: uri-reference description: type: string patternProperties: '^x-': {} additionalProperties: false OAuthFlows: type: object properties: implicit: $ref: '#/definitions/ImplicitOAuthFlow' password: $ref: '#/definitions/PasswordOAuthFlow' clientCredentials: $ref: '#/definitions/ClientCredentialsFlow' authorizationCode: $ref: '#/definitions/AuthorizationCodeOAuthFlow' patternProperties: '^x-': {} additionalProperties: false ImplicitOAuthFlow: type: object required: - authorizationUrl - scopes properties: authorizationUrl: type: string format: uri-reference refreshUrl: type: string format: uri-reference scopes: type: object additionalProperties: type: string patternProperties: '^x-': {} additionalProperties: false PasswordOAuthFlow: type: object required: - tokenUrl properties: tokenUrl: type: string format: uri-reference refreshUrl: type: string format: uri-reference scopes: type: object additionalProperties: type: string patternProperties: '^x-': {} additionalProperties: false ClientCredentialsFlow: type: object required: - tokenUrl properties: tokenUrl: type: string format: uri-reference refreshUrl: type: string format: uri-reference scopes: type: object additionalProperties: type: string patternProperties: '^x-': {} additionalProperties: false AuthorizationCodeOAuthFlow: type: object required: - authorizationUrl - tokenUrl properties: authorizationUrl: type: string format: uri-reference tokenUrl: type: string format: uri-reference refreshUrl: type: string format: uri-reference scopes: type: object additionalProperties: type: string patternProperties: '^x-': {} additionalProperties: false Link: type: object properties: operationId: type: string operationRef: type: string format: uri-reference parameters: type: object additionalProperties: {} requestBody: {} description: type: string server: $ref: '#/definitions/Server' patternProperties: '^x-': {} additionalProperties: false not: description: Operation Id and Operation Ref are mutually exclusive required: [operationId, operationRef] Callback: type: object additionalProperties: $ref: '#/definitions/PathItem' patternProperties: '^x-': {} Encoding: type: object properties: contentType: type: string headers: type: object additionalProperties: $ref: '#/definitions/Header' style: type: string enum: - form - spaceDelimited - pipeDelimited - deepObject explode: type: boolean allowReserved: type: boolean default: false additionalProperties: false php-openapi/schemas/openapi-v3.0.json 0000644 00000105200 14736103140 0013416 0 ustar 00 { "id": "https://spec.openapis.org/oas/3.0/schema/2019-04-02", "$schema": "http://json-schema.org/draft-04/schema#", "description": "Validation schema for OpenAPI Specification 3.0.X.", "type": "object", "required": [ "openapi", "info", "paths" ], "properties": { "openapi": { "type": "string", "pattern": "^3\\.0\\.\\d(-.+)?$" }, "info": { "$ref": "#/definitions/Info" }, "externalDocs": { "$ref": "#/definitions/ExternalDocumentation" }, "servers": { "type": "array", "items": { "$ref": "#/definitions/Server" } }, "security": { "type": "array", "items": { "$ref": "#/definitions/SecurityRequirement" } }, "tags": { "type": "array", "items": { "$ref": "#/definitions/Tag" }, "uniqueItems": true }, "paths": { "$ref": "#/definitions/Paths" }, "components": { "$ref": "#/definitions/Components" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false, "definitions": { "Reference": { "type": "object", "required": [ "$ref" ], "patternProperties": { "^\\$ref$": { "type": "string", "format": "uri-reference" } } }, "Info": { "type": "object", "required": [ "title", "version" ], "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "termsOfService": { "type": "string", "format": "uri-reference" }, "contact": { "$ref": "#/definitions/Contact" }, "license": { "$ref": "#/definitions/License" }, "version": { "type": "string" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Contact": { "type": "object", "properties": { "name": { "type": "string" }, "url": { "type": "string", "format": "uri-reference" }, "email": { "type": "string", "format": "email" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "License": { "type": "object", "required": [ "name" ], "properties": { "name": { "type": "string" }, "url": { "type": "string", "format": "uri-reference" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Server": { "type": "object", "required": [ "url" ], "properties": { "url": { "type": "string" }, "description": { "type": "string" }, "variables": { "type": "object", "additionalProperties": { "$ref": "#/definitions/ServerVariable" } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "ServerVariable": { "type": "object", "required": [ "default" ], "properties": { "enum": { "type": "array", "items": { "type": "string" } }, "default": { "type": "string" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Components": { "type": "object", "properties": { "schemas": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] } } }, "responses": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/Response" } ] } } }, "parameters": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/Parameter" } ] } } }, "examples": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/Example" } ] } } }, "requestBodies": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/RequestBody" } ] } } }, "headers": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/Header" } ] } } }, "securitySchemes": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/SecurityScheme" } ] } } }, "links": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/Link" } ] } } }, "callbacks": { "type": "object", "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "oneOf": [ { "$ref": "#/definitions/Reference" }, { "$ref": "#/definitions/Callback" } ] } } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Schema": { "type": "object", "properties": { "title": { "type": "string" }, "multipleOf": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "maxLength": { "type": "integer", "minimum": 0 }, "minLength": { "type": "integer", "minimum": 0, "default": 0 }, "pattern": { "type": "string", "format": "regex" }, "maxItems": { "type": "integer", "minimum": 0 }, "minItems": { "type": "integer", "minimum": 0, "default": 0 }, "uniqueItems": { "type": "boolean", "default": false }, "maxProperties": { "type": "integer", "minimum": 0 }, "minProperties": { "type": "integer", "minimum": 0, "default": 0 }, "required": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true }, "enum": { "type": "array", "items": { }, "minItems": 1, "uniqueItems": false }, "type": { "type": "string", "enum": [ "array", "boolean", "integer", "number", "object", "string" ] }, "not": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] }, "allOf": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] } }, "oneOf": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] } }, "anyOf": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] } }, "items": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] }, "properties": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] } }, "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" }, { "type": "boolean" } ], "default": true }, "description": { "type": "string" }, "format": { "type": "string" }, "default": { }, "nullable": { "type": "boolean", "default": false }, "discriminator": { "$ref": "#/definitions/Discriminator" }, "readOnly": { "type": "boolean", "default": false }, "writeOnly": { "type": "boolean", "default": false }, "example": { }, "externalDocs": { "$ref": "#/definitions/ExternalDocumentation" }, "deprecated": { "type": "boolean", "default": false }, "xml": { "$ref": "#/definitions/XML" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Discriminator": { "type": "object", "required": [ "propertyName" ], "properties": { "propertyName": { "type": "string" }, "mapping": { "type": "object", "additionalProperties": { "type": "string" } } } }, "XML": { "type": "object", "properties": { "name": { "type": "string" }, "namespace": { "type": "string", "format": "uri" }, "prefix": { "type": "string" }, "attribute": { "type": "boolean", "default": false }, "wrapped": { "type": "boolean", "default": false } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Response": { "type": "object", "required": [ "description" ], "properties": { "description": { "type": "string" }, "headers": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Header" }, { "$ref": "#/definitions/Reference" } ] } }, "content": { "type": "object", "additionalProperties": { "$ref": "#/definitions/MediaType" } }, "links": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Link" }, { "$ref": "#/definitions/Reference" } ] } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "MediaType": { "type": "object", "properties": { "schema": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] }, "example": { }, "examples": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Example" }, { "$ref": "#/definitions/Reference" } ] } }, "encoding": { "type": "object", "additionalProperties": { "$ref": "#/definitions/Encoding" } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false, "allOf": [ { "$ref": "#/definitions/ExampleXORExamples" } ] }, "Example": { "type": "object", "properties": { "summary": { "type": "string" }, "description": { "type": "string" }, "value": { }, "externalValue": { "type": "string", "format": "uri-reference" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Header": { "type": "object", "properties": { "description": { "type": "string" }, "required": { "type": "boolean", "default": false }, "deprecated": { "type": "boolean", "default": false }, "allowEmptyValue": { "type": "boolean", "default": false }, "style": { "type": "string", "enum": [ "simple" ], "default": "simple" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean", "default": false }, "schema": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] }, "content": { "type": "object", "additionalProperties": { "$ref": "#/definitions/MediaType" }, "minProperties": 1, "maxProperties": 1 }, "example": { }, "examples": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Example" }, { "$ref": "#/definitions/Reference" } ] } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false, "allOf": [ { "$ref": "#/definitions/ExampleXORExamples" }, { "$ref": "#/definitions/SchemaXORContent" } ] }, "Paths": { "type": "object", "patternProperties": { "^\\/": { "$ref": "#/definitions/PathItem" }, "^x-": { } }, "additionalProperties": false }, "PathItem": { "type": "object", "properties": { "$ref": { "type": "string" }, "summary": { "type": "string" }, "description": { "type": "string" }, "servers": { "type": "array", "items": { "$ref": "#/definitions/Server" } }, "parameters": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/Parameter" }, { "$ref": "#/definitions/Reference" } ] }, "uniqueItems": true } }, "patternProperties": { "^(get|put|post|delete|options|head|patch|trace)$": { "$ref": "#/definitions/Operation" }, "^x-": { } }, "additionalProperties": false }, "Operation": { "type": "object", "required": [ "responses" ], "properties": { "tags": { "type": "array", "items": { "type": "string" } }, "summary": { "type": "string" }, "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/ExternalDocumentation" }, "operationId": { "type": "string" }, "parameters": { "type": "array", "items": { "oneOf": [ { "$ref": "#/definitions/Parameter" }, { "$ref": "#/definitions/Reference" } ] }, "uniqueItems": true }, "requestBody": { "oneOf": [ { "$ref": "#/definitions/RequestBody" }, { "$ref": "#/definitions/Reference" } ] }, "responses": { "$ref": "#/definitions/Responses" }, "callbacks": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Callback" }, { "$ref": "#/definitions/Reference" } ] } }, "deprecated": { "type": "boolean", "default": false }, "security": { "type": "array", "items": { "$ref": "#/definitions/SecurityRequirement" } }, "servers": { "type": "array", "items": { "$ref": "#/definitions/Server" } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Responses": { "type": "object", "properties": { "default": { "oneOf": [ { "$ref": "#/definitions/Response" }, { "$ref": "#/definitions/Reference" } ] } }, "patternProperties": { "^[1-5](?:\\d{2}|XX)$": { "oneOf": [ { "$ref": "#/definitions/Response" }, { "$ref": "#/definitions/Reference" } ] }, "^x-": { } }, "minProperties": 1, "additionalProperties": false }, "SecurityRequirement": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "Tag": { "type": "object", "required": [ "name" ], "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/ExternalDocumentation" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "ExternalDocumentation": { "type": "object", "required": [ "url" ], "properties": { "description": { "type": "string" }, "url": { "type": "string", "format": "uri-reference" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "ExampleXORExamples": { "description": "Example and examples are mutually exclusive", "not": { "required": [ "example", "examples" ] } }, "SchemaXORContent": { "description": "Schema and content are mutually exclusive, at least one is required", "not": { "required": [ "schema", "content" ] }, "oneOf": [ { "required": [ "schema" ] }, { "required": [ "content" ], "description": "Some properties are not allowed if content is present", "allOf": [ { "not": { "required": [ "style" ] } }, { "not": { "required": [ "explode" ] } }, { "not": { "required": [ "allowReserved" ] } }, { "not": { "required": [ "example" ] } }, { "not": { "required": [ "examples" ] } } ] } ] }, "Parameter": { "type": "object", "properties": { "name": { "type": "string" }, "in": { "type": "string" }, "description": { "type": "string" }, "required": { "type": "boolean", "default": false }, "deprecated": { "type": "boolean", "default": false }, "allowEmptyValue": { "type": "boolean", "default": false }, "style": { "type": "string" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean", "default": false }, "schema": { "oneOf": [ { "$ref": "#/definitions/Schema" }, { "$ref": "#/definitions/Reference" } ] }, "content": { "type": "object", "additionalProperties": { "$ref": "#/definitions/MediaType" }, "minProperties": 1, "maxProperties": 1 }, "example": { }, "examples": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/Example" }, { "$ref": "#/definitions/Reference" } ] } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false, "required": [ "name", "in" ], "allOf": [ { "$ref": "#/definitions/ExampleXORExamples" }, { "$ref": "#/definitions/SchemaXORContent" }, { "$ref": "#/definitions/ParameterLocation" } ] }, "ParameterLocation": { "description": "Parameter location", "oneOf": [ { "description": "Parameter in path", "required": [ "required" ], "properties": { "in": { "enum": [ "path" ] }, "style": { "enum": [ "matrix", "label", "simple" ], "default": "simple" }, "required": { "enum": [ true ] } } }, { "description": "Parameter in query", "properties": { "in": { "enum": [ "query" ] }, "style": { "enum": [ "form", "spaceDelimited", "pipeDelimited", "deepObject" ], "default": "form" } } }, { "description": "Parameter in header", "properties": { "in": { "enum": [ "header" ] }, "style": { "enum": [ "simple" ], "default": "simple" } } }, { "description": "Parameter in cookie", "properties": { "in": { "enum": [ "cookie" ] }, "style": { "enum": [ "form" ], "default": "form" } } } ] }, "RequestBody": { "type": "object", "required": [ "content" ], "properties": { "description": { "type": "string" }, "content": { "type": "object", "additionalProperties": { "$ref": "#/definitions/MediaType" } }, "required": { "type": "boolean", "default": false } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "SecurityScheme": { "oneOf": [ { "$ref": "#/definitions/APIKeySecurityScheme" }, { "$ref": "#/definitions/HTTPSecurityScheme" }, { "$ref": "#/definitions/OAuth2SecurityScheme" }, { "$ref": "#/definitions/OpenIdConnectSecurityScheme" } ] }, "APIKeySecurityScheme": { "type": "object", "required": [ "type", "name", "in" ], "properties": { "type": { "type": "string", "enum": [ "apiKey" ] }, "name": { "type": "string" }, "in": { "type": "string", "enum": [ "header", "query", "cookie" ] }, "description": { "type": "string" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "HTTPSecurityScheme": { "type": "object", "required": [ "scheme", "type" ], "properties": { "scheme": { "type": "string" }, "bearerFormat": { "type": "string" }, "description": { "type": "string" }, "type": { "type": "string", "enum": [ "http" ] } }, "patternProperties": { "^x-": { } }, "additionalProperties": false, "oneOf": [ { "description": "Bearer", "properties": { "scheme": { "enum": [ "bearer" ] } } }, { "description": "Non Bearer", "not": { "required": [ "bearerFormat" ] }, "properties": { "scheme": { "not": { "enum": [ "bearer" ] } } } } ] }, "OAuth2SecurityScheme": { "type": "object", "required": [ "type", "flows" ], "properties": { "type": { "type": "string", "enum": [ "oauth2" ] }, "flows": { "$ref": "#/definitions/OAuthFlows" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "OpenIdConnectSecurityScheme": { "type": "object", "required": [ "type", "openIdConnectUrl" ], "properties": { "type": { "type": "string", "enum": [ "openIdConnect" ] }, "openIdConnectUrl": { "type": "string", "format": "uri-reference" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "OAuthFlows": { "type": "object", "properties": { "implicit": { "$ref": "#/definitions/ImplicitOAuthFlow" }, "password": { "$ref": "#/definitions/PasswordOAuthFlow" }, "clientCredentials": { "$ref": "#/definitions/ClientCredentialsFlow" }, "authorizationCode": { "$ref": "#/definitions/AuthorizationCodeOAuthFlow" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "ImplicitOAuthFlow": { "type": "object", "required": [ "authorizationUrl", "scopes" ], "properties": { "authorizationUrl": { "type": "string", "format": "uri-reference" }, "refreshUrl": { "type": "string", "format": "uri-reference" }, "scopes": { "type": "object", "additionalProperties": { "type": "string" } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "PasswordOAuthFlow": { "type": "object", "required": [ "tokenUrl" ], "properties": { "tokenUrl": { "type": "string", "format": "uri-reference" }, "refreshUrl": { "type": "string", "format": "uri-reference" }, "scopes": { "type": "object", "additionalProperties": { "type": "string" } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "ClientCredentialsFlow": { "type": "object", "required": [ "tokenUrl" ], "properties": { "tokenUrl": { "type": "string", "format": "uri-reference" }, "refreshUrl": { "type": "string", "format": "uri-reference" }, "scopes": { "type": "object", "additionalProperties": { "type": "string" } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "AuthorizationCodeOAuthFlow": { "type": "object", "required": [ "authorizationUrl", "tokenUrl" ], "properties": { "authorizationUrl": { "type": "string", "format": "uri-reference" }, "tokenUrl": { "type": "string", "format": "uri-reference" }, "refreshUrl": { "type": "string", "format": "uri-reference" }, "scopes": { "type": "object", "additionalProperties": { "type": "string" } } }, "patternProperties": { "^x-": { } }, "additionalProperties": false }, "Link": { "type": "object", "properties": { "operationId": { "type": "string" }, "operationRef": { "type": "string", "format": "uri-reference" }, "parameters": { "type": "object", "additionalProperties": { } }, "requestBody": { }, "description": { "type": "string" }, "server": { "$ref": "#/definitions/Server" } }, "patternProperties": { "^x-": { } }, "additionalProperties": false, "not": { "description": "Operation Id and Operation Ref are mutually exclusive", "required": [ "operationId", "operationRef" ] } }, "Callback": { "type": "object", "additionalProperties": { "$ref": "#/definitions/PathItem" }, "patternProperties": { "^x-": { } } }, "Encoding": { "type": "object", "properties": { "contentType": { "type": "string" }, "headers": { "type": "object", "additionalProperties": { "$ref": "#/definitions/Header" } }, "style": { "type": "string", "enum": [ "form", "spaceDelimited", "pipeDelimited", "deepObject" ] }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean", "default": false } }, "additionalProperties": false } } }