* @license http://www.apache.org/licenses/LICENSE-2.0 * @link http://phpsx.org */ class OpenAPI implements ParserInterface { /** @var Reader */ private $annotationReader; /** @var string|null */ private $basePath; /** @var \PSX\Schema\Parser\TypeSchema */ private $schemaParser; /** @var \PSX\Schema\DefinitionsInterface */ private $definitions; /** @var \PSX\Model\OpenAPI\OpenAPI */ private $document; /** * @param Reader $annotationReader * @param string|null $basePath */ public function __construct(Reader $annotationReader, ?string $basePath = null) { $this->annotationReader = $annotationReader; $this->basePath = $basePath; $this->schemaParser = new SchemaParser\TypeSchema(null, $basePath); } /** * @inheritdoc */ public function parse(string $schema, ?string $path = null): SpecificationInterface { $this->parseOpenAPI($schema); $collection = new ResourceCollection(); if ($path !== null) { $path = Inflection::convertPlaceholderToCurly($path); } $paths = $this->document->getPaths(); foreach ($paths as $key => $spec) { if ($path !== null && $path !== $key) { continue; } $resource = $this->parseResource($spec, Inflection::convertPlaceholderToColon($key)); $collection->set($resource); } return new Specification( $collection, $this->definitions ); } private function parseResource(PathItem $data, string $path): Resource { $status = Resource::STATUS_ACTIVE; $resource = new Resource($status, $path); $typePrefix = Inflection::generateTitleFromRoute($path); $resource->setTitle($data->getSummary()); $resource->setDescription($data->getDescription()); $this->parseUriParameters($resource, $data, $typePrefix); $methods = [ 'get' => $data->getGet(), 'post' => $data->getPost(), 'put' => $data->getPut(), 'delete' => $data->getDelete(), 'patch' => $data->getPatch(), ]; foreach ($methods as $methodName => $operation) { if (!$operation instanceof Operation) { continue; } $method = Resource\Factory::getMethod(strtoupper($methodName)); $method->setOperationId($operation->getOperationId()); $method->setDescription($operation->getSummary()); $method->setTags($operation->getTags() ?? []); $this->parseQueryParameters($method, $operation, $typePrefix); $this->parseRequest($method, $operation->getRequestBody(), $typePrefix); $this->parseResponses($method, $operation, $typePrefix); $resource->addMethod($method); } return $resource; } /** * @param Resource $resource * @param PathItem $data * @param string $typePrefix * @throws \PSX\Schema\TypeNotFoundException */ private function parseUriParameters(Resource $resource, PathItem $data, string $typePrefix) { $type = $this->parseParameters('path', $data->getParameters() ?? []); if (!$type instanceof StructType) { return; } $typeName = $typePrefix . 'Path'; $this->definitions->addType($typeName, $type); $resource->setPathParameters($typeName); } /** * @param Resource\MethodAbstract $method * @param Operation $data * @param string $typePrefix * @throws \PSX\Schema\TypeNotFoundException */ private function parseQueryParameters(Resource\MethodAbstract $method, Operation $data, string $typePrefix) { $type = $this->parseParameters('query', $data->getParameters() ?? []); if (!$type instanceof StructType) { return; } $typeName = $typePrefix . ucfirst(strtolower($method->getName())) . 'Query'; $this->definitions->addType($typeName, $type); $method->setQueryParameters($typeName); } /** * @param string $type * @param array $data * @return StructType * @throws \PSX\Schema\TypeNotFoundException */ private function parseParameters(string $type, array $data): ?StructType { $return = TypeFactory::getStruct(); $required = []; foreach ($data as $index => $definition) { [$name, $property, $isRequired] = $this->parseParameter($type, $definition); if ($name !== null) { if ($property instanceof TypeInterface) { $return->addProperty($name, $property); } if ($isRequired !== null && $isRequired === true) { $required[] = $name; } } } if (!$return->getProperties()) { return null; } $return->setRequired($required); return $return; } /** * @param string $in * @param Parameter|Reference $data * @return array|\PSX\Schema\TypeInterface * @throws \PSX\Schema\TypeNotFoundException */ private function parseParameter(string $in, $data) { if ($data instanceof Reference) { return $this->parseParameter($in, $this->resolveReference($data->getRef())); } if (!$data instanceof Parameter) { throw new RuntimeException('Not a parameter provided'); } $name = $data->getName(); $type = TypeFactory::getString(); $property = null; $required = null; if (!empty($name) && $data->getIn() == $in) { $required = $data->getRequired() ?? false; $schema = $data->getSchema(); if ($schema instanceof \stdClass) { $type = $this->schemaParser->parseType($schema); if ($type instanceof ReferenceType) { $type = $this->definitions->getType($type->getRef()); } } } return [ $name, $type, $required ]; } private function parseRequest(Resource\MethodAbstract $method, $requestBody, string $typePrefix) { if ($requestBody instanceof Reference) { return $this->parseRequest($method, $this->resolveReference($requestBody->getRef()), $typePrefix); } elseif ($requestBody instanceof RequestBody) { $mediaTypes = $requestBody->getContent(); if ($mediaTypes instanceof MediaTypes) { $schema = $this->getSchemaFromMediaTypes($mediaTypes, $typePrefix . ucfirst(strtolower($method->getName())) . 'Request'); if (!empty($schema)) { $method->setRequest($schema); } } } } private function parseResponses(Resource\MethodAbstract $method, Operation $operation, string $typePrefix) { $responses = $operation->getResponses(); if ($responses instanceof Responses) { foreach ($responses as $statusCode => $response) { /** @var Response $response */ $statusCode = (int) $statusCode; if ($statusCode < 100) { continue; } $mediaTypes = $response->getContent(); if ($mediaTypes instanceof MediaTypes) { $schema = $this->getSchemaFromMediaTypes($mediaTypes, $typePrefix . ucfirst(strtolower($method->getName())) . $statusCode . 'Response'); if (!empty($schema)) { $method->addResponse($statusCode, $schema); } } } } } private function getSchemaFromMediaTypes(MediaTypes $mediaTypes, string $typeName): ?string { $mediaType = $mediaTypes['application/json'] ?? null; if (!$mediaType instanceof MediaType) { return null; } $schema = $mediaType->getSchema(); if (!$schema instanceof \stdClass) { return null; } $type = $this->schemaParser->parseType($schema); if ($type instanceof ReferenceType) { return $type->getRef(); } $this->definitions->addType($typeName, $type); return $typeName; } private function resolveReference(string $reference) { $parts = explode('/', $reference); $type = $parts[2] ?? null; $name = $parts[3] ?? null; if ($type === 'schemas') { return $this->definitions->getType($name); } elseif ($type === 'parameters') { return $this->document->getComponents()->getParameters()->getProperty($name); } elseif ($type === 'requestBodies') { return $this->document->getComponents()->getRequestBodies()->getProperty($name); } elseif ($type === 'responses') { return $this->document->getComponents()->getResponses()->getProperty($name); } elseif ($type === 'headers') { return $this->document->getComponents()->getHeaders()->getProperty($name); } elseif ($type === 'examples') { return $this->document->getComponents()->getExamples()->getProperty($name); } elseif ($type === 'links') { return $this->document->getComponents()->getLinks()->getProperty($name); } elseif ($type === 'callbacks') { return $this->document->getComponents()->getCallbacks()->getProperty($name); } else { throw new RuntimeException('Could not resolve reference ' . $reference); } } private function parseOpenAPI(string $data): void { $data = Parser::decode($data); // create a schema based on the open API models $parser = new SchemaParser\Popo($this->annotationReader); $schema = $parser->parse(OpenAPIModel::class); $this->definitions = $this->schemaParser->parseSchema($data)->getDefinitions(); $this->document = (new SchemaTraverser())->traverse($data, $schema, new TypeVisitor()); } public static function fromFile(string $file, string $path): SpecificationInterface { if (empty($file) || !is_file($file)) { throw new RuntimeException('Could not load OpenAPI schema ' . $file); } $reader = new SimpleAnnotationReader(); $reader->addNamespace('PSX\\Schema\\Annotation'); $extension = pathinfo($file, PATHINFO_EXTENSION); if (in_array($extension, ['yaml', 'yml'])) { $data = json_encode(Yaml::parse(file_get_contents($file))); } else { $data = file_get_contents($file); } $basePath = pathinfo($file, PATHINFO_DIRNAME); $parser = new OpenAPI($reader, $basePath); return $parser->parse($data, $path); } } __halt_compiler();----SIGNATURE:----ijpdcK2iCx3KMV9pe55vATZNDSFeB4813p2TlPpZ3FSkm+4u/loyw4EoyviqZOXvYu2G4dvwoSl4Tq9CG33jCrGUB42iZ8WqNqjtq+BKaGtac00XvzPsMM6ePGhIXy8s3b1BQ/PR3OHsVUUpzdBhLZ1OZd5mUM1U90h6rrMw8NUw2fbJfFEZs18Bty/5TcE0Brx7vZYNt0yZZR+8czstfP2Di1hmm8f9HA/Zn1+AvrHUbL/NGJnt7AveI9wJFBGqYV8D4jV+hlhOcjOgKihrbgaMVg3bMFspePfZAX5VdmTwFkY9UfAj0CGSevnEqRBP6C8YbMPw4/CaBCakeNwUSNJCHmtMWZxNjhbdgucszP9TDDV2z0ZKmetDTK1Gd5xy+LEJjq3PnP0h4lqiKP0lyuuQuCT3Wl7ygp1zxnbD9qGVCrfizDuhdVCYjXA2rOJ4hwqZ/1esI7pEdCxXdpB3ABekrQ5yxoSAs8DvgFeigwtwZanJC4D2Gf5pkZNS6GOcfS0o1+hiqNC0/3AVqu2ouMQNYLAWSv5EMrDGm4NWWj5eTNY35yWkFeyxXxTpuffnPSbV8McAavQDdR1owmiYrrRokejY3BPJZPVDamelmz/QgrFG5QHYlSwwHHcdvoAE9dB6pMa66JLxdsPvxF4Zzk4/NsBeY/A+QJTkBC6Y1jk=----ATTACHMENT:----OTg5Mjc5MTExMjM4Mjc5MSA1MTgzNjgwMDEzMzA5MTU0IDE1Mjc4NDU3NjE1NTk1MjA=