Passed
Push — master ( 2955c9...ee5866 )
by Fran
05:26
created

Router::getAllRoutes()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 0
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
ccs 0
cts 8
cp 0
crap 12
1
<?php
2
3
namespace PSFS\base;
4
5
use PSFS\base\config\Config;
6
use PSFS\base\exception\AccessDeniedException;
7
use PSFS\base\exception\ConfigException;
8
use PSFS\base\exception\RouterException;
9
use PSFS\base\types\SingletonTrait;
10
use PSFS\controller\Admin;
11
use PSFS\services\AdminServices;
12
use Symfony\Component\Finder\Finder;
13
14
15
/**
16
 * Class Router
17
 * @package PSFS
18
 */
19
class Router
20
{
21
22
    use SingletonTrait;
23
24
    protected $routing;
25
    protected $slugs;
26
    private $domains;
27
    /**
28
     * @var Finder $finder
29
     */
30
    private $finder;
31
    /**
32
     * @var \PSFS\base\Cache $cache
33
     */
34
    private $cache;
35
    /**
36
     * @var bool headersSent
37
     */
38
    protected $headersSent = false;
39
40
    /**
41
     * Constructor Router
42
     * @throws ConfigException
43
     */
44 6
    public function __construct()
45
    {
46 6
        $this->finder = new Finder();
47 6
        $this->cache = Cache::getInstance();
48 6
        $this->init();
49 6
    }
50
51
    /**
52
     * Inicializador Router
53
     * @throws ConfigException
54
     */
55 1
    public function init()
56
    {
57 1
        if (!file_exists(CONFIG_DIR . DIRECTORY_SEPARATOR . "urls.json") || Config::getInstance()->getDebugMode()) {
58 1
            $this->hydrateRouting();
59 1
            $this->simpatize();
60 1
        } else {
61 1
            list($this->routing, $this->slugs) = $this->cache->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . "urls.json", Cache::JSON, TRUE);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 146 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...
62 1
            $this->domains = $this->cache->getDataFromFile(CONFIG_DIR . DIRECTORY_SEPARATOR . "domains.json", Cache::JSON, TRUE);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 129 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...
63
        }
64 1
    }
65
66
    /**
67
     * Método que deriva un error HTTP de página no encontrada
68
     *
69
     * @param \Exception $e
70
     *
71
     * @return string HTML
72
     */
73
    public function httpNotFound(\Exception $e = NULL)
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...
74
    {
75
        Logger::log('Throw not found exception');
76
        $template = Template::getInstance()->setStatus($e->getCode());
77
        if (preg_match('/json/i', Request::getInstance()->getServer('CONTENT_TYPE'))) {
78
            return $template->output(json_encode(array(
79
                "success" => FALSE,
80
                "error" => $e->getMessage(),
81
            )), 'application/json');
82
        } else {
83
            if (NULL === $e) {
84
                Logger::log('Not found page throwed without previus exception');
85
                $e = new \Exception(_('Page not found'), 404);
86
            }
87
88
            return $template->render('error.html.twig', array(
89
                'exception' => $e,
90
                'trace' => $e->getTraceAsString(),
91
                'error_page' => TRUE,
92
            ));
93
        }
94
    }
95
96
    /**
97
     * Método que devuelve las rutas
98
     * @return string|null
99
     */
100 1
    public function getSlugs()
101 1
    {
102
        return $this->slugs;
103
    }
104
105
    /**
106
     * Method that extract all routes in the platform
107
     * @return array
108
     */
109
    public function getAllRoutes() {
110
        $routes = [];
111
        foreach($this->routing as $path => $route) {
112
            if(array_key_exists('slug', $route)) {
113
                $routes[$route['slug']] = $path;
114
            }
115
        }
116
        return $routes;
117
    }
118
119
    /**
120
     * Método que calcula el objeto a enrutar
121
     *
122
     * @param string $route
123
     *
124
     * @throws \Exception
125
     * @return string HTML
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
126
     */
127
    public function execute($route)
