Passed
Branch 4.0 (5a5e94)
by Marco
11:08
created

Table::updateRoute()   B

Complexity

Conditions 7
Paths 64

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 13
c 0
b 0
f 0
ccs 10
cts 10
cp 1
rs 8.2222
cc 7
eloc 9
nc 64
nop 4
crap 7
1
<?php namespace Comodojo\Dispatcher\Router;
2
3
use \Comodojo\Foundation\Base\Configuration;
4
use \Comodojo\Dispatcher\Components\AbstractModel;
5
use \Comodojo\Dispatcher\Cache\RouterCache;
6
use \Comodojo\Dispatcher\Request\Model as Request;
7
use \Comodojo\Dispatcher\Router\Parser;
8
use \Comodojo\Dispatcher\Router\Route;
9
use \Comodojo\Dispatcher\Router\Model as Router;
10
use \Comodojo\SimpleCache\Manager as SimpleCacheManager;
11
use \Comodojo\Exception\DispatcherException;
12
use \Psr\Log\LoggerInterface;
13
use \Countable;
14
use \Exception;
15
16
/**
17
 * @package     Comodojo Dispatcher
18
 * @author      Marco Giovinazzi <[email protected]>
19
 * @author      Marco Castiello <[email protected]>
20
 * @license     GPL-3.0+
21
 *
22
 * LICENSE:
23
 *
24
 * This program is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License as
26
 * published by the Free Software Foundation, either version 3 of the
27
 * License, or (at your option) any later version.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License
35
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36
 */
