Passed
Push — master ( ee5866...de68c3 )
by Fran
05:07
created

Router::exists()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 2
nc 3
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 3
rs 10
c 0
b 0
f 0
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
            $module = "";
320
            if(file_exists($modules . DIRECTORY_SEPARATOR . 'module.json')) {
321
                $mod_cfg = json_decode(file_get_contents($modules . DIRECTORY_SEPARATOR . 'module.json'), true);
322
                $module = $mod_cfg['module'];
323
            }
324
            $this->routing = $this->inspectDir($modules, $module, $this->routing);
325
        }
326 1
        $this->cache->storeData(CONFIG_DIR . DIRECTORY_SEPARATOR . "domains.json", $this->domains, Cache::JSON, TRUE);
327 1
        $home = Config::getInstance()->get('home_action');
328 1
        if (NULL !== $home || $home !== '') {
329 1
            $home_params = NULL;
330 1
            foreach ($this->routing as $pattern => $params) {
331 1
                list($method, $route) = $this->extractHttpRoute($pattern);
332 1
                if (preg_match("/" . preg_quote($route, "/") . "$/i", "/" . $home)) {
333
                    $home_params = $params;
334
                }
335 1
            }
336 1
            if (NULL !== $home_params) {
337
                $this->routing['/'] = $home_params;
338
            }
339 1
        }
340 1
    }
341
342
    /**
343
     * Método que inspecciona los directorios en busca de clases que registren rutas
344
     *
345
     * @param string $origen
346
     * @param string $namespace
347
     * @param array $routing
348
     *
349
     * @return array
350
     * @throws ConfigException
351
     */
352 1
    private function inspectDir($origen, $namespace = 'PSFS', $routing)
353
    {
354 1
        $files = $this->finder->files()->in($origen)->path('/(controller|api)/i')->name("*.php");
355 1
        foreach ($files as $file) {
356 1
            $filename = str_replace("/", '\\', str_replace($origen, '', $file->getPathname()));
357 1
            $routing = $this->addRouting($namespace . str_replace('.php', '', $filename), $routing);
358 1
        }
359 1
        $this->finder = new Finder();
360
361 1
        return $routing;
362
    }
363
364
    /**
365
     * Checks that a namespace exists
366
     * @param string $namespace
367
     * @return bool
368
     */
369 1
    public static function exists($namespace) {
370 1
        return (class_exists($namespace) || interface_exists($namespace) || trait_exists($namespace));
371
    }
372
373
    /**
374
     * Método que añade nuevas rutas al array de referencia
375
     *
376
     * @param string $namespace
377
     * @param array $routing
378
     *
379
     * @return array
380
     * @throws ConfigException
381
     */
382 1
    private function addRouting($namespace, &$routing)
383
    {
384 1
        if (self::exists($namespace)) {
385 1
            $reflection = new \ReflectionClass($namespace);
386 1
            if (FALSE === $reflection->isAbstract() && FALSE === $reflection->isInterface()) {
387 1
                $this->extractDomain($reflection);
388 1
                $classComments = $reflection->getDocComment();
389 1
                preg_match('/@api\ (.*)\n/im', $classComments, $apiPath);
390 1
                $api = '';
391 1
                if (count($apiPath)) {
392
                    $api = array_key_exists(1, $apiPath) ? $apiPath[1] : $api;
393
                }
394 1
                foreach ($reflection->getMethods() as $method) {
395 1
                    if ($method->isPublic()) {
396 1
                        $docComments = $method->getDocComment();
397 1
                        preg_match('/@route\ (.*)\n/i', $docComments, $sr);
398 1
                        if (count($sr)) {
399 1
                            list($regex, $default, $params) = $this->extractReflectionParams($sr, $method);
400 1
                            if (strlen($api)) {
401
                                $regex = str_replace('{__API__}', $api, $regex);
402
                                $default = str_replace('{__API__}', $api, $default);
403
                            }
404 1
                            $httpMethod = $this->extractReflectionHttpMethod($docComments);
405 1
                            $visible = $this->extractReflectionVisibility($docComments);
406 1
                            $expiration = $this->extractReflectionCacheability($docComments);
407 1
                            $routing[$httpMethod . "#|#" . $regex] = array(
408 1
                                "class" => $namespace,
409 1
                                "method" => $method->getName(),
410 1
                                "params" => $params,
411 1
                                "default" => $default,
412 1
                                "visible" => $visible,
413 1
                                "http" => $httpMethod,
414 1
                                "cache" => $expiration,
415
                            );
416 1
                        }
417 1
                    }
418 1
                }
419 1
            }
420 1
        }
421
422 1
        return $routing;
423
    }
