Passed
Push — master ( a15eb9...7b437d )
by Fran
04:59
created

Router   F

Complexity

Total Complexity 103

Size/Duplication

Total Lines 551
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 58.55%

Importance

Changes 0
Metric Value
dl 0
loc 551
ccs 137
cts 234
cp 0.5855
rs 1.5789
c 0
b 0
f 0
wmc 103
lcom 1
cbo 18

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A init() 0 11 3
A debugLoad() 0 6 1
B httpNotFound() 0 24 5
A getSlugs() 0 4 1
A getRoutes() 0 3 1
A getAllRoutes() 0 10 3
A execute() 0 18 4
D searchAction() 0 30 9
B checkRequirements() 0 17 5
A sentAuthHeader() 0 4 1
A getExternalModules() 0 7 2
A checkExternalModules() 0 10 3
A generateRouting() 0 15 3
B hydrateRouting() 0 17 6
A inspectDir() 0 11 2
A exists() 0 4 3
D addRouting() 0 27 10
B extractDomain() 0 15 5
A simpatize() 0 10 1
A getAdminRoutes() 0 4 1
A getAdmin() 0 4 1
A getDomains() 0 4 2
C executeCachedRoute() 0 25 7
B generateSlugs() 0 18 6
A loadExternalAutoloader() 0 11 3
A loadExternalModule() 0 14 4
B getRoute() 0 17 10

How to fix   Complexity   

Complex Class

Complex classes like Router often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Router, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace PSFS\base;
3
4
use PSFS\base\config\Config;
5
use PSFS\base\dto\JsonResponse;
6
use PSFS\base\exception\AccessDeniedException;
7
use PSFS\base\exception\ConfigException;
8
use PSFS\base\exception\RouterException;
9
use PSFS\base\types\helpers\AdminHelper;
10
use PSFS\base\types\helpers\GeneratorHelper;
11
use PSFS\base\types\helpers\I18nHelper;
12
use PSFS\base\types\helpers\RequestHelper;
13
use PSFS\base\types\helpers\RouterHelper;
14
use PSFS\base\types\helpers\SecurityHelper;
15
use PSFS\base\types\traits\SingletonTrait;
16
use PSFS\controller\base\Admin;
17
use PSFS\services\AdminServices;
18
use Symfony\Component\Finder\Finder;
19
use Symfony\Component\Finder\SplFileInfo;
20
21
22
/**
23
 * Class Router
24
 * @package PSFS
25
 */
26
class Router
27
{
28
    use SingletonTrait;
29
30
    protected $routing;
31
    protected $slugs;
32
    private $domains = [];
33
    /**
34
     * @var Finder $finder
35
     */
36
    private $finder;
37
    /**
38
     * @var \PSFS\base\Cache $cache
39
     */
40
    private $cache;
41
    /**
42
     * @var bool headersSent
43
     */
44
    protected $headersSent = false;
45
    /**
46
     * @var int
47
     */
48
    protected $cacheType = Cache::JSON;
49
50
    /**
51
     * Constructor Router
52
     * @throws ConfigException
53
     */
54 1
    public function __construct()
55
    {
56 1
        $this->finder = new Finder();
57 1
        $this->cache = Cache::getInstance();
58 1
        $this->init();
59 1
    }
60
61
    /**
62
     * Inicializador Router
63
     * @throws ConfigException
64
     */
65 1
    public function init()
66
    {
67 1
        list($this->routing, $this->slugs) = $this->cache->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . "urls.json", $this->cacheType, TRUE);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 147 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
68 1
        if (empty($this->routing) || Config::getInstance()->getDebugMode()) {
69 1
            $this->debugLoad();
70
        } else {
71
            $this->domains = $this->cache->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . "domains.json", $this->cacheType, TRUE);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 134 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
72
        }
73 1
        $this->checkExternalModules(false);
74 1
        $this->setLoaded(true);
75 1
    }
76
77
    /**
78
     * Load routes and domains and store them
79
     */
