Completed
Push — develop ( 2c38c2...5e9332 )
by Peter
02:46
created

Router::handleRoute()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 28
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 28
rs 8.8571
cc 3
eloc 12
nc 4
nop 4
1
<?php
2
/*
3
 MIT License
4
 Copyright (c) 2010 - 2015 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\Log\LoggerInterface;
34
35
/**
36
 * King23_Router class, allowing the matching of URL -> classmethod
37
 */
38
class Router implements RouterInterface
39
{
40
    /**
41
     * Array for storing known routes
42
     *
43
     * @var array
44
     */
45
    protected $routes = [];
46
47
    /**
48
     * String containing the basis host of the application, if this is set
49
     * this parameter will be removed from the hostname before hostparameters are extracted,
50
     * so having a low parameter count won't falsify the parameters by using the basic host as parameters
51
     *
52
     * @var string
53
     */
54
    protected $baseHost = null;
55
56
    /**
57
     * @var LoggerInterface
58
     */
59
    protected $log;
60
61
    /**
62
     * @var ContainerInterface
63
     */
64
    protected $container;
65
66
    /**
67
     * @param LoggerInterface $log
68
     * @param ContainerInterface $container
69
     */
70
    public function __construct(LoggerInterface $log, ContainerInterface $container)
71
    {
72
        $this->log = $log;
73
        $this->container = $container;
74
    }
75
76
    /**
77
     * add route to list of known routes
78
     *
79
     * @param String $route beginning string of the route
80
     * @param String $class to be used for this route
81
     * @param String $action method to be called
82
     * @param string[] $parameters list of parameters that should be retrieved from url
83
     * @param array $hostparameters - allows to use subdomains as parameters
84
     * @return void|static
85
     */
86
    public function addRoute($route, $class, $action, $parameters = [], $hostparameters = [])
87
    {
88
        $this->log->debug('adding route : ' . $route . ' to class ' . $class . ' and action ' . $action);
89
90
        $this->routes[$route] = [
91
            "class" => $class,
92
            "action" => $action,
93
            "parameters" => $parameters,
94
            "hostparameters" => $hostparameters
95
        ];
96
        return $this;
97
    }
98
99
    /**
100
     * method to set the basicHost for hostparameters in routing
101
     *
102
     * @see King23_Router::$basicHost
103
     * @param String $baseHost
104
     * @return void|static
105
     */
106
    public function setBaseHost($baseHost = null)
107
    {
108
        $this->log->debug('Setting Router baseHost to ' . $baseHost);
109
        $this->baseHost = $baseHost;
110
        return $this;
111
    }
112
113
    /**
114
     * @param ServerRequestInterface $request
115
     * @param ResponseInterface $response
116
     * @param callable $next
117
     * @return mixed
118
     */
119
    public function __invoke(
120
        ServerRequestInterface $request,
121
        ResponseInterface $response,
122
        callable $next
123
    ) {
124
        $this->log->debug('Dispatching request for ' . $request->getUri()->getPath());
125
126
        // sort routes
127
        uksort(
128
            $this->routes,
129
            function ($a, $b) {
130
                return strlen($a) < strlen($b);
131
            }
132
        );
133
        foreach ($this->routes as $route => $info) {
134
            // check if route is matched
135
            if (substr($request->getUri()->getPath(), 0, strlen($route)) == $route) {
136
                $this->log->debug('route ' . $route . ' matches ' . $request->getUri()->getPath());
137
                $response = $this->handleRoute($info, $request, $response, $route);
138
                break;
139
            }
140
        }
141
        return $next($request, $response);
142
    }
143
144
    /**
145
     * Handle a regular route
146
     *
147
     * @param array $info
148
     * @param ServerRequestInterface $request
149
     * @param ResponseInterface $response
150
     * @param string $route
151
     * @return ResponseInterface
152
     * @throws \King23\Controller\Exceptions\ActionDoesNotExistException
153
     */
154
    private function handleRoute($info, ServerRequestInterface $request, ResponseInterface $response, $route)
155
    {
156
        // initialize router attributes for the request
157
        $attributes = [
158
            'params' => [
159
                'url' => [],
160
                'host' => []
161
            ]
162
        ];
163
164
        // prepare parameters
165
        if ($paramstr = substr($request->getUri()->getPath(), strlen($route))) {
166
            $attributes['params']['url'] = $this->filterParameters($info['parameters'], explode("/", $paramstr));
167
        }
168
169
        // check host parameters
170
        if (count($info["hostparameters"]) > 0) {
171
            $attributes['params']['host'] = $this->extractHostParameters($request, $info);
172
        }
173
174
        // put route parameters into king23.router.parameters attribute
175
        $request = $request->withAttribute('king23.router', $attributes);
176
177
        /** @var \King23\Controller\Controller $controller */
178
        $controller = $this->container->getInstanceOf($info["class"]);
179
180
        return $controller->dispatch($info["action"], $request, $response);
181
    }
182
183
    /**
184
     * @param array $info
185
     * @param array $params
186
     * @return array
187
     */
188
    private function filterParameters($info, $params)
189
    {
190
        $parameters = [];
191
        foreach ($info as $key => $value) {
192
            if (isset($params[$key])) {
193
                $parameters[$value] = urldecode($params[$key]);
194
            } else {
195
                $parameters[$value] = null;
196
            }
197
        }
198
        return $parameters;
199
    }
200
201
    /**
202
     * extract parameters from hostname
203
     *
204
     * @param ServerRequestInterface $request
205
     * @param array $info
206
     * @return array
207
     */
208
    private function extractHostParameters($request, $info)
209
    {
210
        $hostname = $this->cleanHostName($request);
211
212
        if (empty($hostname)) {
213
            $params = [];
214
        } else {
215
            $params = array_reverse(explode(".", $hostname));
216
        }
217
218
        $parameters = $this->filterParameters($info['hostparameters'], $params);
219
        return $parameters;
220
    }
221
222
    /**
223
     * will get hostname, and clean basehost off it
224
     *
225
     * @param ServerRequestInterface $request
226
     * @return string
227
     */
228
    private function cleanHostName(ServerRequestInterface $request)
229
    {
230
231
        if (is_null($this->baseHost)) {
232
            $hostname = $request->getUri()->getHost();
233
        } else {
234
            $hostname = str_replace($this->baseHost, "", $request->getUri()->getHost());
235
        }
236
237
        if (substr($hostname, -1) == ".") {
238
            $hostname = substr($hostname, 0, -1);
239
        }
240
241
        return $hostname;
242
    }
243
}
244