424
425
    /**
426
     * Método que extrae de la ReflectionClass los datos necesarios para componer los dominios en los templates
427
     *
428
     * @param \ReflectionClass $class
429
     *
430
     * @return Router
431
     * @throws ConfigException
432
     */
433 1
    protected function extractDomain(\ReflectionClass $class)
434
    {
435
        //Calculamos los dominios para las plantillas
436 1
        if ($class->hasConstant("DOMAIN")) {
437 1
            $domain = "@" . $class->getConstant("DOMAIN") . "/";
438 1
            $path = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
439 1
            $path = realpath($path) . DIRECTORY_SEPARATOR;
440 1
            $tpl_path = "templates";
441 1
            $public_path = "public";
442 1
            $model_path = "models";
443 1
            if (!preg_match("/ROOT/", $domain)) {
444
                $tpl_path = ucfirst($tpl_path);
445
                $public_path = ucfirst($public_path);
446
                $model_path = ucfirst($model_path);
447
            }
448 1
            if ($class->hasConstant("TPL")) {
449
                $tpl_path .= DIRECTORY_SEPARATOR . $class->getConstant("TPL");
450
            }
451 1
            $this->domains[$domain] = array(
452 1
                "template" => $path . $tpl_path,
453 1
                "model" => $path . $model_path,
454 1
                "public" => $path . $public_path,
455
            );
456 1
        }
457
458 1
        return $this;
459
    }
460
461
    /**
462
     * Método que genera las urls amigables para usar dentro del framework
463
     * @return Router
464
     */
465 1
    public function simpatize()
466
    {
467 1
        $translationFileName = "translations" . DIRECTORY_SEPARATOR . "routes_translations.php";
468 1
        $absoluteTranslationFileName = CACHE_DIR . DIRECTORY_SEPARATOR . $translationFileName;
469 1
        $this->generateSlugs($absoluteTranslationFileName);
470 1
        Config::createDir(CONFIG_DIR);
471 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...
472
473 1
        return $this;
474
    }
475
476
    /**
477
     * Método que devuelve el slug de un string dado
478
     *
479
     * @param string $text
480
     *
481
     * @return string
482
     */
483 1
    private function slugify($text)
484
    {
485
        // replace non letter or digits by -
486 1
        $text = preg_replace('~[^\\pL\d]+~u', '-', $text);
487
488
        // trim
489 1
        $text = trim($text, '-');
490
491
        // transliterate
492 1
        if (function_exists('iconv')) {
493 1
            $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
494 1
        }
495
496
        // lowercase
497 1
        $text = strtolower($text);
498
499
        // remove unwanted characters
500 1
        $text = preg_replace('~[^-\w]+~', '', $text);
501
502 1
        if (empty($text)) {
503
            return 'n-a';
504
        }
505
506 1
        return $text;
507
    }
508
509
    /**
510
     * Método que devuelve una ruta del framework
511
     *
512
     * @param string $slug
513
     * @param boolean $absolute
514
     * @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...
515
     *
516
     * @return string|null
517
     * @throws RouterException
518
     */
519
    public function getRoute($slug = '', $absolute = FALSE, $params = NULL)
520
    {
521
        if (strlen($slug) === 0) {
522
            return ($absolute) ? Request::getInstance()->getRootUrl() . '/' : '/';
523
        }
524
        if (NULL === $slug || !array_key_exists($slug, $this->slugs)) {
525
            throw new RouterException(_("No existe la ruta especificada"));
526
        }
527
        $url = ($absolute) ? Request::getInstance()->getRootUrl() . $this->slugs[$slug] : $this->slugs[$slug];
528
        if (!empty($params)) foreach ($params as $key => $value) {
529
            $url = str_replace("{" . $key . "}", $value, $url);
530
        } elseif (!empty($this->routing[$this->slugs[$slug]]["default"])) {
531
            $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...
532
        }
533
534
        return preg_replace('/(GET|POST|PUT|DELETE|ALL)\#\|\#/', '', $url);
535
    }