128
    {
129
        Logger::log('Executing the request');
130
        try {
131
            //Check CORS for requests
132
            $this->checkCORS();
133
            // Checks restricted access
134
            $this->checkRestrictedAccess($route);
135
            //Search action and execute
136
            $this->searchAction($route);
137
        } catch (AccessDeniedException $e) {
138
            Logger::log(_('Solicitamos credenciales de acceso a zona restringida'));
139
            if ('login' === Config::getInstance()->get('admin_login')) {
140
                return $this->redirectLogin($route);
141
            } else {
142
                return $this->sentAuthHeader();
143
            }
144
        } catch (RouterException $r) {
145
            if (FALSE !== preg_match('/\/$/', $route)) {
146
                if (preg_match('/admin/', $route)) {
147
                    $default = Config::getInstance()->get('admin_action');
148
                } else {
149
                    $default = Config::getInstance()->get('home_action');
150
                }
151
152
                return $this->execute($this->getRoute($default));
153
            }
154
        } catch (\Exception $e) {
155
            Logger::log($e->getMessage(), LOG_ERR);
156
            throw $e;
157
        }
158
159
        return $this->httpNotFound();
160
    }
161
162
    /**
163
     * Check CROS requests
164
     */
165
    private function checkCORS()
166
    {
167
        Logger::log('Checking CORS');
168
        $corsEnabled = Config::getInstance()->get('cors.enabled');
169
        $request = Request::getInstance();
170
        if (NULL !== $corsEnabled && null !== $request->getServer('HTTP_REFERER')) {
171
            if ($corsEnabled == '*' || preg_match($corsEnabled, $request->getServer('HTTP_REFERER'))) {
172
                if (!$this->headersSent) {
173
                    // TODO include this headers in Template class output method
174
                    header("Access-Control-Allow-Credentials: true");
175
                    header("Access-Control-Allow-Origin: *");
176
                    header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
177
                    header("Access-Control-Allow-Headers: Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Origin, Origin, X-Requested-With, Content-Type, Accept, Authorization, X-API-SEC-TOKEN, X-API-USER-TOKEN");
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 246 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...
178
                    $this->headersSent = true;
179
                }
180
                if (Request::getInstance()->getMethod() == 'OPTIONS') {
181
                    Logger::log('Returning OPTIONS header confirmation for CORS pre flight requests');
182
                    header("HTTP/1.1 200 OK");
183
                    exit();
184
                }
185
            }
186
        }
187
    }
188
189
    /**
190
     * Function that checks if the long of the patterns match
191
     * @param $routePattern
192
     * @param $path
193
     * @return bool
194
     */
195
    private function compareSlashes($routePattern, $path)
196
    {
197
        $pattern_sep = count(explode('/', $routePattern));
198
        if (preg_match('/\/$/', $routePattern)) {
199
            $pattern_sep--;
200
        }
201
        $path_sep = count(explode('/', $path));
202
        if (preg_match('/\/$/', $path)) {
203
            $path_sep--;
204
        }
205
        return abs($pattern_sep - $path_sep) < 1;
206
    }
207
208
    /**
209
     * Método que busca el componente que ejecuta la ruta
210
     *
211
     * @param string $route
212
     *
213
     * @throws \PSFS\base\exception\RouterException
214
     */
215
    protected function searchAction($route)
216
    {
217
        Logger::log('Searching action to execute');
218
        //Revisamos si tenemos la ruta registrada
219
        $parts = parse_url($route);
220
        $path = (array_key_exists('path', $parts)) ? $parts['path'] : $route;
221
        $httpRequest = Request::getInstance()->getMethod();
222
        foreach ($this->routing as $pattern => $action) {
223
            list($httpMethod, $routePattern) = $this->extractHttpRoute($pattern);
224
            $matched = $this->matchRoutePattern($routePattern, $path);
225
            if ($matched && ($httpMethod === "ALL" || $httpRequest === $httpMethod) && $this->compareSlashes($routePattern, $path)) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 133 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...
226
                $get = $this->extractComponents($route, $routePattern);
227
                /** @var $class \PSFS\base\types\Controller */
228
                $class = $this->getClassToCall($action);
229
                try {
230
                    $this->executeCachedRoute($route, $action, $class, $get);
231
                } catch (\Exception $e) {
232
                    Logger::log($e->getMessage(), LOG_ERR);
233
                    throw new RouterException($e->getMessage(), 404, $e);
234
                }
235
            }
236
        }
