Server IP : 213.176.29.180  /  Your IP : 3.145.37.211
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/.trash/../mail/../public_html/

[  Home  ][  C0mmand  ][  Upload File  ]

Current File : /home/webtaragh/.trash/../mail/../public_html/cebe.tar
php-openapi/bin/php-openapi000064400000036115147361031400011704 0ustar00#!/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.php000064400000022174147361031400013534 0ustar00<?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.php000064400000002512147361031400014125 0ustar00<?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.php000064400000000414147361031400017526 0ustar00<?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.php000064400000000566147361031400016265 0ustar00<?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.php000064400000000671147361031400014636 0ustar00<?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.php000064400000001121147361031400020576 0ustar00<?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.php000064400000000501147361031400017641 0ustar00<?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.php000064400000004212147361031400011536 0ustar00<?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.php000064400000001004147361031400021436 0ustar00<?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.php000064400000011121147361031400013760 0ustar00<?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.php000064400000012770147361031400013515 0ustar00<?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.php000064400000000737147361031400020630 0ustar00<?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.php000064400000000734147361031400020107 0ustar00<?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.php000064400000022713147361031400013203 0ustar00<?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.php000064400000002056147361031400011733 0ustar00<?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.php000064400000021156147361031400012301 0ustar00<?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.php000064400000003772147361031400013166 0ustar00<?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.php000064400000002050147361031400014126 0ustar00<?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.php000064400000010571147361031400013141 0ustar00<?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.php000064400000002007147361031400012462 0ustar00<?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.php000064400000001674147361031400012607 0ustar00<?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.php000064400000014030147361031400012413 0ustar00<?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.php000064400000001627147361031400015253 0ustar00<?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.php000064400000035245147361031400013124 0ustar00<?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.php000064400000002305147361031400012405 0ustar00<?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.php000064400000001762147361031400012616 0ustar00<?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.php000064400000005070147361031400014153 0ustar00<?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.php000064400000002017147361031400013463 0ustar00<?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.php000064400000002141147361031400014022 0ustar00<?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.php000064400000006202147361031400012743 0ustar00<?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.php000064400000002556147361031400012120 0ustar00<?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.php000064400000002037147361031400012140 0ustar00<?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.php000064400000002000147361031400015521 0ustar00<?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.php000064400000002353147361031400013016 0ustar00<?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.php000064400000002647147361031400013076 0ustar00<?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.php000064400000002140147361031400011752 0ustar00<?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.php000064400000017171147361031400012737 0ustar00<?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.php000064400000004731147361031400013347 0ustar00<?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.php000064400000004701147361031400012552 0ustar00<?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.php000064400000002154147361031400013252 0ustar00<?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.php000064400000005324147361031400013102 0ustar00<?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.php000064400000001767147361031400012623 0ustar00<?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.php000064400000012143147361031400012712 0ustar00<?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.php000064400000002721147361031400012114 0ustar00<?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.php000064400000001613147361031400014453 0ustar00<?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.php000064400000042267147361031400013112 0ustar00<?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.php000064400000016773147361031400011503 0ustar00<?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.php000064400000002733147361031400015234 0ustar00<?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.md000064400000023636147361031400010254 0ustar00# 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/LICENSE000064400000002117147361031400007771 0ustar00MIT 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.json000064400000000064147361031400011251 0ustar00{
  "dependencies": {
    "speccy": "^0.11.0"
  }
}
php-openapi/schemas/openapi-v3.0.yaml000064400000053051147361031400013415 0ustar00id: 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.json000064400000105200147361031400013416 0ustar00{
  "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
    }
  }
}