536
537
    /**
538
     * Método que devuelve las rutas de administración
539
     * @return array
540
     */
541 1
    public function getAdminRoutes()
542
    {
543 1
        $routes = array();
544 1
        foreach ($this->routing as $route => $params) {
545 1
            list($httpMethod, $routePattern) = $this->extractHttpRoute($route);
546 1
            if (preg_match('/^\/admin(\/|$)/', $routePattern)) {
547 1
                if (preg_match('/^\\\?PSFS/', $params["class"])) {
548 1
                    $profile = "superadmin";
549 1
                } else {
550
                    $profile = "admin";
551
                }
552 1
                if (!empty($params["default"]) && preg_match('/(GET|ALL)/i', $httpMethod)) {
553 1
                    $_profile = ($params["visible"]) ? $profile : 'adminhidden';
554 1
                    if (!array_key_exists($_profile, $routes)) {
555 1
                        $routes[$_profile] = array();
556 1
                    }
557 1
                    $routes[$_profile][] = $params["slug"];
558 1
                }
559 1
            }
560 1
        }
561 1
        if (array_key_exists("superadmin", $routes)) {
562 1
            asort($routes["superadmin"]);
563 1
        }
564 1
        if (array_key_exists("adminhidden", $routes)) {
565 1
            asort($routes["adminhidden"]);
566 1
        }
567 1
        if (array_key_exists('admin', $routes)) {
568
            asort($routes["admin"]);
569
        }
570 1
        return $routes;
571
    }
572
573
    /**
574
     * Método que devuelve le controlador del admin
575
     * @return Admin
576
     */
577
    public function getAdmin()
578
    {
579
        return Admin::getInstance();
580
    }
581
582
    /**
583
     * Método que extrae los dominios
584
     * @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...
585
     */
586
    public function getDomains()
587
    {
588
        return $this->domains;
589
    }
590
591
    /**
592
     * Método que extrae el controller a invocar
593
     *
594
     * @param string $action
595
     *
596
     * @return Object
597
     */
598
    protected function getClassToCall($action)
599
    {
600
        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...
601
        $actionClass = class_exists($action["class"]) ? $action["class"] : "\\" . $action["class"];
602
        $class = (method_exists($actionClass, "getInstance")) ? $actionClass::getInstance() : new $actionClass;
603
        return $class;
604
    }
605
606
    /**
607
     * Método que compara la ruta web con la guardada en la cache
608
     *
609
     * @param $routePattern
610
     * @param $path
611
     *
612
     * @return bool
613
     */
614
    protected function matchRoutePattern($routePattern, $path)
615
    {
616
        $expr = preg_replace('/\{([^}]+)\}/', '###', $routePattern);
617
        $expr = preg_quote($expr, '/');
618
        $expr = str_replace('###', '(.*)', $expr);
619
        $expr2 = preg_replace('/\(\.\*\)$/', '', $expr);
620
        $matched = preg_match('/^' . $expr . '\/?$/i', $path) || preg_match('/^' . $expr2 . '?$/i', $path);
621
        return $matched;
622
    }
623
624
    /**
625
     * @param $pattern
626
     *
627
     * @return array
628
     */
629 2
    protected function extractHttpRoute($pattern)
630
    {
631 2
        $httpMethod = "ALL";
632 2
        $routePattern = $pattern;
633 2
        if (FALSE !== strstr($pattern, "#|#")) {
634 2
            list($httpMethod, $routePattern) = explode("#|#", $pattern, 2);
635 2
        }
636
637 2
        return array(strtoupper($httpMethod), $routePattern);
638
    }
639
640
    /**
641
     * Método que extrae los parámetros de una función
642
     *
643
     * @param array $sr
644
     * @param \ReflectionMethod $method
645
     *
646
     * @return array
647
     */