80 1
    private function debugLoad() {
81 1
        Logger::log('Begin routes load', LOG_DEBUG);
82 1
        $this->hydrateRouting();
83 1
        $this->simpatize();
84 1
        Logger::log('End routes load', LOG_DEBUG);
85 1
    }
86
87
    /**
88
     * Método que deriva un error HTTP de página no encontrada
89
     *
90
     * @param \Exception $e
0 ignored issues
show
Documentation introduced by
Should the type for parameter $e not be null|\Exception?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
91
     * @param boolean $isJson
92
     *
93
     * @return string HTML
94
     */
95
    public function httpNotFound(\Exception $e = NULL, $isJson = false)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $e. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
96
    {
97
        Logger::log('Throw not found exception');
98
        if (NULL === $e) {
99
            Logger::log('Not found page throwed without previous exception', LOG_WARNING);
100
            $e = new \Exception(_('Page not found'), 404);
101
        }
102
        $template = Template::getInstance()->setStatus($e->getCode());
103
        if (preg_match('/json/i', Request::getInstance()->getServer('CONTENT_TYPE')) || $isJson) {
104
            $response = new JsonResponse(null, false, 0, 0, $e->getMessage());
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
105
            return $template->output(json_encode($response), 'application/json');
106
        } else {
107
            $not_found_rouote = Config::getParam('route.404');
108
            if(null !== $not_found_rouote) {
109
                Request::getInstance()->redirect($this->getRoute($not_found_rouote, true));
110
            } else {
111
                return $template->render('error.html.twig', array(
112
                    'exception' => $e,
113
                    'trace' => $e->getTraceAsString(),
114
                    'error_page' => TRUE,
115
                ));
116
            }
117
        }
118
    }
119
120
    /**
121
     * Método que devuelve las rutas
122
     * @return string|null
123
     */
124 1
    public function getSlugs()
125
    {
126 1
        return $this->slugs;
127
    }
128
129
    /**
130
     * @return mixed
131
     */
132 2
    public function getRoutes() {
133 2
        return $this->routing;
134
    }
135
136
    /**
137
     * Method that extract all routes in the platform
138
     * @return array
139
     */
140 1
    public function getAllRoutes()
141
    {
142 1
        $routes = [];
143 1
        foreach ($this->getRoutes() as $path => $route) {
144 1
            if (array_key_exists('slug', $route)) {
145 1
                $routes[$route['slug']] = $path;
146
            }
147
        }
148 1
        return $routes;
149
    }
150
151
    /**
152
     * Método que calcula el objeto a enrutar
153
     *
154
     * @param string|null $route
155
     *
156
     * @throws \Exception
157
     * @return string HTML
158
     */
159 1
    public function execute($route)
160
    {
161 1
        Logger::log('Executing the request');
162
        try {
163
            //Search action and execute
164 1
            $this->searchAction($route);
165 1
        } catch (AccessDeniedException $e) {
166
            Logger::log(_('Solicitamos credenciales de acceso a zona restringida'));
167
            return Admin::staticAdminLogon($route);
168 1
        } catch (RouterException $r) {
169 1
            Logger::log($r->getMessage(), LOG_WARNING);
170
        } catch (\Exception $e) {
171
            Logger::log($e->getMessage(), LOG_ERR);
172
            throw $e;
173
        }
174
175 1
        throw new RouterException(_("Página no encontrada"), 404);
176
    }
177
178
    /**
179
     * Método que busca el componente que ejecuta la ruta
180
     *
181
     * @param string $route
182
     *
183
     * @throws \PSFS\base\exception\RouterException
184
     */
185 1
    protected function searchAction($route)
