Completed
Push — master ( c58e8a...f7b9f0 )
by John
04:53
created

OpenApiRouteLoader::resolveRequirements()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
rs 9.2
cc 4
eloc 7
nc 3
nop 1
1
<?php declare(strict_types = 1);
2
/*
3
 * This file is part of the KleijnWeb\SwaggerBundle package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace KleijnWeb\SwaggerBundle\Routing;
10
11
use KleijnWeb\PhpApi\Descriptions\Description\Operation;
12
use KleijnWeb\PhpApi\Descriptions\Description\Parameter;
13
use KleijnWeb\PhpApi\Descriptions\Description\Repository;
14
use KleijnWeb\PhpApi\Descriptions\Description\Schema\ScalarSchema;
15
use KleijnWeb\PhpApi\Middleware\Util\ParameterTypePatternResolver;
16
use KleijnWeb\SwaggerBundle\EventListener\Request\RequestMeta;
17
use Symfony\Component\Config\Loader\Loader;
18
use Symfony\Component\Routing\Route;
19
use Symfony\Component\Routing\RouteCollection;
20
21
/**
22
 * @author John Kleijn <[email protected]>
23
 */
24
class OpenApiRouteLoader extends Loader
25
{
26
    /**
27
     * @var array
28
     */
29
    private $descriptions = [];
30
31
    /**
32
     * @var Repository
33
     */
34
    private $repository;
35
36
    /**
37
     * @var ParameterTypePatternResolver
38
     */
39
    private $typePatternResolver;
40
41
    /**
42
     * @param Repository $repository
43
     */
44
    public function __construct(Repository $repository)
45
    {
46
        $this->repository          = $repository;
47
        $this->typePatternResolver = new ParameterTypePatternResolver();
48
    }
49
50
    /**
51
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
52
     *
53
     * @param mixed  $resource
54
     * @param string $type
55
     *
56
     * @return bool
57
     */
58
    public function supports($resource, $type = null)
59
    {
60
        return 'swagger' === $type;
61
    }
62
63
    /**
64
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
65
     *
66
     * @param mixed $resource
67
     * @param null  $type
68
     *
69
     * @return RouteCollection
70
     */
71
    public function load($resource, $type = null): RouteCollection
72
    {
73
        $resource = (string)$resource;
74
        if (in_array($resource, $this->descriptions)) {
75
            throw new \RuntimeException("Resource '$resource' was already loaded");
76
        }
77
78
        $description = $this->repository->get($resource);
79
80
        $routes           = new RouteCollection();
81
        $router           = $description->getExtension('router') ?: 'swagger.controller';
82
        $routerController = $description->getExtension('router-controller');
83
84
        foreach ($description->getPaths() as $pathItem) {
85
            $relativePath = ltrim($pathItem->getPath(), '/');
86
            $resourceName = strpos($relativePath, '/')
87
                ? substr($relativePath, 0, strpos($relativePath, '/'))
88
                : $relativePath;
89
90
            $routerController = $pathItem->getExtension('router-controller') ?: $routerController;
91
92
            foreach ($pathItem->getOperations() as $operation) {
93
                $controllerKey = $this->resolveControllerKey(
94
                    $operation,
95
                    $resourceName,
96
                    $router,
97
                    $routerController
98
                );
99
                $defaults      = [
100
                    '_controller'               => $controllerKey,
101
                    RequestMeta::ATTRIBUTE_URI  => $resource,
102
                    RequestMeta::ATTRIBUTE_PATH => $pathItem->getPath(),
103
                ];
104
105
                $route = new Route(
106
                    $pathItem->getPath(),
107
                    $defaults,
108
                    $this->resolveRequirements($operation),
109
                    [],
110
                    '',
111
                    $description->getSchemes()
112
                );
113
                $route->setMethods($operation->getMethod());
114
                $routes->add($this->createRouteId($resource, $pathItem->getPath(), $controllerKey), $route);
115
            }
116
        }
117
118
        $this->descriptions[] = $resource;
119
120
        return $routes;
121
    }
122
123
    /**
124
     * @param Operation $operation
125
     *
126
     * @return array
127
     */
128
    private function resolveRequirements(Operation $operation): array
129
    {
130
        $requirements = [];
131
132
        foreach ($operation->getParameters() as $parameter) {
133
            if ($parameter->getIn() === Parameter::IN_PATH
134
                && ($schema = $parameter->getSchema()) instanceof ScalarSchema
135
            ) {
136
                $requirements[$parameter->getName()] = $this->typePatternResolver->resolve($schema);
137
            }
138
        }
139
140
        return $requirements;
141
    }
142
143
    /**
144
     * @param Operation   $operation
145
     * @param string      $resourceName
146
     * @param string      $router
147
     * @param string|null $routerController
148
     *
149
     * @return string
150
     */
151
    private function resolveControllerKey(
152
        Operation $operation,
153
        string $resourceName,
154
        string $router,
155
        string $routerController = null
156
    ): string
157
    {
158
159
        $operationName = $operation->getMethod();
160
        $diKey         = "$router.$resourceName";
161
162
        if (0 !== strpos($operation->getId(), '/')) {
163
            if (false !== strpos($operation->getId(), ':')) {
164
                return $operation->getId();
165
            }
166
            $operationName = $operation->getId();
167
        }
168
169
        if ($controller = $operation->getExtension('router-controller')) {
170
            $diKey = $controller;
171
        } elseif ($routerController) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $routerController of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
172
            $diKey = $routerController;
173
        }
174
175
        if ($controllerMethod = $operation->getExtension('router-controller-method')) {
176
            $operationName = $controllerMethod;
177
        }
178
179
        return "$diKey:$operationName";
180
    }
181
182
    /**
183
     * @param string $resource
184
     * @param string $path
185
     *
186
     * @param string $controllerKey
187
     *
188
     * @return string
189
     */
190
    private function createRouteId(string $resource, string $path, string $controllerKey): string
191
    {
192
        list(, $operationName) = explode(':', $controllerKey);
193
        $fileName       = pathinfo($resource, PATHINFO_FILENAME);
194
        $normalizedPath = strtolower(trim(preg_replace('/\W+/', '.', $path), '.'));
195
        $routeName      = "swagger.{$fileName}.$normalizedPath.$operationName";
196
197
        return $routeName;
198
    }
199
}
200