237
        throw new RouterException(_("Ruta no encontrada"));
238
    }
239
240
    /**
241
     * Método que manda las cabeceras de autenticación
242
     * @return string HTML
243
     */
244
    protected function sentAuthHeader()
245
    {
246
        return AdminServices::getInstance()->setAdminHeaders();
247
    }
248
249
    /**
250
     * Método que redirige a la pantalla web del login
251
     *
252
     * @param string $route
253
     *
254
     * @return string HTML
255
     */
256
    public function redirectLogin($route)
257
    {
258
        return Admin::staticAdminLogon($route);
259
    }
260
261
    /**
262
     * Método que chequea el acceso a una zona restringida
263
     *
264
     * @param string $route
265
     *
266
     * @throws AccessDeniedException
267
     */
268
    protected function checkRestrictedAccess($route)
269
    {
270
        Logger::log('Checking admin zone');
271
        //Chequeamos si entramos en el admin
272
        if (!Config::getInstance()->checkTryToSaveConfig()
273
            && (preg_match('/^\/(admin|setup\-admin)/i', $route) || NULL !== Config::getInstance()->get('restricted'))
274
        ) {
275
            if (!Security::getInstance()->checkAdmin()) {
276
                throw new AccessDeniedException();
277
            }
278
            Logger::log('Admin access granted');
279
        }
280
    }
281
282
    /**
283
     * Método que extrae de la url los parámetros REST
284
     *
285
     * @param string $route
286
     *
287
     * @param string $pattern
288
     *
289
     * @return array
290
     */
291
    protected function extractComponents($route, $pattern)
292
    {
293
        Logger::log('Extracting parts for the request to execute');
294
        $url = parse_url($route);
295
        $_route = explode("/", $url['path']);
296
        $_pattern = explode("/", $pattern);
297
        $get = array();
298
        if (!empty($_pattern)) foreach ($_pattern as $index => $component) {
299
            $_get = array();
300
            preg_match_all('/^\{(.*)\}$/i', $component, $_get);
301
            if (!empty($_get[1]) && isset($_route[$index])) {
302
                $get[array_pop($_get[1])] = $_route[$index];
303
            }
304
        }
305
306
        return $get;
307
    }
308
309
    /**
310
     * Método que regenera el fichero de rutas
311
     * @throws ConfigException
312
     */
313 1
    public function hydrateRouting()
314
    {
315 1
        $base = SOURCE_DIR;
316 1
        $modules = realpath(CORE_DIR);
317 1
        $this->routing = $this->inspectDir($base, "PSFS", array());
318 1
        if (file_exists($modules)) {
319
            $this->routing = $this->inspectDir($modules, "", $this->routing);
320
        }
321 1
        $this->cache->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . "domains.json", $this->domains, Cache::JSON, TRUE);
322 1
        $home = Config::getInstance()->get('home_action');
323 1
        if (NULL !== $home || $home !== '') {
324 1
            $home_params = NULL;
325 1
            foreach ($this->routing as $pattern => $params) {
326 1
                list($method, $route) = $this->extractHttpRoute($pattern);
327 1
                if (preg_match("/" . preg_quote($route, "/") . "$/i", "/" . $home)) {
328
                    $home_params = $params;
329
                }
330 1
            }
331 1
            if (NULL !== $home_params) {
332
                $this->routing['/'] = $home_params;
333
            }
334 1
        }
335 1
    }
336
337
    /**
338
     * Método que inspecciona los directorios en busca de clases que registren rutas
339
     *
340
     * @param string $origen
341
     * @param string $namespace
342
     * @param array $routing
343
     *
344
     * @return array
345
     * @throws ConfigException
346
     */