186
    {
187 1
        Logger::log('Searching action to execute: ' . $route, LOG_INFO);
188
        //Revisamos si tenemos la ruta registrada
189 1
        $parts = parse_url($route);
190 1
        $path = (array_key_exists('path', $parts)) ? $parts['path'] : $route;
191 1
        $httpRequest = Request::getInstance()->getMethod();
192 1
        foreach ($this->routing as $pattern => $action) {
193 1
            list($httpMethod, $routePattern) = RouterHelper::extractHttpRoute($pattern);
194 1
            $matched = RouterHelper::matchRoutePattern($routePattern, $path);
195 1
            if ($matched && ($httpMethod === "ALL" || $httpRequest === $httpMethod) && RouterHelper::compareSlashes($routePattern, $path)) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 140 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
196
                // Checks restricted access
197
                SecurityHelper::checkRestrictedAccess($route);
198
                $get = RouterHelper::extractComponents($route, $routePattern);
199
                /** @var $class \PSFS\base\types\Controller */
200
                $class = RouterHelper::getClassToCall($action);
201
                try {
202
                    if($this->checkRequirements($action, $get)) {
203
                        $this->executeCachedRoute($route, $action, $class, $get);
204
                    } else {
205
                        throw new RouterException(_('La ruta no es válida'), 400);
206
                    }
207
                } catch (\Exception $e) {
208
                    Logger::log($e->getMessage(), LOG_ERR);
209
                    throw new \RuntimeException($e->getMessage(), 404, $e);
210
                }
211
            }
212
        }
213 1
        throw new RouterException(_("Ruta no encontrada"));
214
    }
215
216
    /**
217
     * @param array $action
218
     * @param array $params
219
     * @return bool
220
     */
221
    private function checkRequirements(array $action, array $params = []) {
222
        $valid = true;
223
        if(!empty($action['requirements'])) {
224
            if(!empty($params)) {
225
                $checked = 0;
226
                foreach(array_keys($params) as $key) {
227
                    if(in_array($key, $action['requirements'])) {
228
                        $checked++;
229
                    }
230
                }
231
                $valid = count($action['requirements']) == $checked;
232
            } else {
233
                $valid = false;
234
            }
235
        }
236
        return $valid;
237
    }
238
239
    /**
240
     * Método que manda las cabeceras de autenticación
241
     * @return string HTML
242
     */
243
    protected function sentAuthHeader()
244
    {
245
        return AdminServices::getInstance()->setAdminHeaders();
246
    }
247
248
    /**
249
     * @return string|null
250
     */
251 1
    private function getExternalModules() {
252 1
        $externalModules = Config::getParam('modules.extend', '');
253 1
        if(Config::getParam('psfs.auth', false)) {
254
            $externalModules .= ',psfs/auth';
255
        }
256 1
        return $externalModules;
257
    }
258
259
    /**
260
     * Method that check if the proyect has sub project to include
261
     * @param boolean $hydrateRoute
262
     */
263 1
    private function checkExternalModules($hydrateRoute = true)
264
    {
265 1
        $externalModules = $this->getExternalModules();
266 1
        if (strlen($externalModules)) {
267
            $externalModules = explode(',', $externalModules);
268
            foreach ($externalModules as &$module) {
269
                $module = $this->loadExternalModule($hydrateRoute, $module);
270
            }
271
        }
272 1
    }
273
274
    /**
275
     * Method that gather all the routes in the project
276
     */
277 1
    private function generateRouting()
278
    {
279 1
        $base = SOURCE_DIR;
280 1
        $modulesPath = realpath(CORE_DIR);
281 1
        $this->routing = $this->inspectDir($base, "PSFS", array());
282 1
        $this->checkExternalModules();
283 1
        if (file_exists($modulesPath)) {
284
            $modules = $this->finder->directories()->in($modulesPath)->depth(0);
285
            foreach ($modules as $modulePath) {
286
                $module = $modulePath->getBasename();
287
                $this->routing = $this->inspectDir($modulesPath . DIRECTORY_SEPARATOR . $module, $module, $this->routing);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
288
            }
289
        }
290 1
        $this->cache->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . "domains.json", $this->domains, Cache::JSON, TRUE);
291 1
    }
292
293
    /**
294
     * Método que regenera el fichero de rutas
295
     * @throws ConfigException
296
     */
297 1
    public function hydrateRouting()