648 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...
649
    {
650 1
        $regex = $sr[1] ?: $sr[0];
651 1
        $default = '';
652 1
        $params = array();
653 1
        $parameters = $method->getParameters();
654 1
        if (count($parameters) > 0) foreach ($parameters as $param) {
655 1
            if ($param->isOptional() && !is_array($param->getDefaultValue())) {
656 1
                $params[$param->getName()] = $param->getDefaultValue();
657 1
                $default = str_replace('{' . $param->getName() . '}', $param->getDefaultValue(), $regex);
658 1
            }
659 1
        } else $default = $regex;
660
661 1
        return array($regex, $default, $params);
662
    }
663
664
    /**
665
     * Método que extrae el método http
666
     *
667
     * @param string $docComments
668
     *
669
     * @return string
670
     */
671 1
    private function extractReflectionHttpMethod($docComments)
672
    {
673 1
        preg_match('/@(GET|POST|PUT|DELETE)\n/i', $docComments, $routeMethod);
674
675 1
        return (count($routeMethod) > 0) ? $routeMethod[1] : "ALL";
676
    }
677
678
    /**
679
     * Método que extrae la visibilidad de una ruta
680
     *
681
     * @param string $docComments
682
     *
683
     * @return bool
684
     */
685 1
    private function extractReflectionVisibility($docComments)
686
    {
687 1
        preg_match('/@visible\ (.*)\n/i', $docComments, $visible);
688 1
        return !(array_key_exists(1, $visible) && preg_match('/false/i', $visible[1]));
689
    }
690
691
    /**
692
     * Método que extrae el parámetro de caché
693
     *
694
     * @param string $docComments
695
     *
696
     * @return bool
697
     */
698 1
    private function extractReflectionCacheability($docComments)
699
    {
700 1
        preg_match('/@cache\ (.*)\n/i', $docComments, $cache);
701
702 1
        return (count($cache) > 0) ? $cache[1] : "0";
703
    }
704
705
    /**
706
     * Método que ejecuta una acción del framework y revisa si lo tenemos cacheado ya o no
707
     *
708
     * @param string $route
709
     * @param array $action
710
     * @param types\Controller $class
711
     * @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...
712
     */
713
    protected function executeCachedRoute($route, $action, $class, $params = NULL)
714
    {
715
        Logger::log('Executing route ' . $route);
716
        Security::getInstance()->setSessionKey("__CACHE__", $action);
717
        $cache = Cache::needCache();
718
        $execute = TRUE;
719
        if (FALSE !== $cache && Config::getInstance()->getDebugMode() === FALSE) {
720
            $cacheDataName = $this->cache->getRequestCacheHash();
721
            $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 717 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...
722
            });
723
            if (NULL !== $cachedData) {
724
                $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 717 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...
725
                }, Cache::JSON);
726
                Template::getInstance()->renderCache($cachedData, $headers);
727
                $execute = FALSE;
728
            }
729
        }
730
        if ($execute) {
731
            call_user_func_array(array($class, $action['method']), $params);
732
        }
733
    }
734
735
    /**
736
     * Parse slugs to create translations
737
     *
738
     * @param string $absoluteTranslationFileName
739
     */
740 1
    private function generateSlugs($absoluteTranslationFileName)
741
    {
742 1
        $translations = $this->generateTranslationsFile($absoluteTranslationFileName);
743 1
        foreach ($this->routing as $key => &$info) {
744 1
            $keyParts = $key;
745 1
            if (FALSE === strstr("#|#", $key)) {
746 1
                $keyParts = explode("#|#", $key);
747 1
                $keyParts = array_key_exists(1, $keyParts) ? $keyParts[1] : '';
748 1
            }
749 1
            $slug = $this->slugify($keyParts);
750 1
            if (NULL !== $slug && !array_key_exists($slug, $translations)) {
751 1
                $translations[$slug] = $key;
752 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...
753 1
            }
754 1
            $this->slugs[$slug] = $key;
755 1
            $info["slug"] = $slug;
756 1
        }
757 1
    }
758
759
    /**
760
     * Create translation file if not exists
761
     *
762
     * @param string $absoluteTranslationFileName
763
     *
764
     * @return array
765
     */
766 1
    private function generateTranslationsFile($absoluteTranslationFileName)
767
    {
768 1
        $translations = array();
769 1
        if (file_exists($absoluteTranslationFileName)) {
770
            include($absoluteTranslationFileName);
771
        } else {
772 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...
773
        }
774
775 1
        return $translations;
776
    }
777
778
}
779