Test Setup Failed
Push — master ( 49b9dc...580cf3 )
by Mehmet
01:46
created

Router::withCacheFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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