298
    {
299 1
        $this->generateRouting();
300 1
        $home = Config::getInstance()->get('home.action');
301 1
        if (NULL !== $home || $home !== '') {
302 1
            $home_params = NULL;
303 1
            foreach ($this->routing as $pattern => $params) {
304 1
                list($method, $route) = RouterHelper::extractHttpRoute($pattern);
305 1
                if (preg_match("/" . preg_quote($route, "/") . "$/i", "/" . $home)) {
306
                    $home_params = $params;
307
                }
308
            }
309 1
            if (NULL !== $home_params) {
310
                $this->routing['/'] = $home_params;
311
            }
312
        }
313 1
    }
314
315
    /**
316
     * Método que inspecciona los directorios en busca de clases que registren rutas
317
     *
318
     * @param string $origen
319
     * @param string $namespace
320
     * @param array $routing
321
     *
322
     * @return array
323
     * @throws ConfigException
324
     */
325 1
    private function inspectDir($origen, $namespace = 'PSFS', $routing = [])
326
    {
327 1
        $files = $this->finder->files()->in($origen)->path('/(controller|api)/i')->depth(1)->name("*.php");
328 1
        foreach ($files as $file) {
329 1
            $filename = str_replace("/", '\\', str_replace($origen, '', $file->getPathname()));
330 1
            $routing = $this->addRouting($namespace . str_replace('.php', '', $filename), $routing, $namespace);
331
        }
332 1
        $this->finder = new Finder();
333
334 1
        return $routing;
335
    }
336
337
    /**
338
     * Checks that a namespace exists
339
     * @param string $namespace
340
     * @return bool
341
     */
342 2
    public static function exists($namespace)
343
    {
344 2
        return (class_exists($namespace) || interface_exists($namespace) || trait_exists($namespace));
345
    }
346
347
    /**
348
     * Método que añade nuevas rutas al array de referencia
349
     *
350
     * @param string $namespace
351
     * @param array $routing
352
     * @param string $module
353
     *
354
     * @return array
355
     * @throws ConfigException
356
     */
357 1
    private function addRouting($namespace, &$routing, $module = 'PSFS')
358
    {
359 1
        if (self::exists($namespace)) {
360 1
            $reflection = new \ReflectionClass($namespace);
361 1
            if (FALSE === $reflection->isAbstract() && FALSE === $reflection->isInterface()) {
362 1
                $this->extractDomain($reflection);
363 1
                $classComments = $reflection->getDocComment();
364 1
                preg_match('/@api\ (.*)\n/im', $classComments, $apiPath);
365 1
                $api = '';
366 1
                if (count($apiPath)) {
367
                    $api = array_key_exists(1, $apiPath) ? $apiPath[1] : $api;
368
                }
369 1
                foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
370 1
                    if (preg_match('/@route\ /i', $method->getDocComment())) {
371 1
                        list($route, $info) = RouterHelper::extractRouteInfo($method, str_replace('\\', '', $api), str_replace('\\', '', $module));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 147 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
372
373 1
                        if (null !== $route && null !== $info) {
374 1
                            $info['class'] = $namespace;
375 1
                            $routing[$route] = $info;
376
                        }
377
                    }
378
                }
379
            }
380
        }
381
382 1
        return $routing;
383
    }
384
385
    /**
386
     * Método que extrae de la ReflectionClass los datos necesarios para componer los dominios en los templates
387
     *
388
     * @param \ReflectionClass $class
389
     *
390
     * @return Router
391
     * @throws ConfigException
392
     */
393 1
    protected function extractDomain(\ReflectionClass $class)
394
    {
395
        //Calculamos los dominios para las plantillas
396 1
        if ($class->hasConstant("DOMAIN") && !$class->isAbstract()) {
397 1
            if (!$this->domains) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->domains of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
398 1
                $this->domains = [];
399
            }
400 1
            $domain = "@" . $class->getConstant("DOMAIN") . "/";
401 1
            if (!array_key_exists($domain, $this->domains)) {
402 1
                $this->domains[$domain] = RouterHelper::extractDomainInfo($class, $domain);
403
            }
404
        }
