OpenApiDocumentManager::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 5
dl 0
loc 7
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Nekhay <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Nekhay
8
 * @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-router
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sunrise\Http\Router\OpenApi;
15
16
use ReflectionAttribute;
17
use RuntimeException;
18
use Sunrise\Coder\CodecManagerInterface;
19
use Sunrise\Http\Router\Helper\ReflectorHelper;
20
use Sunrise\Http\Router\Helper\RouteSimplifier;
21
use Sunrise\Http\Router\OpenApi\Annotation\Operation;
22
use Sunrise\Http\Router\RequestHandlerReflectorInterface;
23
use Sunrise\Http\Router\RouteInterface;
24
use Throwable;
25
26
use function array_replace_recursive;
27
use function array_walk_recursive;
28
use function dirname;
29
use function file_put_contents;
30
use function fopen;
31
use function is_readable;
32
use function is_writable;
33
use function strtolower;
34
35
/**
36
 * @link https://spec.openapis.org/oas/v3.1.0
37
 *
38
 * @since 3.0.0
39
 */
40
final class OpenApiDocumentManager implements OpenApiDocumentManagerInterface
41
{
42 5
    public function __construct(
43
        private readonly OpenApiConfiguration $openApiConfiguration,
44
        private readonly OpenApiPhpTypeSchemaResolverManagerInterface $openApiPhpTypeSchemaResolverManager,
45
        private readonly OpenApiOperationEnricherManagerInterface $openApiOperationEnricherManager,
46
        private readonly RequestHandlerReflectorInterface $requestHandlerReflector,
47
        private readonly CodecManagerInterface $codecManager,
48
    ) {
49 5
    }
50
51
    /**
52
     * @inheritDoc
53
     */
54 3
    public function buildDocument(array $routes): array
55
    {
56 3
        $document = $this->openApiConfiguration->initialDocument;
57
58 3
        foreach ($routes as $route) {
59 3
            $this->describeRoute($route, $document);
60
        }
61
62 3
        $this->openApiPhpTypeSchemaResolverManager->enrichDocumentWithDefinitions($document);
63
64 3
        return $document;
65
    }
66
67
    /**
68
     * @inheritDoc
69
     */
70 4
    public function saveDocument(array $document): void
71
    {
72 4
        $filename = $this->openApiConfiguration->getDocumentFilename();
73
74 4
        if (!is_writable(dirname($filename))) {
75 1
            throw new RuntimeException('The directory for the OpenAPI document is not writable.');
76
        }
77
78 3
        $contents = $this->codecManager->encode(
79 3
            $this->openApiConfiguration->documentMediaType,
80 3
            $document,
81 3
            $this->openApiConfiguration->documentEncodingContext,
82 3
        );
83
84
        try {
85 3
            $result = @file_put_contents($filename, $contents);
86 1
        } catch (Throwable) {
87 1
            $result = false;
88
        }
89
90 3
        if ($result === false) {
91 1
            throw new RuntimeException('The OpenAPI document could not be saved.');
92
        }
93
    }
94
95
    /**
96
     * @inheritDoc
97
     */
98 2
    public function openDocument()
99
    {
100 2
        $filename = $this->openApiConfiguration->getDocumentFilename();
101
102 2
        if (!is_readable($filename)) {
103 1
            throw new RuntimeException('The OpenAPI document was not saved or is unavailable.');
104
        }
105
106
        // @codeCoverageIgnoreStart
107
108
        try {
109
            $result = @fopen($filename, 'rb');
110
        } catch (Throwable) {
111
            $result = false;
112
        }
113
114
        if ($result === false) {
115
            throw new RuntimeException('The OpenAPI document could not be read.');
116
        }
117
118
        // @codeCoverageIgnoreEnd
119
120 1
        return $result;
121
    }
122
123
    /**
124
     * @param array<array-key, mixed> $document
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>.
Loading history...
125
     * @param-out array<array-key, mixed> $document
126
     */
127 3
    private function describeRoute(RouteInterface $route, array &$document): void
128
    {
129 3
        $operation = $this->openApiConfiguration->initialOperation;
130
131 3
        $requestHandler = $this->requestHandlerReflector->reflectRequestHandler($route->getRequestHandler());
132
133 3
        foreach (ReflectorHelper::getAncestry($requestHandler) as $member) {
134
            /** @var ReflectionAttribute<Operation> $annotation */
135 3
            foreach ($member->getAttributes(Operation::class, ReflectionAttribute::IS_INSTANCEOF) as $annotation) {
136 3
                $operation = array_replace_recursive($operation, $annotation->newInstance()->value);
137
            }
138
        }
139
140 3
        array_walk_recursive($operation, function (mixed &$value) use ($requestHandler): void {
141 3
            if ($value instanceof Type) {
142 3
                $value = $this->openApiPhpTypeSchemaResolverManager->resolvePhpTypeSchema($value, $requestHandler);
143
            }
144 3
        });
145
146 3
        $this->openApiOperationEnricherManager->enrichOperation($route, $requestHandler, $operation);
147
148 3
        $operation['operationId'] = $route->getName();
149 3
        $operation['tags'] = $route->getTags();
150 3
        $operation['summary'] = $route->getSummary();
151 3
        $operation['description'] = $route->getDescription();
152
153 3
        if ($route->isDeprecated()) {
154 3
            $operation['deprecated'] = true;
155
        }
156
157 3
        $path = RouteSimplifier::simplifyRoute($route->getPath());
158 3
        foreach ($route->getMethods() as $method) {
159 3
            $document['paths'][$path][strtolower($method)] = $operation;
160
        }
161
    }
162
}
163