MethodOptions::checkAvailableMethods()   B
last analyzed

Complexity

Conditions 9
Paths 7

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 29
ccs 0
cts 17
cp 0
rs 8.0555
c 0
b 0
f 0
cc 9
nc 7
nop 2
crap 90
1
<?php
2
3
namespace kalanis\Restful\Application;
4
5
6
use Nette;
7
use Nette\Http\IRequest;
8
use Nette\Http\Request;
9
use Nette\Http\UrlScript;
10
use Nette\Routing\RouteList;
11
use Nette\Routing\Router;
12
use Traversable;
13
14
15
/**
16
 * MethodOptions
17
 * @package kalanis\Restful\Application
18
 */
19
class MethodOptions
20
{
21
22
    /** @var array<int, string> */
23
    private array $methods = [
24
        IResourceRouter::GET => IRequest::Get,
25
        IResourceRouter::POST => IRequest::Post,
26
        IResourceRouter::PUT => IRequest::Put,
27
        IResourceRouter::DELETE => IRequest::Delete,
28
        IResourceRouter::HEAD => IRequest::Head,
29
        IResourceRouter::PATCH => 'PATCH',
30
        IResourceRouter::OPTIONS => 'OPTIONS',
31
    ];
32
33
    public function __construct(
34
        private readonly Router|RouteList $router,
35
    )
36
    {
37
    }
38
39
    /**
40
     * Get list of available options to given request
41
     * @return string[]
42
     */
43
    public function getOptions(UrlScript $url): array
44
    {
45
        $router = is_a($this->router, RouteList::class)
46
            ? $this->router
47
            : (new RouteList())->add($this->router);
48
        ;
49
        return $this->checkAvailableMethods($router, $url);
50
    }
51
52
    /**
53
     * Recursively checks available methods
54
     * @return string[]
55
     */
56
    private function checkAvailableMethods(Nette\Routing\RouteList $router, UrlScript $url): array
57
    {
58
        $methods = [];
59
        foreach ($router->getRouters() as $route) {
60
            if ($route instanceof IResourceRouter && !$route instanceof Traversable) {
61
                $methodFlag = $this->getMethodFlag($route);
62
                if (empty($methodFlag)) {
63
                    continue;
64
                }
65
66
                $request = $this->createAcceptableRequest($url, $methodFlag);
67
68
                $acceptableMethods = array_keys($route->getActionDictionary());
69
                $methodNames = [];
70
                foreach ($acceptableMethods as $flag) {
71
                    $methodNames[] = $this->methods[$flag];
72
                }
73
74
                if (in_array($route->getMethod($request), $acceptableMethods) && $route->match($request)) {
75
                    return $methodNames;
76
                }
77
            }
78
79
            if (is_a($route, Nette\Routing\RouteList::class)) {
80
                $newMethods = $this->checkAvailableMethods($route, $url);
81
                $methods = array_merge($methods, $newMethods);
82
            }
83
        }
84
        return $methods;
85
    }
86
87
    /**
88
     * Get route method flag
89
     */
90
    protected function getMethodFlag(IResourceRouter $route): ?int
91
    {
92
        foreach ($this->methods as $flag => $requestMethod) {
93
            if ($route->isMethod($flag)) {
94
                return $flag;
95
            }
96
        }
97
        return null;
98
    }
99
100
    /**
101
     * Create route acceptable HTTP request
102
     */
103
    protected function createAcceptableRequest(UrlScript $url, int $methodFlag): Request
104
    {
105
        return new Request(
106
            $url,
107
            [], [], [], [],
108
            $this->methods[$methodFlag]
109
        );
110
    }
111
}
112