37
38
class Table extends AbstractModel implements Countable {
39
40
    /**
41
     * Types of route that this table will accept
42
     * @var array
43
     */
44
    const ALLOWED_ROUTES = [ "ROUTE", "REDIRECT", "ERROR" ];
45
46
    /**
47
     * Current repository of routes
48
     * @var array
49
     */
50
    protected $routes = [];
51
52
    /**
53
     * @var Parser
54
     */
55
    protected $parser;
56
57
    /**
58
     * @var RouterCache
59
     */
60
    protected $cache;
61
62
    /**
63
     * Table constructor
64
     *
65
     * @param Configuration $configuration
66
     * @param LoggerInterface $logger
67
     * @param SimpleCacheManager $cache
68
     */
69 1
    public function __construct(
70
        Configuration $configuration,
71
        LoggerInterface $logger,
72
        SimpleCacheManager $cache
73
    ) {
74
75 1
        parent::__construct($configuration, $logger);
76
77 1
        $this->parser = new Parser($logger);
78 1
        $this->cache = new RouterCache($cache);
79
80 1
        $this->readCache();
81
82 1
    }
83
84 1
    public function add(
85
        $route,
86
        $type,
87
        $class = null,
88
        array $parameters = []
89
    ) {
90
91 1
        $type = strtoupper($type);
92
93 1
        if ( !in_array($type, self::ALLOWED_ROUTES) ) {
94
95
            $this->getLogger()->error("Invalid route definition - unknown type $type for route $route)");
96
97 1
        } else if ( $type == 'ROUTE' && empty($class) ) {
98
99
            $this->getLogger()->error("Invalid route definition - missing class for route $route)");
100
101
        } else {
102
103 1
            $routeData = $this->get($route);
104
105 1
            if ( !is_null($routeData) ) {
106
107
                $this->updateRoute($routeData, $type, $class, $parameters);
108
109
            } else {
110
111 1
                $folders = explode("/", $route);
112
113 1
                $this->registerRoute($folders, $type, $class, $parameters);
114
115
            }
116
117
        }
118
119 1
        return $this;
120
121
    }
122
123
    /**
124
     * Get registered routes count
125
     *
126
     * @return int
127
     */
128
    public function count() {
129
130
        return count($this->routes);
131
132
    }
133
134 1
    public function getRoutes() {
135
136 1
        return $this->routes;
137
138
    }
139
140 1
    public function get($route) {
141
142 1
        $regex = $this->regex($route);
143
144 1
        if (isset($this->routes[$regex])) {
145 1
            return $this->routes[$regex];
146
        }
147
148 1
        return null;
149
150
    }
151
152 1
    public function regex($route) {
153
154 1
        $folders = explode("/", $route);
155
156 1
        return $this->parser->read($folders);
157
158
    }
159
160 1
    public function remove($route) {
161
162 1
        $regex = $this->regex($route);
163
164 1
        $routes = $this->routes;
165
166 1
        if (isset($routes[$regex])) {
167
168 1
            unset($routes[$regex]);
169
170 1
            $this->routes = $routes;
171
172 1
            return true;
173
174
        }
175
176
        return false;
177
178
    }
179
180
    public function defaultRoute() {
181
182
        return $this->get('/');
183
184
    }
185
186
    public function load(array $routes) {
187
188
        if (!empty($routes)) {
189
190
            foreach ($routes as $name => $route) {
191
192
                $this->add(
193
                    $route['route'],
194
                    $route['type'],
195
                    empty($route['class']) ? null : $route['class'],
196
                    empty($route['parameters']) ? [] : $route['parameters']
197
                );
198
199
            }
200
201
        }
202
203
        $count = $this->count();
204
        $this->logger->debug("$count routes loaded in routing table");
205
206
        $this->dumpCache();
207
208
    }
209
210
    // This method add a route to the supported list
211 1
    private function registerRoute($folders, $type, $class = null, array $parameters = []) {
212
213
        // The values associated with a route are as follows:
214
        // $route = new Route($this->router);
215 1
        $route = new Route();
216
217 1
        $this->updateRoute($route, $type, $class, $parameters);
218
219
        // $route->setType($type) // Type of route
220
        //     ->setClassName($class) // Class to be invoked
221
        //     ->setParameters($parameters); // Parameters passed via the composer.json configuration (cache, ttl, etc...)
222
223 1
        $this->logger->debug("Route table - route: ".implode("/", $folders));
224
225
        // This method generate a global regular expression which will be able to match all the URI supported by the route
226 1
        $regex = $this->parser->read($folders, $route);
227
228 1
        $this->logger->debug("Route table - route regex: $regex");
229
230 1
        $this->routes = array_merge($this->routes, [$regex => $route]);
231
232 1
    }
233
234 1
    private function updateRoute(Route $route, $type, $class = null, array $parameters = []) {
235
236 1
        $route->setType($type)
237 1
            ->setClassName($class)
238 1
            ->setParameters($parameters);
239
240 1
        if ( !empty($parameters['redirect-code']) ) $route->setRedirectCode($parameters['redirect-code']);
241 1
        if ( !empty($parameters['redirect-location']) ) $route->setRedirectLocation($parameters['redirect-location']);
242 1
        if ( !empty($parameters['redirect-message']) ) $route->setRedirectLocation($parameters['redirect-message']);
243 1
        if ( !empty($parameters['redirect-type']) ) $route->setRedirectType($parameters['redirect-type']);
244
245 1
        if ( !empty($parameters['error-code']) ) $route->setErrorCode($parameters['error-code']);
246 1
        if ( !empty($parameters['error-message']) ) $route->setErrorMessage($parameters['error-message']);
247
248 1
    }
249
250 1
    private function readCache() {
251
252 1
        if ($this->configuration->get('routing-table-cache') !== true) return;
253
254
        $data = $this->cache->read();
255
256
        if (is_null($data)) {
257
258
            $this->routes = [];
259
260
            return;
261
262
        }
263
264
        $this->routes = $data;
265
266
        $this->logger->debug("Routing table loaded from cache");
267
268
    }
269
270
    private function dumpCache() {
271
272
        if ($this->configuration->get('routing-table-cache') !== true) return;
273
274
        $ttl = $this->configuration->get('routing-table-ttl');
275
276
        if ($this->cache->dump($this->routes, $ttl)) {
277
            $this->logger->debug("Routing table saved to cache");
278
        } else {
279
            $this->logger->warning("Cannot save routing table to cache");
280
        }
281
282
    }
283
284
}
285