405
406 1
        return $this;
407
    }
408
409
    /**
410
     * Método que genera las urls amigables para usar dentro del framework
411
     * @return Router
412
     */
413 1
    public function simpatize()
414
    {
415 1
        $translationFileName = "translations" . DIRECTORY_SEPARATOR . "routes_translations.php";
416 1
        $absoluteTranslationFileName = CACHE_DIR . DIRECTORY_SEPARATOR . $translationFileName;
417 1
        $this->generateSlugs($absoluteTranslationFileName);
418 1
        GeneratorHelper::createDir(CONFIG_DIR);
419 1
        Cache::getInstance()->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . "urls.json", array($this->routing, $this->slugs), Cache::JSON, TRUE);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 144 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
420
421 1
        return $this;
422
    }
423
424
    /**
425
     * Método que devuelve una ruta del framework
426
     *
427
     * @param string $slug
428
     * @param boolean $absolute
429
     * @param array $params
430
     *
431
     * @return string|null
432
     * @throws RouterException
433
     */
434 3
    public function getRoute($slug = '', $absolute = FALSE, $params = [])
435
    {
436 3
        if (strlen($slug) === 0) {
437 1
            return ($absolute) ? Request::getInstance()->getRootUrl() . '/' : '/';
438
        }
439 3
        if (!is_array($this->slugs) || !array_key_exists($slug, $this->slugs)) {
440 1
            throw new RouterException(_("No existe la ruta especificada"));
441
        }
442 3
        $url = ($absolute) ? Request::getInstance()->getRootUrl() . $this->slugs[$slug] : $this->slugs[$slug];
443 3
        if (!empty($params)) foreach ($params as $key => $value) {
444
            $url = str_replace("{" . $key . "}", $value, $url);
445 3
        } elseif (!empty($this->routing[$this->slugs[$slug]]["default"])) {
446 3
            $url = ($absolute) ? Request::getInstance()->getRootUrl() . $this->routing[$this->slugs[$slug]]["default"] : $this->routing[$this->slugs[$slug]]["default"];
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 168 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
447
        }
448
449 3
        return preg_replace('/(GET|POST|PUT|DELETE|ALL)\#\|\#/', '', $url);
450
    }
451
452
    /**
453
     * Método que devuelve las rutas de administración
454
     * @deprecated
455
     * @return array
456
     */
457
    public function getAdminRoutes()
458
    {
459
        return AdminHelper::getAdminRoutes($this->routing);
460
    }
461
462
    /**
463
     * Método que devuelve le controlador del admin
464
     * @deprecated
465
     * @return Admin
466
     */
467
    public function getAdmin()
468
    {
469
        return Admin::getInstance();
470
    }
471
472
    /**
473
     * Método que extrae los dominios
474
     * @return array
475
     */
476 1
    public function getDomains()
477
    {
478 1
        return $this->domains ?: [];
479
    }
480
481
    /**
482
     * Método que ejecuta una acción del framework y revisa si lo tenemos cacheado ya o no
483
     *
484
     * @param string $route
485
     * @param array|null $action
486
     * @param types\Controller $class
487
     * @param array $params
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
488
     */
489
    protected function executeCachedRoute($route, $action, $class, $params = NULL)