347 1
    private function inspectDir($origen, $namespace = 'PSFS', $routing)
348
    {
349 1
        $files = $this->finder->files()->in($origen)->path('/(controller|api)/i')->name("*.php");
350 1
        foreach ($files as $file) {
351 1
            $filename = str_replace("/", '\\', str_replace($origen, '', $file->getPathname()));
352 1
            $routing = $this->addRouting($namespace . str_replace('.php', '', $filename), $routing);
353 1
        }
354 1
        $this->finder = new Finder();
355
356 1
        return $routing;
357
    }
358
359
    /**
360
     * Método que añade nuevas rutas al array de referencia
361
     *
362
     * @param string $namespace
363
     * @param array $routing
364
     *
365
     * @return array
366
     * @throws ConfigException
367
     */
368 1
    private function addRouting($namespace, $routing)
369
    {
370 1
        if (class_exists($namespace)) {
371 1
            $reflection = new \ReflectionClass($namespace);
372 1
            if (FALSE === $reflection->isAbstract() && FALSE === $reflection->isInterface()) {
373 1
                $this->extractDomain($reflection);
374 1
                $classComments = $reflection->getDocComment();
375 1
                preg_match('/@api\ (.*)\n/im', $classComments, $apiPath);
376 1
                $api = '';
377 1
                if (count($apiPath)) {
378
                    $api = array_key_exists(1, $apiPath) ? $apiPath[1] : $api;
379
                }
380 1
                foreach ($reflection->getMethods() as $method) {
381 1
                    if ($method->isPublic()) {
382 1
                        $docComments = $method->getDocComment();
383 1
                        preg_match('/@route\ (.*)\n/i', $docComments, $sr);
384 1
                        if (count($sr)) {
385 1
                            list($regex, $default, $params) = $this->extractReflectionParams($sr, $method);
386 1
                            if (strlen($api)) {
387
                                $regex = str_replace('{__API__}', $api, $regex);
388
                                $default = str_replace('{__API__}', $api, $default);
389
                            }
390 1
                            $httpMethod = $this->extractReflectionHttpMethod($docComments);
391 1
                            $visible = $this->extractReflectionVisibility($docComments);
392 1
                            $expiration = $this->extractReflectionCacheability($docComments);
393 1
                            $routing[$httpMethod . "#|#" . $regex] = array(
394 1
                                "class" => $namespace,
395 1
                                "method" => $method->getName(),
396 1
                                "params" => $params,
397 1
                                "default" => $default,
398 1
                                "visible" => $visible,
399 1
                                "http" => $httpMethod,
400 1
                                "cache" => $expiration,
401
                            );
402 1
                        }
403 1
                    }
404 1
                }
405 1
            }
406 1
        }
407
408 1
        return $routing;
409
    }
410
411
    /**
412
     * Método que extrae de la ReflectionClass los datos necesarios para componer los dominios en los templates
413
     *
414
     * @param \ReflectionClass $class
415
     *
416
     * @return Router
417
     * @throws ConfigException
418
     */
419 1
    protected function extractDomain(\ReflectionClass $class)
420
    {
421
        //Calculamos los dominios para las plantillas
422 1
        if ($class->hasConstant("DOMAIN")) {
423 1
            $domain = "@" . $class->getConstant("DOMAIN") . "/";
424 1
            $path = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
425 1
            $path = realpath($path) . DIRECTORY_SEPARATOR;
426 1
            $tpl_path = "templates";
427 1
            $public_path = "public";
428 1
            $model_path = "models";
429 1
            if (!preg_match("/ROOT/", $domain)) {
430
                $tpl_path = ucfirst($tpl_path);
431
                $public_path = ucfirst($public_path);
432
                $model_path = ucfirst($model_path);
433
            }
434 1
            if ($class->hasConstant("TPL")) {
435
                $tpl_path .= DIRECTORY_SEPARATOR . $class->getConstant("TPL");
436
            }
437 1
            $this->domains[$domain] = array(
438 1
                "template" => $path . $tpl_path,
439 1
                "model" => $path . $model_path,
440 1
                "public" => $path . $public_path,
441
            );
442 1
        }
443
444 1
        return $this;
445
    }
