'info', Server::class => ['servers'], PathItem::class => ['paths', 'path'], Components::class => 'components', Tag::class => ['tags'], ExternalDocumentation::class => 'externalDocs', ]; /** * @inheritdoc */ public static $_types = []; /** * @inheritdoc */ public function validate(array $parents = null, array $skip = null, string $ref = ''): bool { if ($parents !== null || $skip !== null || $ref !== '') { Logger::notice('Nested validation for ' . $this->identity() . ' not allowed'); return false; } return parent::validate([], [], '#'); } /** * Save the OpenAPI documentation to a file. */ public function saveAs(string $filename, string $format = 'auto'): void { if ($format === 'auto') { $format = strtolower(substr($filename, -5)) === '.json' ? 'json' : 'yaml'; } if (strtolower($format) === 'json') { $content = $this->toJson(); } else { $content = $this->toYaml(); } if (file_put_contents($filename, $content) === false) { throw new \Exception('Failed to saveAs("' . $filename . '", "' . $format . '")'); } } /** * Look up an annotation with a $ref url. * * @param string $ref The $ref value, for example: "#/components/schemas/Product" */ public function ref(string $ref) { if (substr($ref, 0, 2) !== '#/') { // @todo Add support for external (http) refs? throw new \Exception('Unsupported $ref "' . $ref . '", it should start with "#/"'); } return $this->resolveRef($ref, '#/', $this, []); } /** * Recursive helper for ref(). */ private static function resolveRef(string $ref, string $resolved, $container, array $mapping) { if ($ref === $resolved) { return $container; } $path = substr($ref, strlen($resolved)); $slash = strpos($path, '/'); $subpath = $slash === false ? $path : substr($path, 0, $slash); $property = Util::refDecode($subpath); $unresolved = $slash === false ? $resolved . $subpath : $resolved . $subpath . '/'; if (is_object($container)) { if (property_exists($container, $property) === false) { throw new \Exception('$ref "' . $ref . '" not found'); } if ($slash === false) { return $container->$property; } $mapping = []; if ($container instanceof AbstractAnnotation) { foreach ($container::$_nested as $nestedClass => $nested) { if (is_string($nested) === false && count($nested) === 2 && $nested[0] === $property) { $mapping[$nestedClass] = $nested[1]; } } } return self::resolveRef($ref, $unresolved, $container->$property, $mapping); } elseif (is_array($container)) { if (array_key_exists($property, $container)) { return self::resolveRef($ref, $unresolved, $container[$property], []); } foreach ($mapping as $nestedClass => $keyField) { foreach ($container as $key => $item) { if (is_numeric($key) && is_object($item) && $item instanceof $nestedClass && (string) $item->$keyField === $property) { return self::resolveRef($ref, $unresolved, $item, []); } } } } throw new \Exception('$ref "' . $unresolved . '" not found'); } }