Completed
Push — 4.0 ( b89114...5a5e94 )
by Marco
02:16
created

Table::add()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 38
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6.1384

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 38
ccs 9
cts 14
cp 0.6429
rs 8.439
cc 5
eloc 18
nc 4
nop 4
crap 6.1384
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);
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
215 1
        $route = new Route();
216
217 1
        $this->updateRoute($route, $type, $class, $parameters);
218
219
        // $route->setType($type) // Type of route
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
220
        //     ->setClassName($class) // Class to be invoked
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
221
        //     ->setParameters($parameters); // Parameters passed via the composer.json configuration (cache, ttl, etc...)
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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