446
447
    /**
448
     * Método que genera las urls amigables para usar dentro del framework
449
     * @return Router
450
     */
451 1
    public function simpatize()
452
    {
453 1
        $translationFileName = "translations" . DIRECTORY_SEPARATOR . "routes_translations.php";
454 1
        $absoluteTranslationFileName = CACHE_DIR . DIRECTORY_SEPARATOR . $translationFileName;
455 1
        $this->generateSlugs($absoluteTranslationFileName);
456 1
        Config::createDir(CONFIG_DIR);
457 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...
458
459 1
        return $this;
460
    }
461
462
    /**
463
     * Método que devuelve el slug de un string dado
464
     *
465
     * @param string $text
466
     *
467
     * @return string
468
     */
469 1
    private function slugify($text)
470
    {
471
        // replace non letter or digits by -
472 1
        $text = preg_replace('~[^\\pL\d]+~u', '-', $text);
473
474
        // trim
475 1
        $text = trim($text, '-');
476
477
        // transliterate
478 1
        if (function_exists('iconv')) {
479 1
            $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
480 1
        }
481
482
        // lowercase
483 1
        $text = strtolower($text);
484
485
        // remove unwanted characters
486 1
        $text = preg_replace('~[^-\w]+~', '', $text);
487
488 1
        if (empty($text)) {
489
            return 'n-a';
490
        }
491
492 1
        return $text;
493
    }
494
495
    /**
496
     * Método que devuelve una ruta del framework
497
     *
498
     * @param string $slug
499
     * @param boolean $absolute
500
     * @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...
501
     *
502
     * @return string|null
503
     * @throws RouterException
504
     */
505
    public function getRoute($slug = '', $absolute = FALSE, $params = NULL)
506
    {
507
        if (strlen($slug) === 0) {
508
            return ($absolute) ? Request::getInstance()->getRootUrl() . '/' : '/';
509
        }
510
        if (NULL === $slug || !array_key_exists($slug, $this->slugs)) {
511
            throw new RouterException(_("No existe la ruta especificada"));
512
        }
513
        $url = ($absolute) ? Request::getInstance()->getRootUrl() . $this->slugs[$slug] : $this->slugs[$slug];
514
        if (!empty($params)) foreach ($params as $key => $value) {
515
            $url = str_replace("{" . $key . "}", $value, $url);
516
        } elseif (!empty($this->routing[$this->slugs[$slug]]["default"])) {
517
            $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...
518
        }
519
520
        return preg_replace('/(GET|POST|PUT|DELETE|ALL)\#\|\#/', '', $url);
521
    }
522
523
    /**
524
     * Método que devuelve las rutas de administración
525
     * @return array
526
     */
527 1
    public function getAdminRoutes()
528
    {
529 1
        $routes = array();
530 1
        foreach ($this->routing as $route => $params) {
531 1
            list($httpMethod, $routePattern) = $this->extractHttpRoute($route);
532 1
            if (preg_match('/^\/admin(\/|$)/', $routePattern)) {
533 1
                if (preg_match('/^\\\?PSFS/', $params["class"])) {
534 1
                    $profile = "superadmin";
535 1
                } else {
536
                    $profile = "admin";
537
                }
538 1
                if (!empty($params["default"]) && preg_match('/(GET|ALL)/i', $httpMethod)) {
539 1
                    $_profile = ($params["visible"]) ? $profile : 'adminhidden';
540 1
                    if (!array_key_exists($_profile, $routes)) {
541 1
                        $routes[$_profile] = array();
542 1
                    }
543 1
                    $routes[$_profile][] = $params["slug"];
544 1
                }
545 1
            }
546 1
        }
547 1
        if (array_key_exists("superadmin", $routes)) {
548 1
            asort($routes["superadmin"]);
549 1
        }
550 1
        if (array_key_exists("adminhidden", $routes)) {
551 1
            asort($routes["adminhidden"]);
552 1
        }
553 1
        if (array_key_exists('admin', $routes)) {
554
            asort($routes["admin"]);
555
        }
556 1
        return $routes;
557
    }
