Test Setup Failed
Push — master ( 3034b4...e337a2 )
by Mehmet
06:43
created

Router::__call()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 4
cp 0
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 2
1
<?php
2
/**
3
 * Selami Router
4
 * PHP version 7.1+
5
 *
6
 * @category Library
7
 * @package  Router
8
 * @author   Mehmet Korkmaz <[email protected]>
9
 * @license  https://github.com/selamiphp/router/blob/master/LICENSE (MIT License)
10
 * @link     https://github.com/selamiphp/router
11
 */
12
13
declare(strict_types = 1);
14
15
namespace Selami\Router;
16
17
use InvalidArgumentException;
18
use UnexpectedValueException;
19
use RuntimeException;
20
21
/**
22
 * Router
23
 *
24
 * This class is responsible for registering route objects,
25
 * determining aliases if available and finding requested route
26
 */
27
final class Router
28
{
29
    public const HTML = 1;
30
    public const JSON = 2;
31
    public const TEXT = 3;
32
    public const XML = 4;
33
    public const REDIRECT = 5;
34
    public const DOWNLOAD = 6;
35
    public const CUSTOM = 7;
36
37
    public const OPTIONS = 'OPTIONS';
38
    public const HEAD = 'HEAD';
39
    public const GET = 'GET';
40
    public const POST = 'POST';
41
    public const PUT = 'PUT';
42
    public const DELETE = 'DELETE';
43
    public const PATCH = 'PATCH';
44
45
    /**
46
     * Routes array to be registered.
47
     * Some routes may have aliases to be used in templating system
48
     * Route item can be defined using array key as an alias key.
49
     * Each route item is an array has items respectively: Request Method, Request Uri, Controller/Action, Return Type.
50
     *
51
     * @var array
52
     */
53
    private $routes = [];
54
55
    /**
56
     * Aliases array to be registered.
57
     *
58
     * @var array
59
     */
60
    private $aliases = [];
61
62
    /**
63
     * HTTP request Method
64
     *
65
     * @var string
66
     */
67
    private $method;
68
69
    /**
70
     * Request Uri
71
     *
72
     * @var string
73
     */
74
    private $requestedPath;
75
76
    /**
77
     * Default return type if not noted in the $routes
78
     *
79
     * @var int
80
     */
81
    private $defaultReturnType;
82
83
    /**
84
     * @var null|string
85
     */
86
    private $cachedFile;
87
88
    /**
89
     * Valid Request Methods array.
90
     * Make sures about requested methods.
91
     *
92
     * @var array
93
     */
94
    private static $validRequestMethods = [
95
        'GET',
96
        'OPTIONS',
97
        'HEAD',
98
        'POST',
99
        'PUT',
100
        'DELETE',
101
        'PATCH'
102
    ];
103
104
    /*
105
     * Router constructor.
106
     * Create new router.
107
108
     */
109
    public function __construct(
110
        int $defaultReturnType,
111
        string $method,
112
        string $requestedPath,
113
        string $folder = '',
114
        ?string $cachedFile = null
115
    ) {
116
        if (!in_array($method, self::$validRequestMethods, true)) {
117
            $message = sprintf('%s is not valid Http request method.', $method);
118
            throw new UnexpectedValueException($message);
119
        }
120
        $this->method = $method;
121
        $this->requestedPath = $this->extractFolder($requestedPath, $folder);
122
        $this->defaultReturnType = ($defaultReturnType >=1 && $defaultReturnType <=7) ? $defaultReturnType : self::HTML;
123
        $this->cachedFile = $cachedFile;
124
    }
125
126
    /*
127
     * Remove sub folder from requestedPath if defined
128
     */
129
    private function extractFolder(string $requestPath, string $folder) : string
130
    {
131
        if (!empty($folder)) {
132
            $requestPath = '/' . trim((string) preg_replace('#^/' . $folder . '#msi', '/', $requestPath), '/');
133
        }
134
        if ($requestPath === '') {
135
            $requestPath = '/';
136
        }
137
        return $requestPath;
138
    }
139
140
141
    public function add(
142
        $requestMethods,
143
        string $route,
144
        $action,
145
        ?int $returnType = null,
146
        ?string $alias = null
147
    ) : void {
148
    
149
        $requestMethodsGiven = is_array($requestMethods) ? $requestMethods : [$requestMethods];
150
        $returnType = $this->determineReturnType($returnType);
151
        foreach ($requestMethodsGiven as $requestMethod) {
152
153
            $this->checkRequestMethodIsValid($requestMethod);
154
            if ($alias !== null) {
155
                $this->aliases[$alias] = $route;
156
            }
157
            $this->routes[] = [strtoupper($requestMethod), $route, $action, $returnType];
158
        }
159
    }
160
161
162
    public function __call(string $method, array $args) : void
163
    {
164
        $defaults = [
165
            null,
166
            null,
167
            $this->defaultReturnType,
168
            null
169
        ];
170
        [$route, $action, $returnType, $alias] = array_merge($args, $defaults);
171
        $this->add($method, $route, $action, $returnType, $alias);
172
    }
173
174
175
    private function determineReturnType(?int $returnType) : int
176
    {
177
        if ($returnType === null) {
178
            return $this->defaultReturnType;
179
        }
180
        return ($returnType >=1 && $returnType <=7) ? $returnType : self::HTML;
181
    }
182
183
    private function checkRequestMethodIsValid(string $requestMethod) : void
184
    {
185
        if (!in_array(strtoupper($requestMethod), self::$validRequestMethods, true)) {
186
            $message = sprintf('%s is not valid Http request method.', $requestMethod);
187
            throw new UnexpectedValueException($message);
188
        }
189
    }
190
191
    public function getRoute() : Route
192
    {
193
        $selamiDispatcher = new Dispatcher($this->routes, $this->defaultReturnType, $this->cachedFile);
194
        $routeInfo = $selamiDispatcher->dispatcher()
195
            ->dispatch($this->method, $this->requestedPath);
196
        return $selamiDispatcher->runDispatcher($routeInfo)
197
            ->withAliases($this->aliases);
198
    }
199
}
200