Completed
Push — ppetermann/dev-3.0 ( 088cdd )
by Peter
02:07
created

Router::process()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 3
nc 3
nop 2
1
<?php
2
/*
3
 MIT License
4
 Copyright (c) 2010 - 2018 Peter Petermann
5
6
 Permission is hereby granted, free of charge, to any person
7
 obtaining a copy of this software and associated documentation
8
 files (the "Software"), to deal in the Software without
9
 restriction, including without limitation the rights to use,
10
 copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 copies of the Software, and to permit persons to whom the
12
 Software is furnished to do so, subject to the following
13
 conditions:
14
15
 The above copyright notice and this permission notice shall be
16
 included in all copies or substantial portions of the Software.
17
18
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
 OTHER DEALINGS IN THE SOFTWARE.
26
27
*/
28
namespace King23\Http;
29
30
use King23\DI\ContainerInterface;
31
use Psr\Http\Message\ResponseInterface;
32
use Psr\Http\Message\ServerRequestInterface;
33
use Psr\Http\Server\RequestHandlerInterface;
34
use Psr\Log\LoggerInterface;
35
36
/**
37
 * King23_Router class, allowing the matching of URL -> classmethod
38
 */
39
class Router implements RouterInterface
40
{
41
    /**
42
     * Array for storing known routes
43
     *
44
     * @var array
45
     */
46
    protected $routes = [];
47
48
    /**
49
     * String containing the basis host of the application, if this is set
50
     * this parameter will be removed from the hostname before hostparameters are extracted,
51
     * so having a low parameter count won't falsify the parameters by using the basic host as parameters
52
     *
53
     * @var string
54
     */
55
    protected $baseHost = null;
56
57
    /**
58
     * @var LoggerInterface
59
     */
60
    protected $log;
61
62
    /**
63
     * @var ContainerInterface
64
     */
65
    protected $container;
66
67
    /**
68
     * @param LoggerInterface $log
69
     * @param ContainerInterface $container
70
     */
71
    public function __construct(LoggerInterface $log, ContainerInterface $container)
72
    {
73
        $this->log = $log;
74
        $this->container = $container;
75
    }
76
77
    /**
78
     * add route to list of known routes
79
     *
80
     * @param String $route beginning string of the route
81
     * @param String $class to be used for this route
82
     * @param String $action method to be called
83
     * @param string[] $parameters list of parameters that should be retrieved from url
84
     * @param array $hostparameters - allows to use subdomains as parameters
85
     * @return self
86
     */
87
    public function addRoute($route, $class, $action, $parameters = [], $hostparameters = [])
88
    {
89
        $this->log->debug('adding route : ' . $route . ' to class ' . $class . ' and action ' . $action);
90
91
        $this->routes[$route] = [
92
            "class" => $class,
93
            "action" => $action,
94
            "parameters" => $parameters,
95
            "hostparameters" => $hostparameters
96
        ];
97
        return $this;
98
    }
99
100
    /**
101
     * method to set the basicHost for hostparameters in routing
102
     *
103
     * @see King23_Router::$basicHost
104
     * @param String $baseHost
105
     * @return self
106
     */
107
    public function setBaseHost($baseHost = null)
108
    {
109
        $this->log->debug('Setting Router baseHost to ' . $baseHost);
110
        $this->baseHost = $baseHost;
111
        return $this;
112
    }
113
114
    /**
115
     * @param ServerRequestInterface $request
116
     * @param RequestHandlerInterface $next
117
     * @return ResponseInterface
118
     * @throws \King23\Controller\Exceptions\ActionDoesNotExistException
119
     * @throws \King23\DI\Exception\NotFoundException
120
     */
121
    public function process(ServerRequestInterface $request, RequestHandlerInterface $next) : ResponseInterface {
122
        $this->log->debug('Dispatching request for ' . $request->getUri()->getPath());
123
124
        // sort routes
125
        uksort(
126
            $this->routes,
127
            function ($a, $b) {
128
                return strlen($a) < strlen($b);
129
            }
130
        );
131
        foreach ($this->routes as $route => $info) {
132
            // check if route is matched
133
            if (substr($request->getUri()->getPath(), 0, strlen($route)) == $route) {
134
                $this->log->debug('route ' . $route . ' matches ' . $request->getUri()->getPath());
135
                return $this->handleRoute($info, $request, $route);
136
            }
137
        }
138
        return $next->handle($request);
139
    }
140
141
    /**
142
     * Handle a regular route
143
     *
144
     * @param array $info
145
     * @param ServerRequestInterface $request
146
     * @param string $route
147
     * @return ResponseInterface
148
     * @throws \King23\Controller\Exceptions\ActionDoesNotExistException
149
     * @throws \King23\DI\Exception\NotFoundException
150
     */
151
    private function handleRoute($info, ServerRequestInterface $request, $route) : ResponseInterface
152
    {
153
        // initialize router attributes for the request
154
        $attributes = [
155
            'params' => [
156
                'url' => [],
157
                'host' => []
158
            ]
159
        ];
160
161
        // prepare parameters
162
        if ($paramstr = substr($request->getUri()->getPath(), strlen($route))) {
163
            $attributes['params']['url'] = $this->filterParameters($info['parameters'], explode("/", $paramstr));
164
        }
165
166
        // check host parameters
167
        if (count($info["hostparameters"]) > 0) {
168
            $attributes['params']['host'] = $this->extractHostParameters($request, $info);
169
        }
170
171
        // put route parameters into king23.router.parameters attribute
172
        $request = $request->withAttribute('king23.router', $attributes);
173
174
        /** @var \King23\Controller\Controller $controller */
175
        $controller = $this->container->getInstanceOf($info["class"]);
176
177
        return $controller->dispatch($info["action"], $request);
178
    }
179
180
    /**
181
     * @param array $info
182
     * @param array $params
183
     * @return array
184
     */
185
    private function filterParameters($info, $params)
186
    {
187
        $parameters = [];
188
        foreach ($info as $key => $value) {
189
            if (isset($params[$key])) {
190
                $parameters[$value] = urldecode($params[$key]);
191
            } else {
192
                $parameters[$value] = null;
193
            }
194
        }
195
        return $parameters;
196
    }
197
198
    /**
199
     * extract parameters from hostname
200
     *
201
     * @param ServerRequestInterface $request
202
     * @param array $info
203
     * @return array
204
     */
205
    private function extractHostParameters($request, $info)
206
    {
207
        $hostname = $this->cleanHostName($request);
208
209
        if (empty($hostname)) {
210
            $params = [];
211
        } else {
212
            $params = array_reverse(explode(".", $hostname));
213
        }
214
215
        $parameters = $this->filterParameters($info['hostparameters'], $params);
216
        return $parameters;
217
    }
218
219
    /**
220
     * will get hostname, and clean basehost off it
221
     *
222
     * @param ServerRequestInterface $request
223
     * @return string
224
     */
225
    private function cleanHostName(ServerRequestInterface $request)
226
    {
227
228
        if (is_null($this->baseHost)) {
229
            $hostname = $request->getUri()->getHost();
230
        } else {
231
            $hostname = str_replace($this->baseHost, "", $request->getUri()->getHost());
232
        }
233
234
        if (substr($hostname, -1) == ".") {
235
            $hostname = substr($hostname, 0, -1);
236
        }
237
238
        return $hostname;
239
    }
240
}
241