558
559
    /**
560
     * Método que devuelve le controlador del admin
561
     * @return Admin
562
     */
563
    public function getAdmin()
564
    {
565
        return Admin::getInstance();
566
    }
567
568
    /**
569
     * Método que extrae los dominios
570
     * @return array|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
571
     */
572
    public function getDomains()
573
    {
574
        return $this->domains;
575
    }
576
577
    /**
578
     * Método que extrae el controller a invocar
579
     *
580
     * @param string $action
581
     *
582
     * @return Object
583
     */
584
    protected function getClassToCall($action)
585
    {
586
        Logger::log('Getting class to call for executing the request action', LOG_DEBUG, $action);
0 ignored issues
show
Documentation introduced by
$action is of type string, 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...
587
        $actionClass = class_exists($action["class"]) ? $action["class"] : "\\" . $action["class"];
588
        $class = (method_exists($actionClass, "getInstance")) ? $actionClass::getInstance() : new $actionClass;
589
        return $class;
590
    }
591
592
    /**
593
     * Método que compara la ruta web con la guardada en la cache
594
     *
595
     * @param $routePattern
596
     * @param $path
597
     *
598
     * @return bool
599
     */
600
    protected function matchRoutePattern($routePattern, $path)
601
    {
602
        $expr = preg_replace('/\{([^}]+)\}/', '###', $routePattern);
603
        $expr = preg_quote($expr, '/');
604
        $expr = str_replace('###', '(.*)', $expr);
605
        $expr2 = preg_replace('/\(\.\*\)$/', '', $expr);
606
        $matched = preg_match('/^' . $expr . '\/?$/i', $path) || preg_match('/^' . $expr2 . '?$/i', $path);
607
        return $matched;
608
    }
609
610
    /**
611
     * @param $pattern
612
     *
613
     * @return array
614
     */
615 2
    protected function extractHttpRoute($pattern)
616
    {
617 2
        $httpMethod = "ALL";
618 2
        $routePattern = $pattern;
619 2
        if (FALSE !== strstr($pattern, "#|#")) {
620 2
            list($httpMethod, $routePattern) = explode("#|#", $pattern, 2);
621 2
        }
622
623 2
        return array(strtoupper($httpMethod), $routePattern);
624
    }
625
626
    /**
627
     * Método que extrae los parámetros de una función
628
     *
629
     * @param array $sr
630
     * @param \ReflectionMethod $method
631
     *
632
     * @return array
633
     */
634 1
    private function extractReflectionParams($sr, $method)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $sr. 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...
635
    {
636 1
        $regex = $sr[1] ?: $sr[0];
637 1
        $default = '';
638 1
        $params = array();
639 1
        $parameters = $method->getParameters();
640 1
        if (count($parameters) > 0) foreach ($parameters as $param) {
641 1
            if ($param->isOptional() && !is_array($param->getDefaultValue())) {
642 1
                $params[$param->getName()] = $param->getDefaultValue();
643 1
                $default = str_replace('{' . $param->getName() . '}', $param->getDefaultValue(), $regex);
644 1
            }
645 1
        } else $default = $regex;
646
647 1
        return array($regex, $default, $params);
648
    }
649
650
    /**
651
     * Método que extrae el método http
652
     *
653
     * @param string $docComments
654
     *
655
     * @return string
656
     */
657 1
    private function extractReflectionHttpMethod($docComments)
658
    {
659 1
        preg_match('/@(GET|POST|PUT|DELETE)\n/i', $docComments, $routeMethod);
660
661 1
        return (count($routeMethod) > 0) ? $routeMethod[1] : "ALL";
662
    }
663
664
    /**
665
     * Método que extrae la visibilidad de una ruta
666
     *
667
     * @param string $docComments
668
     *
669
     * @return bool
670
     */