490
    {
491
        Logger::log('Executing route ' . $route, LOG_INFO);
492
        $action['params'] = array_merge($action['params'], $params, Request::getInstance()->getQueryParams());
493
        Security::getInstance()->setSessionKey("__CACHE__", $action);
494
        $cache = Cache::needCache();
495
        $execute = TRUE;
496
        if (FALSE !== $cache && Config::getInstance()->getDebugMode() === FALSE && $action['http'] === 'GET') {
497
            list($path, $cacheDataName) = $this->cache->getRequestCacheHash();
498
            $cachedData = $this->cache->readFromCache("json" . DIRECTORY_SEPARATOR . $path . $cacheDataName,
499
                $cache, null);
0 ignored issues
show
Bug introduced by
It seems like $cache defined by \PSFS\base\Cache::needCache() on line 494 can also be of type boolean; however, PSFS\base\Cache::readFromCache() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
500
            if (NULL !== $cachedData) {
501
                $headers = $this->cache->readFromCache("json" . DIRECTORY_SEPARATOR . $path . $cacheDataName . ".headers",
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
502
                    $cache, null, Cache::JSON);
0 ignored issues
show
Bug introduced by
It seems like $cache defined by \PSFS\base\Cache::needCache() on line 494 can also be of type boolean; however, PSFS\base\Cache::readFromCache() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
503
                Template::getInstance()->renderCache($cachedData, $headers);
504
                $execute = FALSE;
505
            }
506
        }
507
        if ($execute) {
508
            Logger::log(_('Start executing action'), LOG_DEBUG);
509
            if (false === call_user_func_array(array($class, $action['method']), $params)) {
510
                Logger::log(_('An error ocurred trying to execute the action'), LOG_ERR, [error_get_last()]);
511
            }
512
        }
513
    }
514
515
    /**
516
     * Parse slugs to create translations
517
     *
518
     * @param string $absoluteTranslationFileName
519
     */
520 1
    private function generateSlugs($absoluteTranslationFileName)
521
    {
522 1
        $translations = I18nHelper::generateTranslationsFile($absoluteTranslationFileName);
523 1
        foreach ($this->routing as $key => &$info) {
524 1
            $keyParts = $key;
525 1
            if (FALSE === strstr("#|#", $key)) {
526 1
                $keyParts = explode("#|#", $key);
527 1
                $keyParts = array_key_exists(1, $keyParts) ? $keyParts[1] : '';
528
            }
529 1
            $slug = RouterHelper::slugify($keyParts);
530 1
            if (NULL !== $slug && !array_key_exists($slug, $translations)) {
531 1
                $translations[$slug] = $key;
532 1
                file_put_contents($absoluteTranslationFileName, "\$translations[\"{$slug}\"] = _(\"{$slug}\");\n", FILE_APPEND);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 128 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
533
            }
534 1
            $this->slugs[$slug] = $key;
535 1
            $info["slug"] = $slug;
536
        }
537 1
    }
538
539
    /**
540
     * @param bool $hydrateRoute
541
     * @param $modulePath
542
     * @param $externalModulePath
543
     */
544
    private function loadExternalAutoloader($hydrateRoute, SplFileInfo $modulePath, $externalModulePath)
545
    {
546
        $extModule = $modulePath->getBasename();
547
        $moduleAutoloader = realpath($externalModulePath . DIRECTORY_SEPARATOR . $extModule . DIRECTORY_SEPARATOR . 'autoload.php');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
548
        if (file_exists($moduleAutoloader)) {
549
            @include $moduleAutoloader;
550
            if ($hydrateRoute) {
551
                $this->routing = $this->inspectDir($externalModulePath . DIRECTORY_SEPARATOR . $extModule, '\\' . $extModule, $this->routing);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 142 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
552
            }
553
        }
554
    }
555
556
    /**
557
     * @param $hydrateRoute
558
     * @param $module
559
     * @return mixed
560
     */
561
    private function loadExternalModule($hydrateRoute, $module)
562
    {
563
        $module = preg_replace('/(\\\|\/)/', DIRECTORY_SEPARATOR, $module);
564
        $externalModulePath = VENDOR_DIR . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . 'src';
565
        if (file_exists($externalModulePath)) {
566
            $externalModule = $this->finder->directories()->in($externalModulePath)->depth(0);
567
            if (!empty($externalModule)) {
568
                foreach ($externalModule as $modulePath) {
569
                    $this->loadExternalAutoloader($hydrateRoute, $modulePath, $externalModulePath);
570
                }
571
            }
572
        }
573
        return $module;
574
    }
575
576
}
577