671 1
    private function extractReflectionVisibility($docComments)
672
    {
673 1
        preg_match('/@visible\ (.*)\n/i', $docComments, $visible);
674 1
        return !(array_key_exists(1, $visible) && preg_match('/false/i', $visible[1]));
675
    }
676
677
    /**
678
     * Método que extrae el parámetro de caché
679
     *
680
     * @param string $docComments
681
     *
682
     * @return bool
683
     */
684 1
    private function extractReflectionCacheability($docComments)
685
    {
686 1
        preg_match('/@cache\ (.*)\n/i', $docComments, $cache);
687
688 1
        return (count($cache) > 0) ? $cache[1] : "0";
689
    }
690
691
    /**
692
     * Método que ejecuta una acción del framework y revisa si lo tenemos cacheado ya o no
693
     *
694
     * @param string $route
695
     * @param array $action
696
     * @param types\Controller $class
697
     * @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...
698
     */
699
    protected function executeCachedRoute($route, $action, $class, $params = NULL)
700
    {
701
        Logger::log('Executing route ' . $route);
702
        Security::getInstance()->setSessionKey("__CACHE__", $action);
703
        $cache = Cache::needCache();
704
        $execute = TRUE;
705
        if (FALSE !== $cache && Config::getInstance()->getDebugMode() === FALSE) {
706
            $cacheDataName = $this->cache->getRequestCacheHash();
707
            $cachedData = $this->cache->readFromCache("templates" . DIRECTORY_SEPARATOR . $cacheDataName, $cache, function () {
0 ignored issues
show
Bug introduced by
It seems like $cache defined by \PSFS\base\Cache::needCache() on line 703 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...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 127 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...
708
            });
709
            if (NULL !== $cachedData) {
710
                $headers = $this->cache->readFromCache("templates" . DIRECTORY_SEPARATOR . $cacheDataName . ".headers", $cache, function () {
0 ignored issues
show
Bug introduced by
It seems like $cache defined by \PSFS\base\Cache::needCache() on line 703 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...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 141 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...
711
                }, Cache::JSON);
712
                Template::getInstance()->renderCache($cachedData, $headers);
713
                $execute = FALSE;
714
            }
715
        }
716
        if ($execute) {
717
            call_user_func_array(array($class, $action['method']), $params);
718
        }
719
    }
720
721
    /**
722
     * Parse slugs to create translations
723
     *
724
     * @param string $absoluteTranslationFileName
725
     */
726 1
    private function generateSlugs($absoluteTranslationFileName)
727
    {
728 1
        $translations = $this->generateTranslationsFile($absoluteTranslationFileName);
729 1
        foreach ($this->routing as $key => &$info) {
730 1
            $keyParts = $key;
731 1
            if (FALSE === strstr("#|#", $key)) {
732 1
                $keyParts = explode("#|#", $key);
733 1
                $keyParts = array_key_exists(1, $keyParts) ? $keyParts[1] : '';
734 1
            }
735 1
            $slug = $this->slugify($keyParts);
736 1
            if (NULL !== $slug && !array_key_exists($slug, $translations)) {
737 1
                $translations[$slug] = $key;
738 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...
739 1
            }
740 1
            $this->slugs[$slug] = $key;
741 1
            $info["slug"] = $slug;
742 1
        }
743 1
    }
744
745
    /**
746
     * Create translation file if not exists
747
     *
748
     * @param string $absoluteTranslationFileName
749
     *
750
     * @return array
751
     */
752 1
    private function generateTranslationsFile($absoluteTranslationFileName)
753
    {
754 1
        $translations = array();
755 1
        if (file_exists($absoluteTranslationFileName)) {
756
            include($absoluteTranslationFileName);
757
        } else {
758 1
            Cache::getInstance()->storeData($absoluteTranslationFileName, "<?php \$translations = array();\n", Cache::TEXT, TRUE);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 130 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...
759
        }
760
761 1
        return $translations;
762
    }
763
764
}
765